diff --git a/b2g/app/B2GLoader.cpp b/b2g/app/B2GLoader.cpp index cf2eb08dff..40bac4b684 100644 --- a/b2g/app/B2GLoader.cpp +++ b/b2g/app/B2GLoader.cpp @@ -251,7 +251,9 @@ ReserveFileDescriptors(FdArray& aReservedFds) MOZ_CRASH("ProcLoader error: failed to reserve a magic file descriptor."); } - aReservedFds.append(target); + if (!aReservedFds.append(target)) { + MOZ_CRASH("Failed to append to aReservedFds"); + } if (fd == target) { // No need to call dup2(). We already occupy the desired file descriptor. diff --git a/b2g/installer/package-manifest.in b/b2g/installer/package-manifest.in index 2225c801f4..b577b78caf 100644 --- a/b2g/installer/package-manifest.in +++ b/b2g/installer/package-manifest.in @@ -649,10 +649,6 @@ #endif @RESPATH@/components/servicesComponents.manifest @RESPATH@/components/cryptoComponents.manifest -#ifdef MOZ_SERVICES_HEALTHREPORT -@RESPATH@/components/HealthReportComponents.manifest -@RESPATH@/components/HealthReportService.js -#endif @RESPATH@/components/CaptivePortalDetectComponents.manifest @RESPATH@/components/captivedetect.js @RESPATH@/components/TelemetryStartup.js diff --git a/browser/installer/package-manifest.in b/browser/installer/package-manifest.in index e44e79b13b..8486e07985 100644 --- a/browser/installer/package-manifest.in +++ b/browser/installer/package-manifest.in @@ -526,12 +526,7 @@ @RESPATH@/components/nsINIProcessor.js @RESPATH@/components/nsPrompter.manifest @RESPATH@/components/nsPrompter.js -#ifdef MOZ_DATA_REPORTING -@RESPATH@/components/DataReporting.manifest -@RESPATH@/components/DataReportingService.js -#endif #ifdef MOZ_SERVICES_HEALTHREPORT -@RESPATH@/components/HealthReportComponents.manifest @RESPATH@/browser/components/SelfSupportService.manifest @RESPATH@/browser/components/SelfSupportService.js #endif diff --git a/configure.in b/configure.in index 78d0d07005..676a7e87ba 100644 --- a/configure.in +++ b/configure.in @@ -7769,6 +7769,15 @@ MOZ_ARG_ENABLE_BOOL(skia, MOZ_ENABLE_SKIA=, MOZ_ENABLE_SKIA=) +dnl Skia GPU support may not reliably build on certain *BSDs (see bug 1234494). +if test "${OS_TARGET}" = "OpenBSD" -o \ + "${OS_TARGET}" = "NetBSD" -o \ + "${OS_ARCH}" = "SunOS"; then + MOZ_DISABLE_SKIA_GPU=1 +else + MOZ_DISABLE_SKIA_GPU= +fi + MOZ_ARG_DISABLE_BOOL(skia-gpu, [ --disable-skia-gpu Disable use of Skia-GPU], MOZ_DISABLE_SKIA_GPU=1, diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp index d6b0c773ff..2132c0f161 100644 --- a/docshell/base/nsDocShell.cpp +++ b/docshell/base/nsDocShell.cpp @@ -13794,6 +13794,13 @@ nsDocShell::SetIsSignedPackage(const nsAString& aSignedPkg) return NS_OK; } +NS_IMETHODIMP +nsDocShell::SetUserContextId(uint32_t aUserContextId) +{ + mUserContextId = aUserContextId; + return NS_OK; +} + /* [infallible] */ NS_IMETHODIMP nsDocShell::GetIsBrowserElement(bool* aIsBrowser) { @@ -13897,6 +13904,8 @@ nsDocShell::GetOriginAttributes() attrs.mAppId = mOwnOrContainingAppId; } + attrs.mUserContextId = mUserContextId; + if (mFrameType == eFrameTypeBrowser) { attrs.mInBrowser = true; } diff --git a/docshell/base/nsDocShell.h b/docshell/base/nsDocShell.h index 9ec0a879d6..3169436a6a 100644 --- a/docshell/base/nsDocShell.h +++ b/docshell/base/nsDocShell.h @@ -235,6 +235,7 @@ public: NS_IMETHOD GetUseRemoteTabs(bool*) override; NS_IMETHOD SetRemoteTabs(bool) override; NS_IMETHOD GetOriginAttributes(JS::MutableHandle) override; + NS_IMETHOD SetUserContextId(uint32_t); // Restores a cached presentation from history (mLSHE). // This method swaps out the content viewer and simulates loads for @@ -1011,6 +1012,9 @@ protected: // find it by walking up the docshell hierarchy.) uint32_t mOwnOrContainingAppId; + // userContextId signifying which container we are in + uint32_t mUserContextId; + nsString mPaymentRequestId; nsString GetInheritedPaymentRequestId(); diff --git a/docshell/test/browser/head.js b/docshell/test/browser/head.js index 29df446d6c..24ca8f45b3 100644 --- a/docshell/test/browser/head.js +++ b/docshell/test/browser/head.js @@ -31,8 +31,8 @@ function makeTimelineTest(frameScriptName, url) { info(message.data.message); }); mm.addMessageListener("browser:test:finish", function(ignore) { - finish(); gBrowser.removeCurrentTab(); + finish(); }); }); } @@ -60,3 +60,40 @@ function timelineTestOpenUrl(url) { return Promise.all([tabSwitchPromise, loadPromise]).then(([_, tab]) => tab); } + +/** + * Helper function for charset tests. It loads |url| in a new tab, + * runs |check1| in a ContentTask when the page is ready, switches the + * charset to |charset|, and then runs |check2| in a ContentTask when + * the page has finished reloading. + * + * |charset| and |check2| can be omitted, in which case the test + * finishes when |check1| completes. + */ +function runCharsetTest(url, check1, charset, check2) { + waitForExplicitFinish(); + + BrowserTestUtils.openNewForegroundTab(gBrowser, url, true).then(afterOpen); + + function afterOpen() { + if (charset) { + BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser).then(afterChangeCharset); + + ContentTask.spawn(gBrowser.selectedBrowser, null, check1).then(() => { + BrowserSetForcedCharacterSet(charset); + }); + } else { + ContentTask.spawn(gBrowser.selectedBrowser, null, check1).then(() => { + gBrowser.removeCurrentTab(); + finish(); + }); + } + } + + function afterChangeCharset() { + ContentTask.spawn(gBrowser.selectedBrowser, null, check2).then(() => { + gBrowser.removeCurrentTab(); + finish(); + }); + } +} diff --git a/dom/base/nsFrameLoader.cpp b/dom/base/nsFrameLoader.cpp index 4fcda1c8aa..12c22d54dd 100644 --- a/dom/base/nsFrameLoader.cpp +++ b/dom/base/nsFrameLoader.cpp @@ -234,7 +234,7 @@ nsFrameLoader::LoadFrame() if (NS_SUCCEEDED(rv)) { rv = LoadURI(uri); } - + if (NS_FAILED(rv)) { FireErrorEvent(); @@ -325,7 +325,7 @@ nsFrameLoader::ReallyStartLoading() if (NS_FAILED(rv)) { FireErrorEvent(); } - + return rv; } @@ -345,7 +345,7 @@ nsFrameLoader::ReallyStartLoadingInternal() // FIXME get error codes from child mRemoteBrowser->LoadURL(mURIToLoad); - + if (!mRemoteBrowserShown && !ShowRemoteFrame(ScreenIntSize(0, 0))) { NS_WARNING("[nsFrameLoader] ReallyStartLoadingInternal tried but couldn't show remote browser.\n"); } @@ -378,7 +378,7 @@ nsFrameLoader::ReallyStartLoadingInternal() loadInfo->SetOwner(mOwnerContent->NodePrincipal()); nsCOMPtr referrer; - + nsAutoString srcdoc; bool isSrcdoc = mOwnerContent->IsHTMLElement(nsGkAtoms::iframe) && mOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::srcdoc, @@ -552,7 +552,7 @@ nsFrameLoader::AddTreeItemToTreeOwner(nsIDocShellTreeItem* aItem, { NS_PRECONDITION(aItem, "Must have docshell treeitem"); NS_PRECONDITION(mOwnerContent, "Must have owning content"); - + nsAutoString value; bool isContent = false; mOwnerContent->GetAttr(kNameSpaceID_None, TypeAttrName(), value); @@ -1133,7 +1133,7 @@ nsFrameLoader::SwapWithOtherLoader(nsFrameLoader* aOther, !AllDescendantsOfType(otherDocshell, otherType))) { return NS_ERROR_NOT_IMPLEMENTED; } - + // Save off the tree owners, frame elements, chrome event handlers, and // docshell and document parents before doing anything else. nsCOMPtr ourOwner, otherOwner; @@ -1243,7 +1243,7 @@ nsFrameLoader::SwapWithOtherLoader(nsFrameLoader* aOther, ourOwner->ContentShellRemoved(ourDocshell); otherOwner->ContentShellRemoved(otherDocshell); } - + ourParentItem->AddChild(otherDocshell); otherParentItem->AddChild(ourDocshell); @@ -1794,6 +1794,23 @@ nsFrameLoader::MaybeCreateDocShell() mDocShell->SetName(frameName); } + //Grab the userContextId from owner if XUL + nsAutoString userContextIdStr; + if (namespaceID == kNameSpaceID_XUL) { + if (mOwnerContent->HasAttr(kNameSpaceID_None, nsGkAtoms::usercontextid)) { + mOwnerContent->GetAttr(kNameSpaceID_None, + nsGkAtoms::usercontextid, + userContextIdStr); + } + } + + if (!userContextIdStr.IsEmpty()) { + nsresult err; + nsDocShell * ds = nsDocShell::Cast(mDocShell); + ds->SetUserContextId(userContextIdStr.ToInteger(&err)); + NS_ENSURE_SUCCESS(err, err); + } + // Inform our docShell that it has a new child. // Note: This logic duplicates a lot of logic in // nsSubDocumentFrame::AttributeChanged. We should fix that. @@ -1967,7 +1984,7 @@ nsFrameLoader::CheckForRecursiveLoad(nsIURI* aURI) NS_WARN_IF_FALSE(treeOwner, "Trying to load a new url to a docshell without owner!"); NS_ENSURE_STATE(treeOwner); - + if (mDocShell->ItemType() != nsIDocShellTreeItem::typeContent) { // No need to do recursion-protection here XXXbz why not?? Do we really // trust people not to screw up with non-content docshells? @@ -1981,7 +1998,7 @@ nsFrameLoader::CheckForRecursiveLoad(nsIURI* aURI) int32_t depth = 0; while (parentAsItem) { ++depth; - + if (depth >= MAX_DEPTH_CONTENT_FRAMES) { mDepthTooGreat = true; NS_WARNING("Too many nested content frames so giving up"); @@ -2022,7 +2039,7 @@ nsFrameLoader::CheckForRecursiveLoad(nsIURI* aURI) bool equal; rv = aURI->EqualsExceptRef(parentURI, &equal); NS_ENSURE_SUCCESS(rv, rv); - + if (equal) { matchCount++; if (matchCount >= MAX_SAME_URL_CONTENT_FRAMES) { @@ -3039,6 +3056,23 @@ nsFrameLoader::GetNewTabContext(MutableTabContext* aTabContext, // Populate packageId to signedPkg. attrs.mSignedPkg = NS_ConvertUTF8toUTF16(aPackageId); + // set the userContextId on the attrs before we pass them into + // the tab context + if (mOwnerContent) { + nsAutoString userContextIdStr; + if (mOwnerContent->HasAttr(kNameSpaceID_None, nsGkAtoms::usercontextid)) { + mOwnerContent->GetAttr(kNameSpaceID_None, + nsGkAtoms::usercontextid, + userContextIdStr); + } + if (!userContextIdStr.IsEmpty()) { + nsresult err; + uint32_t userContextId = userContextIdStr.ToInteger(&err); + NS_ENSURE_SUCCESS(err, err); + attrs.mUserContextId = userContextId; + } + } + bool tabContextUpdated = aTabContext->SetTabContext(ownApp, containingApp, attrs, aSignedPkgOriginNoSuffix); NS_ENSURE_STATE(tabContextUpdated); diff --git a/dom/base/nsGkAtomList.h b/dom/base/nsGkAtomList.h index 664dca4d9b..71f31c4c31 100644 --- a/dom/base/nsGkAtomList.h +++ b/dom/base/nsGkAtomList.h @@ -2417,6 +2417,9 @@ GK_ATOM(onboundary, "onboundary") GK_ATOM(vr_state, "vr-state") +// Contextual Identity / Containers +GK_ATOM(usercontextid, "usercontextid") + // MSE GK_ATOM(onsourceopen, "onsourceopen") GK_ATOM(onsourceended, "onsourceended") diff --git a/dom/base/nsScriptLoader.cpp b/dom/base/nsScriptLoader.cpp index 0f115d7e51..37460d5df7 100644 --- a/dom/base/nsScriptLoader.cpp +++ b/dom/base/nsScriptLoader.cpp @@ -14,7 +14,6 @@ #include "jsapi.h" #include "jsfriendapi.h" #include "xpcpublic.h" -#include "nsIUnicodeDecoder.h" #include "nsIContent.h" #include "nsJSUtils.h" #include "mozilla/dom/ScriptSettings.h" @@ -54,7 +53,6 @@ #include "mozilla/Attributes.h" #include "mozilla/unused.h" -#include "mozilla/dom/SRICheck.h" #include "nsIScriptError.h" using namespace mozilla; @@ -352,7 +350,13 @@ nsScriptLoader::StartLoad(nsScriptLoadRequest *aRequest, const nsAString &aType, timedChannel->SetInitiatorType(NS_LITERAL_STRING("script")); } - RefPtr handler = new nsScriptLoadHandler(this, aRequest); + nsAutoPtr sriDataVerifier; + if (!aRequest->mIntegrity.IsEmpty()) { + sriDataVerifier = new SRICheckDataVerifier(aRequest->mIntegrity, mDocument); + } + + RefPtr handler = + new nsScriptLoadHandler(this, aRequest, sriDataVerifier.forget()); nsCOMPtr loader; rv = NS_NewIncrementalStreamLoader(getter_AddRefs(loader), handler); @@ -1425,20 +1429,33 @@ nsScriptLoader::ConvertToUTF16(nsIChannel* aChannel, const uint8_t* aData, nsresult nsScriptLoader::OnStreamComplete(nsIIncrementalStreamLoader* aLoader, nsISupports* aContext, - nsresult aStatus, - uint32_t aStringLen, - const uint8_t* aString) + nsresult aChannelStatus, + nsresult aSRIStatus, + mozilla::Vector &aString, + mozilla::dom::SRICheckDataVerifier* aSRIDataVerifier) { nsScriptLoadRequest* request = static_cast(aContext); NS_ASSERTION(request, "null request in stream complete handler"); NS_ENSURE_TRUE(request, NS_ERROR_FAILURE); - nsresult rv = NS_ERROR_SRI_CORRUPT; - if (request->mIntegrity.IsEmpty() || - NS_SUCCEEDED(SRICheck::VerifyIntegrity(request->mIntegrity, aLoader, - request->mCORSMode, aStringLen, - aString, mDocument))) { - rv = PrepareLoadedRequest(request, aLoader, aStatus, aStringLen, aString); + nsresult rv = NS_OK; + if (!request->mIntegrity.IsEmpty() && + NS_SUCCEEDED((rv = aSRIStatus))) { + MOZ_ASSERT(aSRIDataVerifier); + + nsCOMPtr channelRequest; + aLoader->GetRequest(getter_AddRefs(channelRequest)); + nsCOMPtr channel; + channel = do_QueryInterface(channelRequest); + + if (NS_FAILED(aSRIDataVerifier->Verify(request->mIntegrity, channel, + request->mCORSMode, mDocument))) { + rv = NS_ERROR_SRI_CORRUPT; + } + } + + if (NS_SUCCEEDED(rv)) { + rv = PrepareLoadedRequest(request, aLoader, aChannelStatus, aString); } if (NS_FAILED(rv)) { @@ -1481,16 +1498,12 @@ nsScriptLoader::OnStreamComplete(nsIIncrementalStreamLoader* aLoader, } else { mPreloads.RemoveElement(request, PreloadRequestComparator()); } - rv = NS_OK; - } else { - moz_free(const_cast(aString)); - rv = NS_SUCCESS_ADOPTED_DATA; } // Process our request and/or any pending ones ProcessPendingRequests(); - return rv; + return NS_OK; } void @@ -1521,8 +1534,7 @@ nsresult nsScriptLoader::PrepareLoadedRequest(nsScriptLoadRequest* aRequest, nsIIncrementalStreamLoader* aLoader, nsresult aStatus, - uint32_t aStringLen, - const uint8_t* aString) + mozilla::Vector &aString) { if (NS_FAILED(aStatus)) { return aStatus; @@ -1573,21 +1585,9 @@ nsScriptLoader::PrepareLoadedRequest(nsScriptLoadRequest* aRequest, NS_ENSURE_SUCCESS(rv, rv); } - if (aStringLen) { - // Check the charset attribute to determine script charset. - nsAutoString hintCharset; - if (!aRequest->IsPreload()) { - aRequest->mElement->GetScriptCharset(hintCharset); - } else { - nsTArray::index_type i = - mPreloads.IndexOf(aRequest, 0, PreloadRequestComparator()); - NS_ASSERTION(i != mPreloads.NoIndex, "Incorrect preload bookkeeping"); - hintCharset = mPreloads[i].mCharset; - } - rv = ConvertToUTF16(channel, aString, aStringLen, hintCharset, mDocument, - aRequest->mScriptTextBuf, aRequest->mScriptTextLength); - - NS_ENSURE_SUCCESS(rv, rv); + if (!aString.empty()) { + aRequest->mScriptTextLength = aString.length(); + aRequest->mScriptTextBuf = aString.extractRawBuffer(); } // This assertion could fire errorously if we ran out of memory when @@ -1732,9 +1732,14 @@ nsScriptLoader::MaybeRemovedDeferRequests() ////////////////////////////////////////////////////////////// nsScriptLoadHandler::nsScriptLoadHandler(nsScriptLoader *aScriptLoader, - nsScriptLoadRequest *aRequest) + nsScriptLoadRequest *aRequest, + mozilla::dom::SRICheckDataVerifier *aSRIDataVerifier) : mScriptLoader(aScriptLoader), - mRequest(aRequest) + mRequest(aRequest), + mSRIDataVerifier(aSRIDataVerifier), + mSRIStatus(NS_OK), + mDecoder(), + mBuffer() {} nsScriptLoadHandler::~nsScriptLoadHandler() @@ -1749,18 +1754,164 @@ nsScriptLoadHandler::OnIncrementalData(nsIIncrementalStreamLoader* aLoader, const uint8_t* aData, uint32_t *aConsumedLength) { + if (mRequest->IsCanceled()) { + // If request cancelled, ignore any incoming data. + *aConsumedLength = aDataLength; + return NS_OK; + } + + if (!EnsureDecoder(aLoader, aData, aDataLength, + /* aEndOfStream = */ false)) { + return NS_OK; + } + + // Below we will/shall consume entire data chunk. + *aConsumedLength = aDataLength; + + // Decoder has already been initialized. -- trying to decode all loaded bytes. + nsresult rv = TryDecodeRawData(aData, aDataLength, + /* aEndOfStream = */ false); + NS_ENSURE_SUCCESS(rv, rv); + + // If SRI is required for this load, appending new bytes to the hash. + if (mSRIDataVerifier && NS_SUCCEEDED(mSRIStatus)) { + mSRIStatus = mSRIDataVerifier->Update(aDataLength, aData); + } + + return rv; +} + +nsresult +nsScriptLoadHandler::TryDecodeRawData(const uint8_t* aData, + uint32_t aDataLength, + bool aEndOfStream) +{ + int32_t srcLen = aDataLength; + const char* src = reinterpret_cast(aData); + int32_t dstLen; + nsresult rv = + mDecoder->GetMaxLength(src, srcLen, &dstLen); + + NS_ENSURE_SUCCESS(rv, rv); + + uint32_t haveRead = mBuffer.length(); + uint32_t capacity = haveRead + dstLen; + if (!mBuffer.reserve(capacity)) { + return NS_ERROR_OUT_OF_MEMORY; + } + + rv = mDecoder->Convert(src, + &srcLen, + mBuffer.begin() + haveRead, + &dstLen); + + NS_ENSURE_SUCCESS(rv, rv); + + haveRead += dstLen; + MOZ_ASSERT(haveRead <= capacity, "mDecoder produced more data than expected"); + MOZ_ALWAYS_TRUE(mBuffer.resizeUninitialized(haveRead)); + return NS_OK; } +bool +nsScriptLoadHandler::EnsureDecoder(nsIIncrementalStreamLoader *aLoader, + const uint8_t* aData, + uint32_t aDataLength, + bool aEndOfStream) +{ + // Check if decoder has already been created. + if (mDecoder) { + return true; + } + + nsAutoCString charset; + + // Determine if BOM check should be done. This occurs either + // if end-of-stream has been reached, or at least 3 bytes have + // been read from input. + if (!aEndOfStream && (aDataLength < 3)) { + return false; + } + + // Do BOM detection. + if (DetectByteOrderMark(aData, aDataLength, charset)) { + mDecoder = EncodingUtils::DecoderForEncoding(charset); + return true; + } + + // BOM detection failed, check content stream for charset. + nsCOMPtr req; + nsresult rv = aLoader->GetRequest(getter_AddRefs(req)); + NS_ASSERTION(req, "StreamLoader's request went away prematurely"); + NS_ENSURE_SUCCESS(rv, false); + + nsCOMPtr channel = do_QueryInterface(req); + + if (channel && + NS_SUCCEEDED(channel->GetContentCharset(charset)) && + EncodingUtils::FindEncodingForLabel(charset, charset)) { + mDecoder = EncodingUtils::DecoderForEncoding(charset); + return true; + } + + // Check the hint charset from the script element or preload + // request. + nsAutoString hintCharset; + if (!mRequest->IsPreload()) { + mRequest->mElement->GetScriptCharset(hintCharset); + } else { + nsTArray::index_type i = + mScriptLoader->mPreloads.IndexOf(mRequest, 0, + nsScriptLoader::PreloadRequestComparator()); + + NS_ASSERTION(i != mScriptLoader->mPreloads.NoIndex, + "Incorrect preload bookkeeping"); + hintCharset = mScriptLoader->mPreloads[i].mCharset; + } + + if (EncodingUtils::FindEncodingForLabel(hintCharset, charset)) { + mDecoder = EncodingUtils::DecoderForEncoding(charset); + return true; + } + + // Get the charset from the charset of the document. + if (mScriptLoader->mDocument) { + charset = mScriptLoader->mDocument->GetDocumentCharacterSet(); + mDecoder = EncodingUtils::DecoderForEncoding(charset); + return true; + } + + // Curiously, there are various callers that don't pass aDocument. The + // fallback in the old code was ISO-8859-1, which behaved like + // windows-1252. Saying windows-1252 for clarity and for compliance + // with the Encoding Standard. + charset = "windows-1252"; + mDecoder = EncodingUtils::DecoderForEncoding(charset); + return true; +} + NS_IMETHODIMP nsScriptLoadHandler::OnStreamComplete(nsIIncrementalStreamLoader* aLoader, nsISupports* aContext, nsresult aStatus, - uint32_t aStringLen, - const uint8_t* aString) + uint32_t aDataLength, + const uint8_t* aData) { - // pass arguments through except for the aContext, - // we have to mediate and use mRequest instead. - return mScriptLoader->OnStreamComplete(aLoader, mRequest, aStatus, - aStringLen, aString); + if (!mRequest->IsCanceled()) { + DebugOnly encoderSet = + EnsureDecoder(aLoader, aData, aDataLength, /* aEndOfStream = */ true); + MOZ_ASSERT(encoderSet); + DebugOnly rv = TryDecodeRawData(aData, aDataLength, + /* aEndOfStream = */ true); + + // If SRI is required for this load, appending new bytes to the hash. + if (mSRIDataVerifier && NS_SUCCEEDED(mSRIStatus)) { + mSRIStatus = mSRIDataVerifier->Update(aDataLength, aData); + } + } + + // we have to mediate and use mRequest. + return mScriptLoader->OnStreamComplete(aLoader, mRequest, aStatus, mSRIStatus, + mBuffer, mSRIDataVerifier); } diff --git a/dom/base/nsScriptLoader.h b/dom/base/nsScriptLoader.h index 8261c49cb0..70d730e417 100644 --- a/dom/base/nsScriptLoader.h +++ b/dom/base/nsScriptLoader.h @@ -12,6 +12,7 @@ #define __nsScriptLoader_h__ #include "nsCOMPtr.h" +#include "nsIUnicodeDecoder.h" #include "nsIScriptElement.h" #include "nsCOMArray.h" #include "nsTArray.h" @@ -20,8 +21,10 @@ #include "nsIIncrementalStreamLoader.h" #include "mozilla/CORSMode.h" #include "mozilla/dom/SRIMetadata.h" +#include "mozilla/dom/SRICheck.h" #include "mozilla/LinkedList.h" #include "mozilla/net/ReferrerPolicy.h" +#include "mozilla/Vector.h" class nsScriptLoadRequestList; class nsIURI; @@ -217,6 +220,7 @@ class nsScriptLoader final : public nsISupports }; friend class nsScriptRequestProcessor; + friend class nsScriptLoadHandler; friend class AutoCurrentScriptUpdater; public: @@ -348,9 +352,10 @@ public: */ nsresult OnStreamComplete(nsIIncrementalStreamLoader* aLoader, nsISupports* aContext, - nsresult aStatus, - uint32_t aStringLen, - const uint8_t* aString); + nsresult aChannelStatus, + nsresult aSRIStatus, + mozilla::Vector &aString, + mozilla::dom::SRICheckDataVerifier* aSRIDataVerifier); /** * Processes any pending requests that are ready for processing. @@ -501,8 +506,7 @@ private: nsresult PrepareLoadedRequest(nsScriptLoadRequest* aRequest, nsIIncrementalStreamLoader* aLoader, nsresult aStatus, - uint32_t aStringLen, - const uint8_t* aString); + mozilla::Vector &aString); void AddDeferRequest(nsScriptLoadRequest* aRequest); bool MaybeRemovedDeferRequests(); @@ -552,7 +556,8 @@ class nsScriptLoadHandler final : public nsIIncrementalStreamLoaderObserver { public: explicit nsScriptLoadHandler(nsScriptLoader* aScriptLoader, - nsScriptLoadRequest *aRequest); + nsScriptLoadRequest *aRequest, + mozilla::dom::SRICheckDataVerifier *aSRIDataVerifier); NS_DECL_ISUPPORTS NS_DECL_NSIINCREMENTALSTREAMLOADEROBSERVER @@ -560,8 +565,38 @@ public: private: virtual ~nsScriptLoadHandler(); - RefPtr mScriptLoader; - RefPtr mRequest; + /* + * Try to decode some raw data. + */ + nsresult TryDecodeRawData(const uint8_t* aData, uint32_t aDataLength, + bool aEndOfStream); + + /* + * Discover the charset by looking at the stream data, the script + * tag, and other indicators. Returns true if charset has been + * discovered. + */ + bool EnsureDecoder(nsIIncrementalStreamLoader *aLoader, + const uint8_t* aData, uint32_t aDataLength, + bool aEndOfStream); + + // ScriptLoader which will handle the parsed script. + RefPtr mScriptLoader; + + // The nsScriptLoadRequest for this load. + RefPtr mRequest; + + // SRI data verifier. + nsAutoPtr mSRIDataVerifier; + + // Status of SRI data operations. + nsresult mSRIStatus; + + // Unicode decoder for charset. + nsCOMPtr mDecoder; + + // Accumulated decoded char buffer. + mozilla::Vector mBuffer; }; class nsAutoScriptLoaderDisabler diff --git a/dom/bluetooth/bluez/BluetoothDBusService.cpp b/dom/bluetooth/bluez/BluetoothDBusService.cpp index 5a7f672c6d..c1040b42d2 100644 --- a/dom/bluetooth/bluez/BluetoothDBusService.cpp +++ b/dom/bluetooth/bluez/BluetoothDBusService.cpp @@ -862,12 +862,12 @@ CheckDBusReply(DBusMessage* aMsg, void* aServiceClass, bool aConnect) nsAutoString replyError; UnpackVoidMessage(aMsg, nullptr, v, replyError); - nsAutoPtr serviceClass( - static_cast(aServiceClass)); + BluetoothServiceClass serviceClass = + static_cast(NS_PTR_TO_INT32(aServiceClass)); if (!replyError.IsEmpty()) { NS_DispatchToMainThread( - new ReplyErrorToProfileManager(*serviceClass, aConnect, replyError)); + new ReplyErrorToProfileManager(serviceClass, aConnect, replyError)); } } @@ -2592,7 +2592,7 @@ class SendAsyncDBusMessageTask : public Task { public: SendAsyncDBusMessageTask(DBusReplyCallback aCallback, - BluetoothServiceClass* aServiceClass, + BluetoothServiceClass aServiceClass, const nsACString& aObjectPath, const char* aInterface, const nsACString& aMessage) @@ -2602,7 +2602,6 @@ public: , mInterface(aInterface) , mMessage(aMessage) { - MOZ_ASSERT(mServiceClass); MOZ_ASSERT(!mObjectPath.IsEmpty()); MOZ_ASSERT(!mInterface.IsEmpty()); MOZ_ASSERT(!mMessage.IsEmpty()); @@ -2613,18 +2612,18 @@ public: MOZ_ASSERT(!NS_IsMainThread()); // I/O thread MOZ_ASSERT(sDBusConnection); + static_assert(sizeof(BluetoothServiceClass) <= sizeof(intptr_t), + "BluetoothServiceClass cannot be passed via intptr_t"); bool success = sDBusConnection->SendWithReply( - mCallback, static_cast(mServiceClass), -1, + mCallback, NS_INT32_TO_PTR(mServiceClass), -1, BLUEZ_DBUS_BASE_IFC, mObjectPath.get(), mInterface.get(), mMessage.get(), DBUS_TYPE_INVALID); NS_ENSURE_TRUE_VOID(success); - - mServiceClass.forget(); } private: DBusReplyCallback mCallback; - nsAutoPtr mServiceClass; + BluetoothServiceClass mServiceClass; const nsCString mObjectPath; const nsCString mInterface; const nsCString mMessage; @@ -2642,18 +2641,18 @@ BluetoothDBusService::SendAsyncDBusMessage(const nsAString& aObjectPath, MOZ_ASSERT(!aObjectPath.IsEmpty()); MOZ_ASSERT(aInterface); - nsAutoPtr serviceClass(new BluetoothServiceClass()); + BluetoothServiceClass serviceClass; if (!strcmp(aInterface, DBUS_SINK_IFACE)) { - *serviceClass = BluetoothServiceClass::A2DP; + serviceClass = BluetoothServiceClass::A2DP; } else if (!strcmp(aInterface, DBUS_INPUT_IFACE)) { - *serviceClass = BluetoothServiceClass::HID; + serviceClass = BluetoothServiceClass::HID; } else { MOZ_ASSERT(false); return NS_ERROR_FAILURE; } Task* task = new SendAsyncDBusMessageTask(aCallback, - serviceClass.forget(), + serviceClass, NS_ConvertUTF16toUTF8(aObjectPath), aInterface, NS_ConvertUTF16toUTF8(aMessage)); diff --git a/dom/indexedDB/ActorsParent.cpp b/dom/indexedDB/ActorsParent.cpp index f6176fd6b2..94318f6e9f 100644 --- a/dom/indexedDB/ActorsParent.cpp +++ b/dom/indexedDB/ActorsParent.cpp @@ -3059,8 +3059,6 @@ UpgradeKeyFunction::OnFunctionCall(mozIStorageValueArray* aValues, nsCOMPtr result = new mozilla::storage::AdoptedBlobVariant(data); - upgradedBlobData.release(); - result.forget(_retval); return NS_OK; } @@ -4010,12 +4008,10 @@ UpgradeIndexDataValuesFunction::OnFunctionCall(mozIStorageValueArray* aArguments uint32_t newIdvLength; rv = MakeCompressedIndexDataValues(oldIdv, newIdv, &newIdvLength); - std::pair data(newIdv.get(), newIdvLength); + std::pair data(newIdv.release(), newIdvLength); nsCOMPtr result = new storage::AdoptedBlobVariant(data); - newIdv.release(); - result.forget(aResult); return NS_OK; } @@ -19051,13 +19047,11 @@ DatabaseOperationBase::UpdateIndexValues( if (indexDataValues) { rv = updateStmt->BindAdoptedBlobByName(indexDataValuesString, - indexDataValues.get(), + indexDataValues.release(), indexDataValuesLength); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } - - indexDataValues.release(); } else { rv = updateStmt->BindNullByName(indexDataValuesString); if (NS_WARN_IF(NS_FAILED(rv))) { @@ -23812,13 +23806,11 @@ UpdateIndexDataValuesFunction::OnFunctionCall(mozIStorageValueArray* aValues, return rv; } - std::pair copiedBlobDataPair(indexValuesBlob.get(), + std::pair copiedBlobDataPair(indexValuesBlob.release(), indexValuesBlobLength); value = new storage::AdoptedBlobVariant(copiedBlobDataPair); - indexValuesBlob.release(); - value.forget(_retval); return NS_OK; } @@ -24764,12 +24756,9 @@ ObjectStoreAddOrPutRequestOp::DoDatabaseWork(DatabaseConnection* aConnection) uint8_t* dataBuffer = reinterpret_cast(compressed); size_t dataBufferLength = compressedLength; - // If this call succeeds, | compressed | is now owned by the statement, and - // we are no longer responsible for it. rv = stmt->BindAdoptedBlobByName(NS_LITERAL_CSTRING("data"), dataBuffer, dataBufferLength); if (NS_WARN_IF(NS_FAILED(rv))) { - free(compressed); return rv; } } diff --git a/dom/security/SRICheck.cpp b/dom/security/SRICheck.cpp index 471669db0f..a135502394 100644 --- a/dom/security/SRICheck.cpp +++ b/dom/security/SRICheck.cpp @@ -11,7 +11,6 @@ #include "mozilla/Preferences.h" #include "nsContentUtils.h" #include "nsIChannel.h" -#include "nsICryptoHash.h" #include "nsIDocument.h" #include "nsIProtocolHandler.h" #include "nsIScriptError.h" @@ -97,65 +96,6 @@ IsEligible(nsIChannel* aChannel, const CORSMode aCORSMode, return NS_ERROR_SRI_NOT_ELIGIBLE; } -/** - * Compute the hash of a sub-resource and compare it with the expected - * value. - */ -static nsresult -VerifyHash(const SRIMetadata& aMetadata, uint32_t aHashIndex, - uint32_t aStringLen, const uint8_t* aString, - const nsIDocument* aDocument) -{ - NS_ENSURE_ARG_POINTER(aString); - NS_ENSURE_ARG_POINTER(aDocument); - - nsAutoCString base64Hash; - aMetadata.GetHash(aHashIndex, &base64Hash); - SRILOG(("SRICheck::VerifyHash, hash[%u]=%s", aHashIndex, base64Hash.get())); - - nsAutoCString binaryHash; - if (NS_WARN_IF(NS_FAILED(Base64Decode(base64Hash, binaryHash)))) { - nsContentUtils::ReportToConsole(nsIScriptError::errorFlag, - NS_LITERAL_CSTRING("Sub-resource Integrity"), - aDocument, - nsContentUtils::eSECURITY_PROPERTIES, - "InvalidIntegrityBase64"); - return NS_ERROR_SRI_CORRUPT; - } - - uint32_t hashLength; - int8_t hashType; - aMetadata.GetHashType(&hashType, &hashLength); - if (binaryHash.Length() != hashLength) { - nsContentUtils::ReportToConsole(nsIScriptError::errorFlag, - NS_LITERAL_CSTRING("Sub-resource Integrity"), - aDocument, - nsContentUtils::eSECURITY_PROPERTIES, - "InvalidIntegrityLength"); - return NS_ERROR_SRI_CORRUPT; - } - - nsresult rv; - nsCOMPtr cryptoHash = - do_CreateInstance("@mozilla.org/security/hash;1", &rv); - NS_ENSURE_SUCCESS(rv, rv); - rv = cryptoHash->Init(hashType); - NS_ENSURE_SUCCESS(rv, rv); - rv = cryptoHash->Update(aString, aStringLen); - NS_ENSURE_SUCCESS(rv, rv); - - nsAutoCString computedHash; - rv = cryptoHash->Finish(false, computedHash); - NS_ENSURE_SUCCESS(rv, rv); - if (!binaryHash.Equals(computedHash)) { - SRILOG(("SRICheck::VerifyHash, hash[%u] did not match", aHashIndex)); - return NS_ERROR_SRI_CORRUPT; - } - - SRILOG(("SRICheck::VerifyHash, hash[%u] verified successfully", aHashIndex)); - return NS_OK; -} - /* static */ nsresult SRICheck::IntegrityMetadata(const nsAString& aMetadataList, const nsIDocument* aDocument, @@ -239,52 +179,6 @@ SRICheck::IntegrityMetadata(const nsAString& aMetadataList, return NS_OK; } -static nsresult -VerifyIntegrityInternal(const SRIMetadata& aMetadata, - nsIChannel* aChannel, - const CORSMode aCORSMode, - uint32_t aStringLen, - const uint8_t* aString, - const nsIDocument* aDocument) -{ - MOZ_ASSERT(!aMetadata.IsEmpty()); // should be checked by caller - - // IntegrityMetadata() checks this and returns "no metadata" if - // it's disabled so we should never make it this far - MOZ_ASSERT(Preferences::GetBool("security.sri.enable", false)); - - if (NS_FAILED(IsEligible(aChannel, aCORSMode, aDocument))) { - return NS_ERROR_SRI_NOT_ELIGIBLE; - } - if (!aMetadata.IsValid()) { - nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, - NS_LITERAL_CSTRING("Sub-resource Integrity"), - aDocument, - nsContentUtils::eSECURITY_PROPERTIES, - "NoValidMetadata"); - return NS_OK; // ignore invalid metadata for forward-compatibility - } - - for (uint32_t i = 0; i < aMetadata.HashCount(); i++) { - if (NS_SUCCEEDED(VerifyHash(aMetadata, i, aStringLen, - aString, aDocument))) { - return NS_OK; // stop at the first valid hash - } - } - - nsAutoCString alg; - aMetadata.GetAlgorithm(&alg); - NS_ConvertUTF8toUTF16 algUTF16(alg); - const char16_t* params[] = { algUTF16.get() }; - nsContentUtils::ReportToConsole(nsIScriptError::errorFlag, - NS_LITERAL_CSTRING("Sub-resource Integrity"), - aDocument, - nsContentUtils::eSECURITY_PROPERTIES, - "IntegrityMismatch", - params, ArrayLength(params)); - return NS_ERROR_SRI_CORRUPT; -} - /* static */ nsresult SRICheck::VerifyIntegrity(const SRIMetadata& aMetadata, nsIUnicharStreamLoader* aLoader, @@ -306,40 +200,188 @@ SRICheck::VerifyIntegrity(const SRIMetadata& aMetadata, originalURI) { originalURI->GetAsciiSpec(requestURL); } - SRILOG(("SRICheck::VerifyIntegrity (unichar stream), url=%s (length=%u)", - requestURL.get(), utf8Hash.Length())); + SRILOG(("SRICheck::VerifyIntegrity (unichar stream)")); } - return VerifyIntegrityInternal(aMetadata, channel, aCORSMode, - utf8Hash.Length(), (uint8_t*)utf8Hash.get(), - aDocument); + SRICheckDataVerifier verifier(aMetadata, aDocument); + nsresult rv; + rv = verifier.Update(utf8Hash.Length(), (uint8_t*)utf8Hash.get()); + NS_ENSURE_SUCCESS(rv, rv); + + return verifier.Verify(aMetadata, channel, aCORSMode, aDocument); } -/* static */ nsresult -SRICheck::VerifyIntegrity(const SRIMetadata& aMetadata, - nsIIncrementalStreamLoader* aLoader, - const CORSMode aCORSMode, - uint32_t aStringLen, - const uint8_t* aString, - const nsIDocument* aDocument) +////////////////////////////////////////////////////////////// +// +////////////////////////////////////////////////////////////// +SRICheckDataVerifier::SRICheckDataVerifier(const SRIMetadata& aMetadata, + const nsIDocument* aDocument) + : mCryptoHash(nullptr), + mBytesHashed(0), + mInvalidMetadata(false), + mComplete(false) { - NS_ENSURE_ARG_POINTER(aLoader); + MOZ_ASSERT(!aMetadata.IsEmpty()); // should be checked by caller - nsCOMPtr request; - aLoader->GetRequest(getter_AddRefs(request)); - NS_ENSURE_ARG_POINTER(request); - nsCOMPtr channel; - channel = do_QueryInterface(request); + // IntegrityMetadata() checks this and returns "no metadata" if + // it's disabled so we should never make it this far + MOZ_ASSERT(Preferences::GetBool("security.sri.enable", false)); + + if (!aMetadata.IsValid()) { + nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, + NS_LITERAL_CSTRING("Sub-resource Integrity"), + aDocument, + nsContentUtils::eSECURITY_PROPERTIES, + "NoValidMetadata"); + mInvalidMetadata = true; + return; // ignore invalid metadata for forward-compatibility + } + + uint32_t hashLength; + aMetadata.GetHashType(&mHashType, &hashLength); +} + +nsresult +SRICheckDataVerifier::EnsureCryptoHash() +{ + MOZ_ASSERT(!mInvalidMetadata); + + if (mCryptoHash) { + return NS_OK; + } + + nsresult rv; + nsCOMPtr cryptoHash = + do_CreateInstance("@mozilla.org/security/hash;1", &rv); + NS_ENSURE_SUCCESS(rv, rv); + rv = cryptoHash->Init(mHashType); + NS_ENSURE_SUCCESS(rv, rv); + + mCryptoHash = cryptoHash; + return NS_OK; +} + +nsresult +SRICheckDataVerifier::Update(uint32_t aStringLen, const uint8_t* aString) +{ + NS_ENSURE_ARG_POINTER(aString); + if (mInvalidMetadata) { + return NS_OK; // ignoring any data updates, see mInvalidMetadata usage + } + + nsresult rv; + rv = EnsureCryptoHash(); + NS_ENSURE_SUCCESS(rv, rv); + + mBytesHashed += aStringLen; + + return mCryptoHash->Update(aString, aStringLen); +} + +nsresult +SRICheckDataVerifier::Finish() +{ + if (mInvalidMetadata || mComplete) { + return NS_OK; // already finished or invalid metadata + } + + nsresult rv; + rv = EnsureCryptoHash(); // we need computed hash even for 0-length data + NS_ENSURE_SUCCESS(rv, rv); + + rv = mCryptoHash->Finish(false, mComputedHash); + mCryptoHash = nullptr; + mComplete = true; + return rv; +} + +nsresult +SRICheckDataVerifier::VerifyHash(const SRIMetadata& aMetadata, + uint32_t aHashIndex, + const nsIDocument* aDocument) +{ + NS_ENSURE_ARG_POINTER(aDocument); + + nsAutoCString base64Hash; + aMetadata.GetHash(aHashIndex, &base64Hash); + SRILOG(("SRICheckDataVerifier::VerifyHash, hash[%u]=%s", aHashIndex, base64Hash.get())); + + nsAutoCString binaryHash; + if (NS_WARN_IF(NS_FAILED(Base64Decode(base64Hash, binaryHash)))) { + nsContentUtils::ReportToConsole(nsIScriptError::errorFlag, + NS_LITERAL_CSTRING("Sub-resource Integrity"), + aDocument, + nsContentUtils::eSECURITY_PROPERTIES, + "InvalidIntegrityBase64"); + return NS_ERROR_SRI_CORRUPT; + } + + uint32_t hashLength; + int8_t hashType; + aMetadata.GetHashType(&hashType, &hashLength); + if (binaryHash.Length() != hashLength) { + nsContentUtils::ReportToConsole(nsIScriptError::errorFlag, + NS_LITERAL_CSTRING("Sub-resource Integrity"), + aDocument, + nsContentUtils::eSECURITY_PROPERTIES, + "InvalidIntegrityLength"); + return NS_ERROR_SRI_CORRUPT; + } + + if (!binaryHash.Equals(mComputedHash)) { + SRILOG(("SRICheckDataVerifier::VerifyHash, hash[%u] did not match", aHashIndex)); + return NS_ERROR_SRI_CORRUPT; + } + + SRILOG(("SRICheckDataVerifier::VerifyHash, hash[%u] verified successfully", aHashIndex)); + return NS_OK; +} + +nsresult +SRICheckDataVerifier::Verify(const SRIMetadata& aMetadata, + nsIChannel* aChannel, + const CORSMode aCORSMode, + const nsIDocument* aDocument) +{ + NS_ENSURE_ARG_POINTER(aDocument); if (MOZ_LOG_TEST(GetSriLog(), mozilla::LogLevel::Debug)) { nsAutoCString requestURL; + nsCOMPtr request; + request = do_QueryInterface(aChannel); request->GetName(requestURL); - SRILOG(("SRICheck::VerifyIntegrity (stream), url=%s (length=%u)", - requestURL.get(), aStringLen)); + SRILOG(("SRICheckDataVerifier::Verify, url=%s (length=%lu)", + requestURL.get(), mBytesHashed)); } - return VerifyIntegrityInternal(aMetadata, channel, aCORSMode, - aStringLen, aString, aDocument); + nsresult rv = Finish(); + NS_ENSURE_SUCCESS(rv, rv); + + if (NS_FAILED(IsEligible(aChannel, aCORSMode, aDocument))) { + return NS_ERROR_SRI_NOT_ELIGIBLE; + } + + if (mInvalidMetadata) { + return NS_OK; // ignore invalid metadata for forward-compatibility + } + + for (uint32_t i = 0; i < aMetadata.HashCount(); i++) { + if (NS_SUCCEEDED(VerifyHash(aMetadata, i, aDocument))) { + return NS_OK; // stop at the first valid hash + } + } + + nsAutoCString alg; + aMetadata.GetAlgorithm(&alg); + NS_ConvertUTF8toUTF16 algUTF16(alg); + const char16_t* params[] = { algUTF16.get() }; + nsContentUtils::ReportToConsole(nsIScriptError::errorFlag, + NS_LITERAL_CSTRING("Sub-resource Integrity"), + aDocument, + nsContentUtils::eSECURITY_PROPERTIES, + "IntegrityMismatch", + params, ArrayLength(params)); + return NS_ERROR_SRI_CORRUPT; } } // namespace dom diff --git a/dom/security/SRICheck.h b/dom/security/SRICheck.h index 0aecba50fb..8734c1f99b 100644 --- a/dom/security/SRICheck.h +++ b/dom/security/SRICheck.h @@ -9,10 +9,11 @@ #include "mozilla/CORSMode.h" #include "nsCOMPtr.h" +#include "nsICryptoHash.h" #include "SRIMetadata.h" +class nsIChannel; class nsIDocument; -class nsIIncrementalStreamLoader; class nsIUnicharStreamLoader; namespace mozilla { @@ -41,17 +42,30 @@ public: const CORSMode aCORSMode, const nsAString& aString, const nsIDocument* aDocument); +}; - /** - * Process the integrity attribute of the element. A result of false - * must prevent the resource from loading. - */ - static nsresult VerifyIntegrity(const SRIMetadata& aMetadata, - nsIIncrementalStreamLoader* aLoader, - const CORSMode aCORSMode, - uint32_t aStringLen, - const uint8_t* aString, - const nsIDocument* aDocument); +class SRICheckDataVerifier final +{ + public: + SRICheckDataVerifier(const SRIMetadata& aMetadata, + const nsIDocument* aDocument); + + nsresult Update(uint32_t aStringLen, const uint8_t* aString); + nsresult Verify(const SRIMetadata& aMetadata, nsIChannel* aChannel, + const CORSMode aCORSMode, const nsIDocument* aDocument); + + private: + nsCOMPtr mCryptoHash; + nsAutoCString mComputedHash; + size_t mBytesHashed; + int8_t mHashType; + bool mInvalidMetadata; + bool mComplete; + + nsresult EnsureCryptoHash(); + nsresult Finish(); + nsresult VerifyHash(const SRIMetadata& aMetadata, uint32_t aHashIndex, + const nsIDocument* aDocument); }; } // namespace dom diff --git a/embedding/components/printingui/ipc/PPrintingTypes.ipdlh b/embedding/components/printingui/ipc/PPrintingTypes.ipdlh index 5a4d7a21c1..550410baaf 100644 --- a/embedding/components/printingui/ipc/PPrintingTypes.ipdlh +++ b/embedding/components/printingui/ipc/PPrintingTypes.ipdlh @@ -51,7 +51,6 @@ struct PrintData { bool showPrintProgress; nsString paperName; - short paperSizeType; short paperData; double paperWidth; double paperHeight; @@ -75,6 +74,8 @@ struct PrintData { /* Windows-specific things */ nsString driverName; nsString deviceName; + double printableWidthInInches; + double printableHeightInInches; bool isFramesetDocument; bool isFramesetFrameSelected; bool isIFrameSelected; diff --git a/embedding/components/printingui/win/nsPrintDialogUtil.cpp b/embedding/components/printingui/win/nsPrintDialogUtil.cpp index 4ab579618b..6931f183e2 100644 --- a/embedding/components/printingui/win/nsPrintDialogUtil.cpp +++ b/embedding/components/printingui/win/nsPrintDialogUtil.cpp @@ -74,224 +74,6 @@ static bool gDialogWasExtended = false; static HWND gParentWnd = nullptr; -//****************************************************** -// Define native paper sizes -//****************************************************** -typedef struct { - short mPaperSize; // native enum - double mWidth; - double mHeight; - bool mIsInches; -} NativePaperSizes; - -// There are around 40 default print sizes defined by Windows -const NativePaperSizes kPaperSizes[] = { - {DMPAPER_LETTER, 8.5, 11.0, true}, - {DMPAPER_LEGAL, 8.5, 14.0, true}, - {DMPAPER_A4, 210.0, 297.0, false}, - {DMPAPER_TABLOID, 11.0, 17.0, true}, - {DMPAPER_LEDGER, 17.0, 11.0, true}, - {DMPAPER_STATEMENT, 5.5, 8.5, true}, - {DMPAPER_EXECUTIVE, 7.25, 10.5, true}, - {DMPAPER_A3, 297.0, 420.0, false}, - {DMPAPER_A5, 148.0, 210.0, false}, - {DMPAPER_CSHEET, 17.0, 22.0, true}, - {DMPAPER_DSHEET, 22.0, 34.0, true}, - {DMPAPER_ESHEET, 34.0, 44.0, true}, - {DMPAPER_LETTERSMALL, 8.5, 11.0, true}, - {DMPAPER_A4SMALL, 210.0, 297.0, false}, - {DMPAPER_B4, 250.0, 354.0, false}, - {DMPAPER_B5, 182.0, 257.0, false}, - {DMPAPER_FOLIO, 8.5, 13.0, true}, - {DMPAPER_QUARTO, 215.0, 275.0, false}, - {DMPAPER_10X14, 10.0, 14.0, true}, - {DMPAPER_11X17, 11.0, 17.0, true}, - {DMPAPER_NOTE, 8.5, 11.0, true}, - {DMPAPER_ENV_9, 3.875, 8.875, true}, - {DMPAPER_ENV_10, 40.125, 9.5, true}, - {DMPAPER_ENV_11, 4.5, 10.375, true}, - {DMPAPER_ENV_12, 4.75, 11.0, true}, - {DMPAPER_ENV_14, 5.0, 11.5, true}, - {DMPAPER_ENV_DL, 110.0, 220.0, false}, - {DMPAPER_ENV_C5, 162.0, 229.0, false}, - {DMPAPER_ENV_C3, 324.0, 458.0, false}, - {DMPAPER_ENV_C4, 229.0, 324.0, false}, - {DMPAPER_ENV_C6, 114.0, 162.0, false}, - {DMPAPER_ENV_C65, 114.0, 229.0, false}, - {DMPAPER_ENV_B4, 250.0, 353.0, false}, - {DMPAPER_ENV_B5, 176.0, 250.0, false}, - {DMPAPER_ENV_B6, 176.0, 125.0, false}, - {DMPAPER_ENV_ITALY, 110.0, 230.0, false}, - {DMPAPER_ENV_MONARCH, 3.875, 7.5, true}, - {DMPAPER_ENV_PERSONAL, 3.625, 6.5, true}, - {DMPAPER_FANFOLD_US, 14.875, 11.0, true}, - {DMPAPER_FANFOLD_STD_GERMAN, 8.5, 12.0, true}, - {DMPAPER_FANFOLD_LGL_GERMAN, 8.5, 13.0, true}, -}; -const int32_t kNumPaperSizes = 41; - -//---------------------------------------------------------------------------------- -// Map an incoming size to a Windows Native enum in the DevMode -static void -MapPaperSizeToNativeEnum(LPDEVMODEW aDevMode, - int16_t aType, - double aW, - double aH) -{ - -#ifdef DEBUG_rods - BOOL doingOrientation = aDevMode->dmFields & DM_ORIENTATION; - BOOL doingPaperSize = aDevMode->dmFields & DM_PAPERSIZE; - BOOL doingPaperLength = aDevMode->dmFields & DM_PAPERLENGTH; - BOOL doingPaperWidth = aDevMode->dmFields & DM_PAPERWIDTH; -#endif - - const double kThreshold = 0.05; - for (int32_t i=0;i width-kThreshold && - aH < height+kThreshold && aH > height-kThreshold) { - aDevMode->dmPaperSize = kPaperSizes[i].mPaperSize; - aDevMode->dmFields &= ~DM_PAPERLENGTH; - aDevMode->dmFields &= ~DM_PAPERWIDTH; - aDevMode->dmFields |= DM_PAPERSIZE; - return; - } - } - - short width = 0; - short height = 0; - if (aType == nsIPrintSettings::kPaperSizeInches) { - width = short(NS_TWIPS_TO_MILLIMETERS(NS_INCHES_TO_TWIPS(float(aW))) / 10); - height = short(NS_TWIPS_TO_MILLIMETERS(NS_INCHES_TO_TWIPS(float(aH))) / 10); - - } else if (aType == nsIPrintSettings::kPaperSizeMillimeters) { - width = short(aW / 10.0); - height = short(aH / 10.0); - } else { - return; // don't set anything - } - - // width and height is in - aDevMode->dmPaperSize = 0; - aDevMode->dmPaperWidth = width; - aDevMode->dmPaperLength = height; - - aDevMode->dmFields |= DM_PAPERSIZE; - aDevMode->dmFields |= DM_PAPERLENGTH; - aDevMode->dmFields |= DM_PAPERWIDTH; -} - -//---------------------------------------------------------------------------------- -// Setup Paper Size & Orientation options into the DevMode -// -static void -SetupDevModeFromSettings(LPDEVMODEW aDevMode, nsIPrintSettings* aPrintSettings) -{ - // Setup paper size - if (aPrintSettings) { - int16_t type; - aPrintSettings->GetPaperSizeType(&type); - if (type == nsIPrintSettings::kPaperSizeNativeData) { - int16_t paperEnum; - aPrintSettings->GetPaperData(&paperEnum); - aDevMode->dmPaperSize = paperEnum; - aDevMode->dmFields &= ~DM_PAPERLENGTH; - aDevMode->dmFields &= ~DM_PAPERWIDTH; - aDevMode->dmFields |= DM_PAPERSIZE; - } else { - int16_t unit; - double width, height; - aPrintSettings->GetPaperSizeUnit(&unit); - aPrintSettings->GetPaperWidth(&width); - aPrintSettings->GetPaperHeight(&height); - MapPaperSizeToNativeEnum(aDevMode, unit, width, height); - } - - // Setup Orientation - int32_t orientation; - aPrintSettings->GetOrientation(&orientation); - aDevMode->dmOrientation = orientation == nsIPrintSettings::kPortraitOrientation?DMORIENT_PORTRAIT:DMORIENT_LANDSCAPE; - aDevMode->dmFields |= DM_ORIENTATION; - - // Setup Number of Copies - int32_t copies; - aPrintSettings->GetNumCopies(&copies); - aDevMode->dmCopies = copies; - aDevMode->dmFields |= DM_COPIES; - - } - -} - -//---------------------------------------------------------------------------------- -// Helper Function - Free and reallocate the string -static nsresult -SetPrintSettingsFromDevMode(nsIPrintSettings* aPrintSettings, - LPDEVMODEW aDevMode) -{ - if (aPrintSettings == nullptr) { - return NS_ERROR_FAILURE; - } - - aPrintSettings->SetIsInitializedFromPrinter(true); - if (aDevMode->dmFields & DM_ORIENTATION) { - int32_t orientation = aDevMode->dmOrientation == DMORIENT_PORTRAIT? - nsIPrintSettings::kPortraitOrientation:nsIPrintSettings::kLandscapeOrientation; - aPrintSettings->SetOrientation(orientation); - } - - // Setup Number of Copies - if (aDevMode->dmFields & DM_COPIES) { - aPrintSettings->SetNumCopies(int32_t(aDevMode->dmCopies)); - } - - // Scaling - // Since we do the scaling, grab their value and reset back to 100 - if (aDevMode->dmFields & DM_SCALE) { - double origScale = 1.0; - aPrintSettings->GetScaling(&origScale); - double scale = double(aDevMode->dmScale) / 100.0f; - if (origScale == 1.0 || scale != 1.0) { - aPrintSettings->SetScaling(scale); - } - aDevMode->dmScale = 100; - // To turn this on you must change where the mPrt->mShrinkToFit is being set in the DocumentViewer - //aPrintSettings->SetShrinkToFit(false); - } - - if (aDevMode->dmFields & DM_PAPERSIZE) { - aPrintSettings->SetPaperSizeType(nsIPrintSettings::kPaperSizeNativeData); - aPrintSettings->SetPaperData(aDevMode->dmPaperSize); - for (int32_t i=0;idmPaperSize) { - aPrintSettings->SetPaperSizeUnit(kPaperSizes[i].mIsInches?nsIPrintSettings::kPaperSizeInches:nsIPrintSettings::kPaperSizeMillimeters); - break; - } - } - - } else if (aDevMode->dmFields & DM_PAPERLENGTH && aDevMode->dmFields & DM_PAPERWIDTH) { - bool found = false; - for (int32_t i=0;idmPaperSize) { - aPrintSettings->SetPaperSizeType(nsIPrintSettings::kPaperSizeDefined); - aPrintSettings->SetPaperWidth(kPaperSizes[i].mWidth); - aPrintSettings->SetPaperHeight(kPaperSizes[i].mHeight); - aPrintSettings->SetPaperSizeUnit(kPaperSizes[i].mIsInches?nsIPrintSettings::kPaperSizeInches:nsIPrintSettings::kPaperSizeMillimeters); - found = true; - break; - } - } - if (!found) { - return NS_ERROR_FAILURE; - } - } else { - return NS_ERROR_FAILURE; - } - return NS_OK; -} - //---------------------------------------------------------------------------------- // Return localized bundle for resource strings static nsresult @@ -724,7 +506,9 @@ HGLOBAL CreateGlobalDevModeAndInit(const nsXPIDLString& aPrintName, nsIPrintSett if (devMode) { memcpy(devMode, pNewDevMode, dwNeeded); // Initialize values from the PrintSettings - SetupDevModeFromSettings(devMode, aPS); + nsCOMPtr psWin = do_QueryInterface(aPS); + MOZ_ASSERT(psWin); + psWin->CopyToNative(devMode); // Sets back the changes we made to the DevMode into the Printer Driver dwRet = ::DocumentPropertiesW(gParentWnd, hPrinter, printName, devMode, devMode, DM_IN_BUFFER | DM_OUT_BUFFER); @@ -987,13 +771,14 @@ ShowNativePrintDialog(HWND aHWnd, // Transfer the settings from the native data to the PrintSettings LPDEVMODEW devMode = (LPDEVMODEW)::GlobalLock(prntdlg.hDevMode); - if (devMode == nullptr) { + if (!devMode || !prntdlg.hDC) { ::GlobalFree(hGlobalDevMode); return NS_ERROR_FAILURE; } psWin->SetDevMode(devMode); // copies DevMode - SetPrintSettingsFromDevMode(aPrintSettings, devMode); + psWin->CopyFromNative(prntdlg.hDC, devMode); ::GlobalUnlock(prntdlg.hDevMode); + ::DeleteDC(prntdlg.hDC); #if defined(DEBUG_rods) || defined(DEBUG_dcone) bool printSelection = prntdlg.Flags & PD_SELECTION; diff --git a/gfx/2d/2D.h b/gfx/2d/2D.h index c7109a45b4..2011bc4d0f 100644 --- a/gfx/2d/2D.h +++ b/gfx/2d/2D.h @@ -746,7 +746,8 @@ public: * Release takes the original data pointer for safety. */ virtual bool LockBits(uint8_t** aData, IntSize* aSize, - int32_t* aStride, SurfaceFormat* aFormat) { return false; } + int32_t* aStride, SurfaceFormat* aFormat, + IntPoint* aOrigin = nullptr) { return false; } virtual void ReleaseBits(uint8_t* aData) {} /** Ensure that the DrawTarget backend has flushed all drawing operations to @@ -967,6 +968,35 @@ public: */ virtual void PopClip() = 0; + /** + * Push a 'layer' to the DrawTarget, a layer is a temporary surface that all + * drawing will be redirected to, this is used for example to support group + * opacity or the masking of groups. Clips must be balanced within a layer, + * i.e. between a matching PushLayer/PopLayer pair there must be as many + * PushClip(Rect) calls as there are PopClip calls. + * + * @param aOpaque Whether the layer will be opaque + * @param aOpacity Opacity of the layer + * @param aMask Mask applied to the layer + * @param aMaskTransform Transform applied to the layer mask + * @param aBounds Optional bounds in device space to which the layer is + * limited in size. + * @param aCopyBackground Whether to copy the background into the layer, this + * is only supported when aOpaque is true. + */ + virtual void PushLayer(bool aOpaque, Float aOpacity, + SourceSurface* aMask, + const Matrix& aMaskTransform, + const IntRect& aBounds = IntRect(), + bool aCopyBackground = false) { MOZ_CRASH(); } + + /** + * This balances a call to PushLayer and proceeds to blend the layer back + * onto the background. This blend will blend the temporary surface back + * onto the target in device space using POINT sampling and operator over. + */ + virtual void PopLayer() { MOZ_CRASH(); } + /** * Create a SourceSurface optimized for use with this DrawTarget from * existing bitmap data in memory. @@ -1113,6 +1143,10 @@ public: return mOpaqueRect; } + virtual bool IsCurrentGroupOpaque() { + return GetFormat() == SurfaceFormat::B8G8R8X8; + } + virtual void SetPermitSubpixelAA(bool aPermitSubpixelAA) { mPermitSubpixelAA = aPermitSubpixelAA; } diff --git a/gfx/2d/BorrowedContext.h b/gfx/2d/BorrowedContext.h index 0b5612ad8e..73b624c27d 100644 --- a/gfx/2d/BorrowedContext.h +++ b/gfx/2d/BorrowedContext.h @@ -124,6 +124,8 @@ public: Drawable GetDrawable() const { return mDrawable; } Screen *GetScreen() const { return mScreen; } Visual *GetVisual() const { return mVisual; } + IntSize GetSize() const { return mSize; } + Point GetOffset() const { return mOffset; } XRenderPictFormat* GetXRenderFormat() const { return mXRenderFormat; } @@ -134,6 +136,8 @@ private: Screen *mScreen; Visual *mVisual; XRenderPictFormat *mXRenderFormat; + IntSize mSize; + Point mOffset; }; #endif diff --git a/gfx/2d/DrawTargetCairo.cpp b/gfx/2d/DrawTargetCairo.cpp index f9d7e5bca5..3d9f3244a5 100644 --- a/gfx/2d/DrawTargetCairo.cpp +++ b/gfx/2d/DrawTargetCairo.cpp @@ -714,17 +714,32 @@ DrawTargetCairo::Snapshot() bool DrawTargetCairo::LockBits(uint8_t** aData, IntSize* aSize, - int32_t* aStride, SurfaceFormat* aFormat) + int32_t* aStride, SurfaceFormat* aFormat, + IntPoint* aOrigin) { - if (cairo_surface_get_type(mSurface) == CAIRO_SURFACE_TYPE_IMAGE) { + cairo_surface_t* surf = cairo_get_group_target(mContext); + if (cairo_surface_get_type(surf) == CAIRO_SURFACE_TYPE_IMAGE) { + PointDouble offset; + cairo_surface_get_device_offset(surf, &offset.x, &offset.y); + // verify the device offset can be converted to integers suitable for a bounds rect + IntPoint origin(int32_t(-offset.x), int32_t(-offset.y)); + if (-PointDouble(origin) != offset || + (!aOrigin && origin != IntPoint())) { + return false; + } + WillChange(); Flush(); - mLockedBits = cairo_image_surface_get_data(mSurface); + mLockedBits = cairo_image_surface_get_data(surf); *aData = mLockedBits; - *aSize = GetSize(); - *aStride = cairo_image_surface_get_stride(mSurface); - *aFormat = GetFormat(); + *aSize = IntSize(cairo_image_surface_get_width(surf), + cairo_image_surface_get_height(surf)); + *aStride = cairo_image_surface_get_stride(surf); + *aFormat = CairoFormatToGfxFormat(cairo_image_surface_get_format(surf)); + if (aOrigin) { + *aOrigin = origin; + } return true; } @@ -736,13 +751,14 @@ DrawTargetCairo::ReleaseBits(uint8_t* aData) { MOZ_ASSERT(mLockedBits == aData); mLockedBits = nullptr; - cairo_surface_mark_dirty(mSurface); + cairo_surface_t* surf = cairo_get_group_target(mContext); + cairo_surface_mark_dirty(surf); } void DrawTargetCairo::Flush() { - cairo_surface_t* surf = cairo_get_target(mContext); + cairo_surface_t* surf = cairo_get_group_target(mContext); cairo_surface_flush(surf); } @@ -1234,6 +1250,18 @@ DrawTargetCairo::Fill(const Path *aPath, DrawPattern(aPattern, StrokeOptions(), aOptions, DRAW_FILL); } +bool +DrawTargetCairo::IsCurrentGroupOpaque() +{ + cairo_surface_t* surf = cairo_get_group_target(mContext); + + if (!surf) { + return false; + } + + return cairo_surface_get_content(surf) == CAIRO_CONTENT_COLOR; +} + void DrawTargetCairo::SetPermitSubpixelAA(bool aPermitSubpixelAA) { @@ -1455,6 +1483,87 @@ DrawTargetCairo::PopClip() cairo_set_matrix(mContext, &mat); } + +void +DrawTargetCairo::PushLayer(bool aOpaque, Float aOpacity, SourceSurface* aMask, + const Matrix& aMaskTransform, const IntRect& aBounds, + bool aCopyBackground) +{ + cairo_content_t content = CAIRO_CONTENT_COLOR_ALPHA; + + if (mFormat == SurfaceFormat::A8) { + content = CAIRO_CONTENT_ALPHA; + } else if (aOpaque) { + content = CAIRO_CONTENT_COLOR; + } + + if (aCopyBackground) { + cairo_surface_t* source = cairo_get_group_target(mContext); + cairo_push_group_with_content(mContext, content); + cairo_surface_t* dest = cairo_get_group_target(mContext); + cairo_t* ctx = cairo_create(dest); + cairo_set_source_surface(ctx, source, 0, 0); + cairo_set_operator(ctx, CAIRO_OPERATOR_SOURCE); + cairo_paint(ctx); + cairo_destroy(ctx); + } else { + cairo_push_group_with_content(mContext, content); + } + + PushedLayer layer(aOpacity, mPermitSubpixelAA); + + if (aMask) { + cairo_surface_t* surf = GetCairoSurfaceForSourceSurface(aMask); + if (surf) { + layer.mMaskPattern = cairo_pattern_create_for_surface(surf); + cairo_matrix_t mat; + GfxMatrixToCairoMatrix(aMaskTransform, mat); + cairo_matrix_invert(&mat); + cairo_pattern_set_matrix(layer.mMaskPattern, &mat); + cairo_surface_destroy(surf); + } else { + gfxCriticalError() << "Failed to get cairo surface for mask surface!"; + } + } + + mPushedLayers.push_back(layer); + + SetPermitSubpixelAA(aOpaque); +} + +void +DrawTargetCairo::PopLayer() +{ + MOZ_ASSERT(mPushedLayers.size()); + + cairo_set_operator(mContext, CAIRO_OPERATOR_OVER); + + cairo_pop_group_to_source(mContext); + + PushedLayer layer = mPushedLayers.back(); + mPushedLayers.pop_back(); + + if (!layer.mMaskPattern) { + cairo_paint_with_alpha(mContext, layer.mOpacity); + } else { + if (layer.mOpacity != Float(1.0)) { + cairo_push_group_with_content(mContext, CAIRO_CONTENT_COLOR_ALPHA); + + // Now draw the content using the desired operator + cairo_paint_with_alpha(mContext, layer.mOpacity); + + cairo_pop_group_to_source(mContext); + } + cairo_mask(mContext, layer.mMaskPattern); + } + + cairo_matrix_t mat; + GfxMatrixToCairoMatrix(mTransform, mat); + cairo_set_matrix(mContext, &mat); + + cairo_pattern_destroy(layer.mMaskPattern); + SetPermitSubpixelAA(layer.mWasPermittingSubpixelAA); +} already_AddRefed DrawTargetCairo::CreatePathBuilder(FillRule aFillRule /* = FillRule::FILL_WINDING */) const @@ -1765,7 +1874,7 @@ void * DrawTargetCairo::GetNativeSurface(NativeSurfaceType aType) { if (aType == NativeSurfaceType::CAIRO_SURFACE) { - return cairo_get_target(mContext); + return cairo_get_group_target(mContext); } if (aType == NativeSurfaceType::CAIRO_CONTEXT) { return mContext; @@ -1868,7 +1977,7 @@ BorrowedXlibDrawable::Init(DrawTarget* aDT) } DrawTargetCairo* cairoDT = static_cast(aDT); - cairo_surface_t* surf = cairoDT->mSurface; + cairo_surface_t* surf = cairo_get_group_target(cairoDT->mContext); if (cairo_surface_get_type(surf) != CAIRO_SURFACE_TYPE_XLIB) { return false; } @@ -1881,6 +1990,12 @@ BorrowedXlibDrawable::Init(DrawTarget* aDT) mScreen = cairo_xlib_surface_get_screen(surf); mVisual = cairo_xlib_surface_get_visual(surf); mXRenderFormat = cairo_xlib_surface_get_xrender_format(surf); + mSize.width = cairo_xlib_surface_get_width(surf); + mSize.height = cairo_xlib_surface_get_height(surf); + + double x = 0, y = 0; + cairo_surface_get_device_offset(surf, &x, &y); + mOffset = Point(x, y); return true; #else @@ -1892,7 +2007,7 @@ void BorrowedXlibDrawable::Finish() { DrawTargetCairo* cairoDT = static_cast(mDT); - cairo_surface_t* surf = cairoDT->mSurface; + cairo_surface_t* surf = cairo_get_group_target(cairoDT->mContext); cairo_surface_mark_dirty(surf); if (mDrawable) { mDrawable = None; diff --git a/gfx/2d/DrawTargetCairo.h b/gfx/2d/DrawTargetCairo.h index 4fdd25de91..0b2b300a83 100644 --- a/gfx/2d/DrawTargetCairo.h +++ b/gfx/2d/DrawTargetCairo.h @@ -65,10 +65,13 @@ public: virtual already_AddRefed Snapshot() override; virtual IntSize GetSize() override; + virtual bool IsCurrentGroupOpaque() override; + virtual void SetPermitSubpixelAA(bool aPermitSubpixelAA) override; virtual bool LockBits(uint8_t** aData, IntSize* aSize, - int32_t* aStride, SurfaceFormat* aFormat) override; + int32_t* aStride, SurfaceFormat* aFormat, + IntPoint* aOrigin = nullptr) override; virtual void ReleaseBits(uint8_t* aData) override; virtual void Flush() override; @@ -134,6 +137,12 @@ public: virtual void PushClip(const Path *aPath) override; virtual void PushClipRect(const Rect &aRect) override; virtual void PopClip() override; + virtual void PushLayer(bool aOpaque, Float aOpacity, + SourceSurface* aMask, + const Matrix& aMaskTransform, + const IntRect& aBounds = IntRect(), + bool aCopyBackground = false) override; + virtual void PopLayer() override; virtual already_AddRefed CreatePathBuilder(FillRule aFillRule = FillRule::FILL_WINDING) const override; @@ -214,6 +223,19 @@ private: // data uint8_t* mLockedBits; + struct PushedLayer + { + PushedLayer(Float aOpacity, bool aWasPermittingSubpixelAA) + : mOpacity(aOpacity) + , mMaskPattern(nullptr) + , mWasPermittingSubpixelAA(aWasPermittingSubpixelAA) + {} + Float mOpacity; + cairo_pattern_t* mMaskPattern; + bool mWasPermittingSubpixelAA; + }; + std::vector mPushedLayers; + // The latest snapshot of this surface. This needs to be told when this // target is modified. We keep it alive as a cache. RefPtr mSnapshot; diff --git a/gfx/2d/DrawTargetD2D1.cpp b/gfx/2d/DrawTargetD2D1.cpp index 52e9e3d17c..96fa80e452 100644 --- a/gfx/2d/DrawTargetD2D1.cpp +++ b/gfx/2d/DrawTargetD2D1.cpp @@ -31,7 +31,7 @@ ID2D1Factory1 *D2DFactory1() } DrawTargetD2D1::DrawTargetD2D1() - : mClipsArePushed(false) + : mPushedLayers(1) { } @@ -267,7 +267,7 @@ DrawTargetD2D1::ClearRect(const Rect &aRect) mDC->FillGeometry(geom, brush); mDC->PopAxisAlignedClip(); - mDC->SetTarget(mBitmap); + mDC->SetTarget(CurrentTarget()); list->Close(); mDC->DrawImage(list, D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR, D2D1_COMPOSITE_MODE_DESTINATION_OUT); @@ -515,7 +515,7 @@ DrawTargetD2D1::FillGlyphs(ScaledFont *aFont, PrepareForDrawing(aOptions.mCompositionOp, aPattern); bool forceClearType = false; - if (mFormat == SurfaceFormat::B8G8R8A8 && mPermitSubpixelAA && + if (!CurrentLayer().mIsOpaque && mPermitSubpixelAA && aOptions.mCompositionOp == CompositionOp::OP_OVER && aaMode == AntialiasMode::SUBPIXEL) { forceClearType = true; } @@ -538,7 +538,7 @@ DrawTargetD2D1::FillGlyphs(ScaledFont *aFont, } if (d2dAAMode == D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE && - mFormat != SurfaceFormat::B8G8R8X8 && !forceClearType) { + !CurrentLayer().mIsOpaque && !forceClearType) { d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE; } @@ -555,10 +555,10 @@ DrawTargetD2D1::FillGlyphs(ScaledFont *aFont, DWriteGlyphRunFromGlyphs(aBuffer, font, &autoRun); bool needsRepushedLayers = false; - if (d2dAAMode == D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE && mFormat != SurfaceFormat::B8G8R8X8) { + if (forceClearType) { D2D1_RECT_F rect; bool isAligned; - needsRepushedLayers = mPushedClips.size() && !GetDeviceSpaceClipRect(rect, isAligned); + needsRepushedLayers = CurrentLayer().mPushedClips.size() && !GetDeviceSpaceClipRect(rect, isAligned); // If we have a complex clip in our stack and we have a transparent // background, and subpixel AA is permitted, we need to repush our layer @@ -587,11 +587,7 @@ DrawTargetD2D1::FillGlyphs(ScaledFont *aFont, D2DFactory()->CreatePathGeometry(getter_AddRefs(path)); RefPtr sink; path->Open(getter_AddRefs(sink)); - sink->BeginFigure(D2D1::Point2F(userRect.left, userRect.top), D2D1_FIGURE_BEGIN_FILLED); - sink->AddLine(D2D1::Point2F(userRect.right, userRect.top)); - sink->AddLine(D2D1::Point2F(userRect.right, userRect.bottom)); - sink->AddLine(D2D1::Point2F(userRect.left, userRect.bottom)); - sink->EndFigure(D2D1_FIGURE_END_CLOSED); + AddRectToSink(sink, userRect); sink->Close(); mDC->PushLayer(D2D1::LayerParameters1(D2D1::InfiniteRect(), path, D2D1_ANTIALIAS_MODE_ALIASED, @@ -664,14 +660,14 @@ DrawTargetD2D1::PushClip(const Path *aPath) pathD2D->mGeometry->GetBounds(clip.mTransform, &clip.mBounds); - mPushedClips.push_back(clip); + CurrentLayer().mPushedClips.push_back(clip); // The transform of clips is relative to the world matrix, since we use the total // transform for the clips, make the world matrix identity. mDC->SetTransform(D2D1::IdentityMatrix()); mTransformDirty = true; - if (mClipsArePushed) { + if (CurrentLayer().mClipsArePushed) { PushD2DLayer(mDC, pathD2D->mGeometry, clip.mTransform); } } @@ -704,12 +700,12 @@ DrawTargetD2D1::PushClipRect(const Rect &aRect) // Do not store the transform, just store the device space rectangle directly. clip.mBounds = D2DRect(rect); - mPushedClips.push_back(clip); + CurrentLayer().mPushedClips.push_back(clip); mDC->SetTransform(D2D1::IdentityMatrix()); mTransformDirty = true; - if (mClipsArePushed) { + if (CurrentLayer().mClipsArePushed) { mDC->PushAxisAlignedClip(clip.mBounds, clip.mIsPixelAligned ? D2D1_ANTIALIAS_MODE_ALIASED : D2D1_ANTIALIAS_MODE_PER_PRIMITIVE); } } @@ -719,14 +715,95 @@ DrawTargetD2D1::PopClip() { mCurrentClippedGeometry = nullptr; - if (mClipsArePushed) { - if (mPushedClips.back().mPath) { + if (CurrentLayer().mClipsArePushed) { + if (CurrentLayer().mPushedClips.back().mPath) { mDC->PopLayer(); } else { mDC->PopAxisAlignedClip(); } } - mPushedClips.pop_back(); + CurrentLayer().mPushedClips.pop_back(); +} + +void +DrawTargetD2D1::PushLayer(bool aOpaque, Float aOpacity, SourceSurface* aMask, + const Matrix& aMaskTransform, const IntRect& aBounds, + bool aCopyBackground) +{ + D2D1_LAYER_OPTIONS1 options = D2D1_LAYER_OPTIONS1_NONE; + + if (aOpaque) { + options |= D2D1_LAYER_OPTIONS1_IGNORE_ALPHA; + } + if (aCopyBackground) { + options |= D2D1_LAYER_OPTIONS1_INITIALIZE_FROM_BACKGROUND; + } + + RefPtr mask; + + Matrix maskTransform = aMaskTransform; + + RefPtr clip; + if (aMask) { + mDC->SetTransform(D2D1::IdentityMatrix()); + mTransformDirty = true; + + RefPtr image = GetImageForSurface(aMask, maskTransform, ExtendMode::CLAMP); + + // The mask is given in user space. Our layer will apply it in device space. + maskTransform = maskTransform * mTransform; + + if (image) { + RefPtr bitmap; + image->QueryInterface((ID2D1Bitmap**)getter_AddRefs(bitmap)); + + mDC->CreateBitmapBrush(bitmap, D2D1::BitmapBrushProperties(), D2D1::BrushProperties(1.0f, D2DMatrix(maskTransform)), getter_AddRefs(mask)); + MOZ_ASSERT(bitmap); // This should always be true since it was created for a surface. + + factory()->CreatePathGeometry(getter_AddRefs(clip)); + RefPtr sink; + clip->Open(getter_AddRefs(sink)); + AddRectToSink(sink, D2D1::RectF(0, 0, aMask->GetSize().width, aMask->GetSize().height)); + sink->Close(); + } else { + gfxCriticalError() << "Failed to get image for mask surface!"; + } + } + + PushAllClips(); + + mDC->PushLayer(D2D1::LayerParameters1(D2D1::InfiniteRect(), clip, D2D1_ANTIALIAS_MODE_ALIASED, D2DMatrix(maskTransform), aOpacity, mask, options), nullptr); + PushedLayer pushedLayer; + pushedLayer.mClipsArePushed = false; + pushedLayer.mIsOpaque = aOpaque; + pushedLayer.mOldPermitSubpixelAA = mPermitSubpixelAA; + mPermitSubpixelAA = aOpaque; + + mDC->CreateCommandList(getter_AddRefs(pushedLayer.mCurrentList)); + mPushedLayers.push_back(pushedLayer); + + mDC->SetTarget(CurrentTarget()); +} + +void +DrawTargetD2D1::PopLayer() +{ + MOZ_ASSERT(CurrentLayer().mPushedClips.size() == 0); + + RefPtr list = CurrentLayer().mCurrentList; + mPermitSubpixelAA = CurrentLayer().mOldPermitSubpixelAA; + + mPushedLayers.pop_back(); + mDC->SetTarget(CurrentTarget()); + + list->Close(); + mDC->SetTransform(D2D1::IdentityMatrix()); + mTransformDirty = true; + + DCCommandSink sink(mDC); + list->Stream(&sink); + + mDC->PopLayer(); } already_AddRefed @@ -878,10 +955,12 @@ DrawTargetD2D1::Init(ID3D11Texture2D* aTexture, SurfaceFormat aFormat) return false; } - mDC->SetTarget(mBitmap); + mDC->SetTarget(CurrentTarget()); mDC->BeginDraw(); + CurrentLayer().mIsOpaque = aFormat == SurfaceFormat::B8G8R8X8; + return true; } @@ -922,7 +1001,7 @@ DrawTargetD2D1::Init(const IntSize &aSize, SurfaceFormat aFormat) return false; } - mDC->SetTarget(mBitmap); + mDC->SetTarget(CurrentTarget()); hr = mDC->CreateSolidColorBrush(D2D1::ColorF(0, 0), getter_AddRefs(mSolidColorBrush)); @@ -933,6 +1012,8 @@ DrawTargetD2D1::Init(const IntSize &aSize, SurfaceFormat aFormat) mDC->BeginDraw(); + CurrentLayer().mIsOpaque = aFormat == SurfaceFormat::B8G8R8X8; + mDC->Clear(); mFormat = aFormat; @@ -1048,7 +1129,7 @@ DrawTargetD2D1::FinalizeDrawing(CompositionOp aOp, const Pattern &aPattern) PopAllClips(); - mDC->SetTarget(mBitmap); + mDC->SetTarget(CurrentTarget()); mCommandList->Close(); RefPtr source = mCommandList; @@ -1061,31 +1142,23 @@ DrawTargetD2D1::FinalizeDrawing(CompositionOp aOp, const Pattern &aPattern) if (D2DSupportsCompositeMode(aOp)) { D2D1_RECT_F rect; bool isAligned; - RefPtr tmpBitmap; - bool clipIsComplex = mPushedClips.size() && !GetDeviceSpaceClipRect(rect, isAligned); + RefPtr tmpImage; + bool clipIsComplex = CurrentLayer().mPushedClips.size() && !GetDeviceSpaceClipRect(rect, isAligned); if (clipIsComplex) { if (!IsOperatorBoundByMask(aOp)) { - HRESULT hr = mDC->CreateBitmap(D2DIntSize(mSize), D2D1::BitmapProperties(D2DPixelFormat(mFormat)), getter_AddRefs(tmpBitmap)); - if (FAILED(hr)) { - gfxCriticalError(CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(mSize))) << "[D2D1.1] 6CreateBitmap failure " << mSize << " Code: " << hexa(hr) << " format " << (int)mFormat; - // For now, crash in this scenario; this should happen because tmpBitmap is - // null and CopyFromBitmap call below dereferences it. - // return; - } - mDC->Flush(); - - tmpBitmap->CopyFromBitmap(nullptr, mBitmap, nullptr); + tmpImage = GetImageForLayerContent(); } } else { PushAllClips(); } mDC->DrawImage(source, D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR, D2DCompositionMode(aOp)); - if (tmpBitmap) { - RefPtr brush; + if (tmpImage) { + RefPtr brush; RefPtr inverseGeom = GetInverseClippedGeometry(); - mDC->CreateBitmapBrush(tmpBitmap, getter_AddRefs(brush)); + mDC->CreateImageBrush(tmpImage, D2D1::ImageBrushProperties(D2D1::RectF(0, 0, mSize.width, mSize.height)), + getter_AddRefs(brush)); mDC->SetPrimitiveBlend(D2D1_PRIMITIVE_BLEND_COPY); mDC->FillGeometry(inverseGeom, brush); @@ -1094,37 +1167,23 @@ DrawTargetD2D1::FinalizeDrawing(CompositionOp aOp, const Pattern &aPattern) return; } - if (!mBlendEffect) { - HRESULT hr = mDC->CreateEffect(CLSID_D2D1Blend, getter_AddRefs(mBlendEffect)); + RefPtr blendEffect; + HRESULT hr = mDC->CreateEffect(CLSID_D2D1Blend, getter_AddRefs(blendEffect)); - if (FAILED(hr) || !mBlendEffect) { - gfxWarning() << "Failed to create blend effect!"; - return; - } - } - - RefPtr tmpBitmap; - HRESULT hr = mDC->CreateBitmap(D2DIntSize(mSize), D2D1::BitmapProperties(D2DPixelFormat(mFormat)), getter_AddRefs(tmpBitmap)); - if (FAILED(hr)) { - gfxCriticalError(CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(mSize))) << "[D2D1.1] 5CreateBitmap failure " << mSize << " Code: " << hexa(hr) << " format " << (int)mFormat; + if (FAILED(hr) || !blendEffect) { + gfxWarning() << "Failed to create blend effect!"; return; } - // This flush is important since the copy method will not know about the context drawing to the surface. - // We also need to pop all the clips to make sure any drawn content will have made it to the final bitmap. - mDC->Flush(); + RefPtr tmpImage = GetImageForLayerContent(); - // We need to use a copy here because affects don't accept a surface on - // both their in- and outputs. - tmpBitmap->CopyFromBitmap(nullptr, mBitmap, nullptr); - - mBlendEffect->SetInput(0, tmpBitmap); - mBlendEffect->SetInput(1, source); - mBlendEffect->SetValue(D2D1_BLEND_PROP_MODE, D2DBlendMode(aOp)); + blendEffect->SetInput(0, tmpImage); + blendEffect->SetInput(1, source); + blendEffect->SetValue(D2D1_BLEND_PROP_MODE, D2DBlendMode(aOp)); PushAllClips(); - mDC->DrawImage(mBlendEffect, D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR, D2D1_COMPOSITE_MODE_BOUNDED_SOURCE_COPY); + mDC->DrawImage(blendEffect, D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR, D2D1_COMPOSITE_MODE_BOUNDED_SOURCE_COPY); return; } @@ -1184,12 +1243,12 @@ IntersectRect(const D2D1_RECT_F& aRect1, const D2D1_RECT_F& aRect2) bool DrawTargetD2D1::GetDeviceSpaceClipRect(D2D1_RECT_F& aClipRect, bool& aIsPixelAligned) { - if (!mPushedClips.size()) { + if (!CurrentLayer().mPushedClips.size()) { return false; } aClipRect = D2D1::RectF(0, 0, mSize.width, mSize.height); - for (auto iter = mPushedClips.begin();iter != mPushedClips.end(); iter++) { + for (auto iter = CurrentLayer().mPushedClips.begin();iter != CurrentLayer().mPushedClips.end(); iter++) { if (iter->mPath) { return false; } @@ -1201,6 +1260,35 @@ DrawTargetD2D1::GetDeviceSpaceClipRect(D2D1_RECT_F& aClipRect, bool& aIsPixelAli return true; } +already_AddRefed +DrawTargetD2D1::GetImageForLayerContent() +{ + if (!CurrentLayer().mCurrentList) { + RefPtr tmpBitmap; + HRESULT hr = mDC->CreateBitmap(D2DIntSize(mSize), D2D1::BitmapProperties(D2DPixelFormat(mFormat)), getter_AddRefs(tmpBitmap)); + if (FAILED(hr)) { + gfxCriticalError(CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(mSize))) << "[D2D1.1] 6CreateBitmap failure " << mSize << " Code: " << hexa(hr) << " format " << (int)mFormat; + // For now, crash in this scenario; this should happen because tmpBitmap is + // null and CopyFromBitmap call below dereferences it. + // return; + } + mDC->Flush(); + + tmpBitmap->CopyFromBitmap(nullptr, mBitmap, nullptr); + return tmpBitmap.forget(); + } else { + RefPtr list = CurrentLayer().mCurrentList; + mDC->CreateCommandList(getter_AddRefs(CurrentLayer().mCurrentList)); + mDC->SetTarget(CurrentTarget()); + list->Close(); + + DCCommandSink sink(mDC); + list->Stream(&sink); + + return list.forget(); + } +} + already_AddRefed DrawTargetD2D1::GetClippedGeometry(IntRect *aClipBounds) { @@ -1210,7 +1298,7 @@ DrawTargetD2D1::GetClippedGeometry(IntRect *aClipBounds) return clippedGeometry.forget(); } - MOZ_ASSERT(mPushedClips.size()); + MOZ_ASSERT(CurrentLayer().mPushedClips.size()); mCurrentClipBounds = IntRect(IntPoint(0, 0), mSize); @@ -1218,7 +1306,7 @@ DrawTargetD2D1::GetClippedGeometry(IntRect *aClipBounds) RefPtr pathGeom; D2D1_RECT_F pathRect; bool pathRectIsAxisAligned = false; - auto iter = mPushedClips.begin(); + auto iter = CurrentLayer().mPushedClips.begin(); if (iter->mPath) { pathGeom = GetTransformedGeometry(iter->mPath->GetGeometry(), iter->mTransform); @@ -1228,7 +1316,7 @@ DrawTargetD2D1::GetClippedGeometry(IntRect *aClipBounds) } iter++; - for (;iter != mPushedClips.end(); iter++) { + for (;iter != CurrentLayer().mPushedClips.end(); iter++) { // Do nothing but add it to the current clip bounds. if (!iter->mPath && iter->mIsPixelAligned) { mCurrentClipBounds.IntersectRect(mCurrentClipBounds, @@ -1313,20 +1401,20 @@ DrawTargetD2D1::GetInverseClippedGeometry() void DrawTargetD2D1::PopAllClips() { - if (mClipsArePushed) { + if (CurrentLayer().mClipsArePushed) { PopClipsFromDC(mDC); - mClipsArePushed = false; + CurrentLayer().mClipsArePushed = false; } } void DrawTargetD2D1::PushAllClips() { - if (!mClipsArePushed) { + if (!CurrentLayer().mClipsArePushed) { PushClipsToDC(mDC); - mClipsArePushed = true; + CurrentLayer().mClipsArePushed = true; } } @@ -1336,8 +1424,7 @@ DrawTargetD2D1::PushClipsToDC(ID2D1DeviceContext *aDC, bool aForceIgnoreAlpha, c mDC->SetTransform(D2D1::IdentityMatrix()); mTransformDirty = true; - for (std::vector::iterator iter = mPushedClips.begin(); - iter != mPushedClips.end(); iter++) { + for (auto iter = CurrentLayer().mPushedClips.begin(); iter != CurrentLayer().mPushedClips.end(); iter++) { if (iter->mPath) { PushD2DLayer(aDC, iter->mPath->mGeometry, iter->mTransform, aForceIgnoreAlpha, aMaxRect); } else { @@ -1349,8 +1436,8 @@ DrawTargetD2D1::PushClipsToDC(ID2D1DeviceContext *aDC, bool aForceIgnoreAlpha, c void DrawTargetD2D1::PopClipsFromDC(ID2D1DeviceContext *aDC) { - for (int i = mPushedClips.size() - 1; i >= 0; i--) { - if (mPushedClips[i].mPath) { + for (int i = CurrentLayer().mPushedClips.size() - 1; i >= 0; i--) { + if (CurrentLayer().mPushedClips[i].mPath) { aDC->PopLayer(); } else { aDC->PopAxisAlignedClip(); diff --git a/gfx/2d/DrawTargetD2D1.h b/gfx/2d/DrawTargetD2D1.h index 94a2092c97..def9ffa64b 100644 --- a/gfx/2d/DrawTargetD2D1.h +++ b/gfx/2d/DrawTargetD2D1.h @@ -94,6 +94,12 @@ public: virtual void PushClip(const Path *aPath) override; virtual void PushClipRect(const Rect &aRect) override; virtual void PopClip() override; + virtual void PushLayer(bool aOpaque, Float aOpacity, + SourceSurface* aMask, + const Matrix& aMaskTransform, + const IntRect& aBounds = IntRect(), + bool aCopyBackground = false) override; + virtual void PopLayer() override; virtual already_AddRefed CreateSourceSurfaceFromData(unsigned char *aData, const IntSize &aSize, @@ -117,6 +123,7 @@ public: virtual already_AddRefed CreateFilter(FilterType aType) override; virtual bool SupportsRegionClipping() const override { return false; } + virtual bool IsCurrentGroupOpaque() override { return CurrentLayer().mIsOpaque; } virtual void *GetNativeSurface(NativeSurfaceType aType) override { return nullptr; } @@ -167,14 +174,27 @@ private: } void AddDependencyOnSource(SourceSurfaceD2D1* aSource); + // Must be called with all clips popped and an identity matrix set. + already_AddRefed GetImageForLayerContent(); + + ID2D1Image* CurrentTarget() + { + if (CurrentLayer().mCurrentList) { + return CurrentLayer().mCurrentList; + } + return mBitmap; + } + // This returns the clipped geometry, in addition it returns aClipBounds which // represents the intersection of all pixel-aligned rectangular clips that // are currently set. The returned clipped geometry must be clipped by these - // bounds to correctly reflect the total clip. This is in device space. + // bounds to correctly reflect the total clip. This is in device space and + // only for clips applied to the -current layer-. already_AddRefed GetClippedGeometry(IntRect *aClipBounds); already_AddRefed GetInverseClippedGeometry(); + // This gives the device space clip rect applied to the -current layer-. bool GetDeviceSpaceClipRect(D2D1_RECT_F& aClipRect, bool& aIsPixelAligned); void PopAllClips(); @@ -201,7 +221,6 @@ private: mutable RefPtr mDC; RefPtr mBitmap; RefPtr mCommandList; - RefPtr mBlendEffect; RefPtr mSolidColorBrush; @@ -220,7 +239,24 @@ private: }; RefPtr mPath; }; - std::vector mPushedClips; + + // List of pushed layers. + struct PushedLayer + { + PushedLayer() : mClipsArePushed(false), mIsOpaque(false), mOldPermitSubpixelAA(false) {} + + std::vector mPushedClips; + RefPtr mCurrentList; + // True if the current clip stack is pushed to the CurrentTarget(). + bool mClipsArePushed; + bool mIsOpaque; + bool mOldPermitSubpixelAA; + }; + std::vector mPushedLayers; + PushedLayer& CurrentLayer() + { + return mPushedLayers.back(); + } // The latest snapshot of this surface. This needs to be told when this // target is modified. We keep it alive as a cache. @@ -230,8 +266,6 @@ private: // A list of targets which have this object in their mDependentTargets set TargetSet mDependingOnTargets; - // True of the current clip stack is pushed to the main RT. - bool mClipsArePushed; static ID2D1Factory1 *mFactory; static IDWriteFactory *mDWriteFactory; }; diff --git a/gfx/2d/DrawTargetDual.cpp b/gfx/2d/DrawTargetDual.cpp index bdc27897eb..1b302d2129 100644 --- a/gfx/2d/DrawTargetDual.cpp +++ b/gfx/2d/DrawTargetDual.cpp @@ -15,6 +15,11 @@ class DualSurface public: inline explicit DualSurface(SourceSurface *aSurface) { + if (!aSurface) { + mA = mB = nullptr; + return; + } + if (aSurface->GetType() != SurfaceType::DUAL_DT) { mA = mB = aSurface; return; @@ -181,6 +186,16 @@ DrawTargetDual::Mask(const Pattern &aSource, const Pattern &aMask, const DrawOpt mB->Mask(*source.mB, *mask.mB, aOptions); } +void +DrawTargetDual::PushLayer(bool aOpaque, Float aOpacity, SourceSurface* aMask, + const Matrix& aMaskTransform, const IntRect& aBounds, + bool aCopyBackground) +{ + DualSurface mask(aMask); + mA->PushLayer(aOpaque, aOpacity, mask.mA, aMaskTransform, aBounds, aCopyBackground); + mB->PushLayer(aOpaque, aOpacity, mask.mB, aMaskTransform, aBounds, aCopyBackground); +} + already_AddRefed DrawTargetDual::CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const { diff --git a/gfx/2d/DrawTargetDual.h b/gfx/2d/DrawTargetDual.h index bcc06be26e..7cbb8ac576 100644 --- a/gfx/2d/DrawTargetDual.h +++ b/gfx/2d/DrawTargetDual.h @@ -54,6 +54,7 @@ public: FORWARD_FUNCTION1(PushClip, const Path *, aPath) FORWARD_FUNCTION1(PushClipRect, const Rect &, aRect) FORWARD_FUNCTION(PopClip) + FORWARD_FUNCTION(PopLayer) FORWARD_FUNCTION1(ClearRect, const Rect &, aRect) virtual void SetTransform(const Matrix &aTransform) override { @@ -104,7 +105,13 @@ public: const GlyphRenderingOptions *aRenderingOptions) override; virtual void Mask(const Pattern &aSource, const Pattern &aMask, const DrawOptions &aOptions) override; - + + virtual void PushLayer(bool aOpaque, Float aOpacity, + SourceSurface* aMask, + const Matrix& aMaskTransform, + const IntRect& aBounds = IntRect(), + bool aCopyBackground = false) override; + virtual already_AddRefed CreateSourceSurfaceFromData(unsigned char *aData, const IntSize &aSize, @@ -155,6 +162,8 @@ public: { return true; } + + virtual bool IsCurrentGroupOpaque() override { return mA->IsCurrentGroupOpaque(); } private: RefPtr mA; diff --git a/gfx/2d/DrawTargetRecording.cpp b/gfx/2d/DrawTargetRecording.cpp index 8940667b8f..84f3764271 100644 --- a/gfx/2d/DrawTargetRecording.cpp +++ b/gfx/2d/DrawTargetRecording.cpp @@ -558,6 +558,30 @@ DrawTargetRecording::PopClip() mFinalDT->PopClip(); } +void +DrawTargetRecording::PushLayer(bool aOpaque, Float aOpacity, + SourceSurface* aMask, + const Matrix& aMaskTransform, + const IntRect& aBounds, bool aCopyBackground) +{ + if (aMask) { + EnsureSurfaceStored(mRecorder, aMask, "PushLayer"); + } + + mRecorder->RecordEvent(RecordedPushLayer(this, aOpacity, aOpacity, aMask, + aMaskTransform, aBounds, + aCopyBackground)); + mFinalDT->PushLayer(aOpacity, aOpacity, aMask, aMaskTransform, aBounds, + aCopyBackground); +} + +void +DrawTargetRecording::PopLayer() +{ + mRecorder->RecordEvent(RecordedPopLayer(this)); + mFinalDT->PopLayer(); +} + already_AddRefed DrawTargetRecording::CreateSourceSurfaceFromData(unsigned char *aData, const IntSize &aSize, diff --git a/gfx/2d/DrawTargetRecording.h b/gfx/2d/DrawTargetRecording.h index b192a8741f..df30971a55 100644 --- a/gfx/2d/DrawTargetRecording.h +++ b/gfx/2d/DrawTargetRecording.h @@ -217,6 +217,35 @@ public: */ virtual void PopClip() override; + /** + * Push a 'layer' to the DrawTarget, a layer is a temporary surface that all + * drawing will be redirected to, this is used for example to support group + * opacity or the masking of groups. Clips must be balanced within a layer, + * i.e. between a matching PushLayer/PopLayer pair there must be as many + * PushClip(Rect) calls as there are PopClip calls. + * + * @param aOpaque Whether the layer will be opaque + * @param aOpacity Opacity of the layer + * @param aMask Mask applied to the layer + * @param aMaskTransform Transform applied to the layer mask + * @param aBounds Optional bounds in device space to which the layer is + * limited in size. + * @param aCopyBackground Whether to copy the background into the layer, this + * is only supported when aOpaque is true. + */ + virtual void PushLayer(bool aOpaque, Float aOpacity, + SourceSurface* aMask, + const Matrix& aMaskTransform, + const IntRect& aBounds = IntRect(), + bool aCopyBackground = false) override; + + /** + * This balances a call to PushLayer and proceeds to blend the layer back + * onto the background. This blend will blend the temporary surface back + * onto the target in device space using POINT sampling and operator over. + */ + virtual void PopLayer() override; + /* * Create a SourceSurface optimized for use with this DrawTarget from * existing bitmap data in memory. @@ -286,6 +315,10 @@ public: */ virtual void *GetNativeSurface(NativeSurfaceType aType) override { return mFinalDT->GetNativeSurface(aType); } + virtual bool IsCurrentGroupOpaque() override { + return mFinalDT->IsCurrentGroupOpaque(); + } + private: Path *GetPathForPathRecording(const Path *aPath) const; already_AddRefed EnsurePathStored(const Path *aPath); diff --git a/gfx/2d/DrawTargetSkia.cpp b/gfx/2d/DrawTargetSkia.cpp index e4810473b6..5444c3b249 100644 --- a/gfx/2d/DrawTargetSkia.cpp +++ b/gfx/2d/DrawTargetSkia.cpp @@ -75,29 +75,32 @@ public: /** * When constructing a temporary SkBitmap via GetBitmapForSurface, we may also * have to construct a temporary DataSourceSurface, which must live as long as - * the SkBitmap. So we return a pair of the SkBitmap and the (optional) - * temporary surface. + * the SkBitmap. We attach this temporary surface to the bitmap's pixelref, so + * that it can be released once the pixelref is freed. */ -struct TempBitmap +static void +ReleaseTemporarySurface(void* aPixels, void* aContext) { - SkBitmap mBitmap; - RefPtr mTmpSurface; -}; + DataSourceSurface* surf = static_cast(aContext); + if (surf) { + surf->Release(); + } +} -static TempBitmap +static SkBitmap GetBitmapForSurface(SourceSurface* aSurface) { - TempBitmap result; + SkBitmap bitmap; if (aSurface->GetType() == SurfaceType::SKIA) { - result.mBitmap = static_cast(aSurface)->GetBitmap(); - return result; + bitmap = static_cast(aSurface)->GetBitmap(); + return bitmap; } - RefPtr surf = aSurface->GetDataSurface(); + DataSourceSurface* surf = aSurface->GetDataSurface().take(); if (!surf) { - gfxDevCrash(LogReason::SourceSurfaceIncompatible) << "Non-skia SourceSurfaces need to be DataSourceSurfaces"; - return result; + gfxDevCrash(LogReason::SourceSurfaceIncompatible) << "Non-Skia SourceSurfaces need to be DataSourceSurfaces"; + return bitmap; } SkAlphaType alphaType = (surf->GetFormat() == SurfaceFormat::B8G8R8X8) ? @@ -107,11 +110,12 @@ GetBitmapForSurface(SourceSurface* aSurface) surf->GetSize().height, GfxFormatToSkiaColorType(surf->GetFormat()), alphaType); - result.mBitmap.setInfo(info, surf->Stride()); + if (!bitmap.installPixels(info, surf->GetData(), surf->Stride(), nullptr, + ReleaseTemporarySurface, surf)) { + gfxDebug() << "Failed installing pixels on Skia bitmap for temporary surface"; + } - result.mBitmap.setPixels(surf->GetData()); - result.mTmpSurface = surf.forget(); - return result; + return bitmap; } DrawTargetSkia::DrawTargetSkia() @@ -143,8 +147,15 @@ DrawTargetSkia::Snapshot() bool DrawTargetSkia::LockBits(uint8_t** aData, IntSize* aSize, - int32_t* aStride, SurfaceFormat* aFormat) + int32_t* aStride, SurfaceFormat* aFormat, + IntPoint* aOrigin) { + // Ensure the layer is at the origin if required. + SkIPoint origin = mCanvas->getTopDevice()->getOrigin(); + if (!aOrigin && !origin.isZero()) { + return false; + } + /* Test if the canvas' device has accessible pixels first, as actually * accessing the pixels may trigger side-effects, even if it fails. */ @@ -165,6 +176,9 @@ DrawTargetSkia::LockBits(uint8_t** aData, IntSize* aSize, *aSize = IntSize(info.width(), info.height()); *aStride = int32_t(rowBytes); *aFormat = SkiaColorTypeToGfxFormat(info.colorType()); + if (aOrigin) { + *aOrigin = IntPoint(origin.x(), origin.y()); + } return true; } @@ -174,8 +188,7 @@ DrawTargetSkia::ReleaseBits(uint8_t* aData) } static void -SetPaintPattern(SkPaint& aPaint, const Pattern& aPattern, TempBitmap& aTmpBitmap, - Float aAlpha = 1.0) +SetPaintPattern(SkPaint& aPaint, const Pattern& aPattern, Float aAlpha = 1.0) { switch (aPattern.GetType()) { case PatternType::COLOR: { @@ -193,22 +206,16 @@ SetPaintPattern(SkPaint& aPaint, const Pattern& aPattern, TempBitmap& aTmpBitmap points[0] = SkPoint::Make(SkFloatToScalar(pat.mBegin.x), SkFloatToScalar(pat.mBegin.y)); points[1] = SkPoint::Make(SkFloatToScalar(pat.mEnd.x), SkFloatToScalar(pat.mEnd.y)); + SkMatrix mat; + GfxMatrixToSkiaMatrix(pat.mMatrix, mat); SkShader* shader = SkGradientShader::CreateLinear(points, &stops->mColors.front(), &stops->mPositions.front(), stops->mCount, - mode); - - if (shader) { - SkMatrix mat; - GfxMatrixToSkiaMatrix(pat.mMatrix, mat); - SkShader* matrixShader = SkShader::CreateLocalMatrixShader(shader, mat); - SkSafeUnref(shader); - SkSafeUnref(aPaint.setShader(matrixShader)); - } - + mode, 0, &mat); + SkSafeUnref(aPaint.setShader(shader)); } else { - aPaint.setColor(SkColorSetARGB(0, 0, 0, 0)); + aPaint.setColor(SK_ColorTRANSPARENT); } break; } @@ -222,6 +229,8 @@ SetPaintPattern(SkPaint& aPaint, const Pattern& aPattern, TempBitmap& aTmpBitmap points[0] = SkPoint::Make(SkFloatToScalar(pat.mCenter1.x), SkFloatToScalar(pat.mCenter1.y)); points[1] = SkPoint::Make(SkFloatToScalar(pat.mCenter2.x), SkFloatToScalar(pat.mCenter2.y)); + SkMatrix mat; + GfxMatrixToSkiaMatrix(pat.mMatrix, mat); SkShader* shader = SkGradientShader::CreateTwoPointConical(points[0], SkFloatToScalar(pat.mRadius1), points[1], @@ -229,24 +238,16 @@ SetPaintPattern(SkPaint& aPaint, const Pattern& aPattern, TempBitmap& aTmpBitmap &stops->mColors.front(), &stops->mPositions.front(), stops->mCount, - mode); - if (shader) { - SkMatrix mat; - GfxMatrixToSkiaMatrix(pat.mMatrix, mat); - SkShader* matrixShader = SkShader::CreateLocalMatrixShader(shader, mat); - SkSafeUnref(shader); - SkSafeUnref(aPaint.setShader(matrixShader)); - } - + mode, 0, &mat); + SkSafeUnref(aPaint.setShader(shader)); } else { - aPaint.setColor(SkColorSetARGB(0, 0, 0, 0)); + aPaint.setColor(SK_ColorTRANSPARENT); } break; } case PatternType::SURFACE: { const SurfacePattern& pat = static_cast(aPattern); - aTmpBitmap = GetBitmapForSurface(pat.mSurface); - SkBitmap& bitmap = aTmpBitmap.mBitmap; + SkBitmap bitmap = GetBitmapForSurface(pat.mSurface); SkMatrix mat; GfxMatrixToSkiaMatrix(pat.mMatrix, mat); @@ -260,10 +261,8 @@ SetPaintPattern(SkPaint& aPaint, const Pattern& aPattern, TempBitmap& aTmpBitmap SkShader::TileMode xTileMode = ExtendModeToTileMode(pat.mExtendMode, Axis::X_AXIS); SkShader::TileMode yTileMode = ExtendModeToTileMode(pat.mExtendMode, Axis::Y_AXIS); - SkShader* shader = SkShader::CreateBitmapShader(bitmap, xTileMode, yTileMode); - SkShader* matrixShader = SkShader::CreateLocalMatrixShader(shader, mat); - SkSafeUnref(shader); - SkSafeUnref(aPaint.setShader(matrixShader)); + SkShader* shader = SkShader::CreateBitmapShader(bitmap, xTileMode, yTileMode, &mat); + SkSafeUnref(aPaint.setShader(shader)); if (pat.mFilter == Filter::POINT) { aPaint.setFilterQuality(kNone_SkFilterQuality); } @@ -285,7 +284,7 @@ struct AutoPaintSetup { : mNeedsRestore(false), mAlpha(1.0) { Init(aCanvas, aOptions, aMaskBounds); - SetPaintPattern(mPaint, aPattern, mTmpBitmap, mAlpha); + SetPaintPattern(mPaint, aPattern, mAlpha); } AutoPaintSetup(SkCanvas *aCanvas, const DrawOptions& aOptions, const Rect* aMaskBounds = nullptr) @@ -337,7 +336,6 @@ struct AutoPaintSetup { // TODO: Maybe add an operator overload to access this easier? SkPaint mPaint; - TempBitmap mTmpBitmap; bool mNeedsRestore; SkCanvas* mCanvas; Float mAlpha; @@ -376,14 +374,14 @@ DrawTargetSkia::DrawSurface(SourceSurface *aSurface, SkRect destRect = RectToSkRect(aDest); SkRect sourceRect = RectToSkRect(aSource); - TempBitmap bitmap = GetBitmapForSurface(aSurface); + SkBitmap bitmap = GetBitmapForSurface(aSurface); AutoPaintSetup paint(mCanvas.get(), aOptions, &aDest); if (aSurfOptions.mFilter == Filter::POINT) { paint.mPaint.setFilterQuality(kNone_SkFilterQuality); } - mCanvas->drawBitmapRect(bitmap.mBitmap, sourceRect, destRect, &paint.mPaint); + mCanvas->drawBitmapRect(bitmap, sourceRect, destRect, &paint.mPaint); } DrawTargetType @@ -424,7 +422,7 @@ DrawTargetSkia::DrawSurfaceWithShadow(SourceSurface *aSurface, mCanvas->save(); mCanvas->resetMatrix(); - TempBitmap bitmap = GetBitmapForSurface(aSurface); + SkBitmap bitmap = GetBitmapForSurface(aSurface); SkPaint paint; paint.setXfermodeMode(GfxOpToSkiaOp(aOperator)); @@ -446,17 +444,12 @@ DrawTargetSkia::DrawSurfaceWithShadow(SourceSurface *aSurface, shadowPaint.setImageFilter(blurFilter.get()); shadowPaint.setColorFilter(colorFilter.get()); - // drawBitmap implicitly calls saveLayer with a src-over xfer mode if given - // an image filter, whereas the supplied xfer mode gets used to render into - // the layer, which is the wrong order. We instead must use drawSprite which - // applies the image filter directly to the bitmap without rendering it first, - // then uses the xfer mode to composite it. IntPoint shadowDest = RoundedToInt(aDest + aOffset); - mCanvas->drawSprite(bitmap.mBitmap, shadowDest.x, shadowDest.y, &shadowPaint); + mCanvas->drawBitmap(bitmap, shadowDest.x, shadowDest.y, &shadowPaint); // Composite the original image after the shadow IntPoint dest = RoundedToInt(aDest); - mCanvas->drawSprite(bitmap.mBitmap, dest.x, dest.y, &paint); + mCanvas->drawBitmap(bitmap, dest.x, dest.y, &paint); mCanvas->restore(); } @@ -636,15 +629,14 @@ DrawTargetSkia::Mask(const Pattern &aSource, AutoPaintSetup paint(mCanvas.get(), aOptions, aSource); SkPaint maskPaint; - TempBitmap tmpBitmap; - SetPaintPattern(maskPaint, aMask, tmpBitmap); + SetPaintPattern(maskPaint, aMask); SkLayerRasterizer::Builder builder; builder.addLayer(maskPaint); SkAutoTUnref raster(builder.detachRasterizer()); paint.mPaint.setRasterizer(raster.get()); - mCanvas->drawRect(SkRectCoveringWholeSurface(), paint.mPaint); + mCanvas->drawPaint(paint.mPaint); } void @@ -656,32 +648,21 @@ DrawTargetSkia::MaskSurface(const Pattern &aSource, MarkChanged(); AutoPaintSetup paint(mCanvas.get(), aOptions, aSource); - TempBitmap bitmap = GetBitmapForSurface(aMask); - if (bitmap.mBitmap.colorType() == kAlpha_8_SkColorType) { - if (aOffset != Point(0, 0)) { - SkMatrix transform; - transform.setTranslate(SkFloatToScalar(-aOffset.x), SkFloatToScalar(-aOffset.y)); - SkShader* matrixShader = SkShader::CreateLocalMatrixShader(paint.mPaint.getShader(), transform); - SkSafeUnref(paint.mPaint.setShader(matrixShader)); - } - - mCanvas->drawBitmap(bitmap.mBitmap, aOffset.x, aOffset.y, &paint.mPaint); - } else { - SkPaint maskPaint; - TempBitmap tmpBitmap; - SetPaintPattern(maskPaint, - SurfacePattern(aMask, ExtendMode::CLAMP, Matrix::Translation(aOffset)), - tmpBitmap); - - SkLayerRasterizer::Builder builder; - builder.addLayer(maskPaint); - SkAutoTUnref raster(builder.detachRasterizer()); - paint.mPaint.setRasterizer(raster.get()); - - IntSize size = aMask->GetSize(); - Rect rect = Rect(aOffset.x, aOffset.y, size.width, size.height); - mCanvas->drawRect(RectToSkRect(rect), paint.mPaint); + SkBitmap bitmap = GetBitmapForSurface(aMask); + if (bitmap.colorType() != kAlpha_8_SkColorType && + !bitmap.extractAlpha(&bitmap)) { + gfxDebug() << *this << ": MaskSurface() failed to extract alpha for mask"; + return; } + + if (aOffset != Point(0, 0)) { + SkMatrix transform; + transform.setTranslate(PointToSkPoint(-aOffset)); + SkShader* matrixShader = paint.mPaint.getShader()->newWithLocalMatrix(transform); + SkSafeUnref(paint.mPaint.setShader(matrixShader)); + } + + mCanvas->drawBitmap(bitmap, aOffset.x, aOffset.y, &paint.mPaint); } already_AddRefed @@ -782,54 +763,32 @@ DrawTargetSkia::CopySurface(SourceSurface *aSurface, const IntRect& aSourceRect, const IntPoint &aDestination) { - //TODO: We could just use writePixels() here if the sourceRect is the entire source - if (aSurface->GetType() != SurfaceType::SKIA && aSurface->GetType() != SurfaceType::DATA) { return; } MarkChanged(); - TempBitmap bitmap = GetBitmapForSurface(aSurface); - - // This is a fast path that is disabled for now to mimimize risk - if (false && !bitmap.mBitmap.getTexture() && mCanvas->imageInfo() == bitmap.mBitmap.info()) { - SkBitmap bm(bitmap.mBitmap); - bm.lockPixels(); - if (bm.getPixels()) { - SkImageInfo info = bm.info().makeWH(aSourceRect.width, aSourceRect.height); - uint8_t* pixels = static_cast(bm.getPixels()); - // adjust pixels for the source offset - pixels += aSourceRect.x + aSourceRect.y*bm.rowBytes(); - mCanvas->writePixels(info, pixels, bm.rowBytes(), aDestination.x, aDestination.y); - return; - } - } + SkBitmap bitmap = GetBitmapForSurface(aSurface); mCanvas->save(); mCanvas->resetMatrix(); SkRect dest = IntRectToSkRect(IntRect(aDestination.x, aDestination.y, aSourceRect.width, aSourceRect.height)); SkIRect source = IntRectToSkIRect(aSourceRect); mCanvas->clipRect(dest, SkRegion::kReplace_Op); - SkPaint paint; - if (mCanvas->imageInfo().colorType() == kRGB_565_SkColorType) { - // Set the xfermode to SOURCE_OVER to workaround + SkPaint paint; + if (!bitmap.isOpaque()) { + // Keep the xfermode as SOURCE_OVER for opaque bitmaps // http://code.google.com/p/skia/issues/detail?id=628 - // RGB565 is opaque so they're equivalent anyway - paint.setXfermodeMode(SkXfermode::kSrcOver_Mode); - } else { paint.setXfermodeMode(SkXfermode::kSrc_Mode); } // drawBitmapRect with A8 bitmaps ends up doing a mask operation // so we need to clear before - if (bitmap.mBitmap.colorType() == kAlpha_8_SkColorType) { - SkPaint clearPaint; - clearPaint.setColor(SkColorSetARGB(0, 0, 0, 0)); - clearPaint.setXfermodeMode(SkXfermode::kSrc_Mode); - mCanvas->drawPaint(clearPaint); + if (bitmap.colorType() == kAlpha_8_SkColorType) { + mCanvas->clear(SK_ColorTRANSPARENT); } - mCanvas->drawBitmapRect(bitmap.mBitmap, source, dest, &paint); + mCanvas->drawBitmapRect(bitmap, source, dest, &paint); mCanvas->restore(); } @@ -856,7 +815,7 @@ DrawTargetSkia::Init(const IntSize &aSize, SurfaceFormat aFormat) return false; } - bitmap.eraseARGB(0, 0, 0, 0); + bitmap.eraseColor(SK_ColorTRANSPARENT); mCanvas.adopt(new SkCanvas(bitmap)); mSize = aSize; @@ -964,12 +923,9 @@ void DrawTargetSkia::ClearRect(const Rect &aRect) { MarkChanged(); - SkPaint paint; mCanvas->save(); mCanvas->clipRect(RectToSkRect(aRect), SkRegion::kIntersect_Op, true); - paint.setColor(SkColorSetARGB(0, 0, 0, 0)); - paint.setXfermodeMode(SkXfermode::kSrc_Mode); - mCanvas->drawPaint(paint); + mCanvas->clear(SK_ColorTRANSPARENT); mCanvas->restore(); } @@ -1000,6 +956,135 @@ DrawTargetSkia::PopClip() mCanvas->restore(); } +// Image filter that just passes the source through to the result unmodified. +class CopyLayerImageFilter : public SkImageFilter +{ +public: + CopyLayerImageFilter() + : SkImageFilter(0, nullptr) + {} + + virtual bool onFilterImage(Proxy*, const SkBitmap& src, const Context&, + SkBitmap* result, SkIPoint* offset) const override { + *result = src; + offset->set(0, 0); + return true; + } + + SK_TO_STRING_OVERRIDE() + SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(CopyLayerImageFilter) +}; + +SkFlattenable* +CopyLayerImageFilter::CreateProc(SkReadBuffer& buffer) +{ + SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 0); + return new CopyLayerImageFilter; +} + +#ifndef SK_IGNORE_TO_STRING +void +CopyLayerImageFilter::toString(SkString* str) const +{ + str->append("CopyLayerImageFilter: ()"); +} +#endif + +void +DrawTargetSkia::PushLayer(bool aOpaque, Float aOpacity, SourceSurface* aMask, + const Matrix& aMaskTransform, const IntRect& aBounds, + bool aCopyBackground) +{ + PushedLayer layer(GetPermitSubpixelAA(), aOpaque, aOpacity, aMask, aMaskTransform); + mPushedLayers.push_back(layer); + + SkPaint paint; + + // If we have a mask, set the opacity to 0 so that SkCanvas::restore skips + // implicitly drawing the layer so that we can properly mask it in PopLayer. + paint.setAlpha(aMask ? 0 : ColorFloatToByte(aOpacity)); + + SkRect bounds = IntRectToSkRect(aBounds); + + SkAutoTUnref backdrop(aCopyBackground ? new CopyLayerImageFilter : nullptr); + + SkCanvas::SaveLayerRec saveRec(aBounds.IsEmpty() ? nullptr : &bounds, + &paint, + backdrop.get(), + aOpaque ? SkCanvas::kIsOpaque_SaveLayerFlag : 0); + + mCanvas->saveLayer(saveRec); + + SetPermitSubpixelAA(aOpaque); +} + +void +DrawTargetSkia::PopLayer() +{ + MarkChanged(); + + MOZ_ASSERT(mPushedLayers.size()); + const PushedLayer& layer = mPushedLayers.back(); + + if (layer.mMask) { + // If we have a mask, take a reference to the layer's bitmap device so that + // we can mask it ourselves. This assumes we forced SkCanvas::restore to + // skip implicitly drawing the layer. + SkAutoTUnref layerDevice(SkSafeRef(mCanvas->getTopDevice())); + SkIRect layerBounds = layerDevice->getGlobalBounds(); + SkBitmap layerBitmap = layerDevice->accessBitmap(false); + + // Restore the background with the layer's device left alive. + mCanvas->restore(); + + SkPaint paint; + paint.setAlpha(ColorFloatToByte(layer.mOpacity)); + + SkMatrix maskMat, layerMat; + // Get the total transform affecting the mask, considering its pattern + // transform and the current canvas transform. + GfxMatrixToSkiaMatrix(layer.mMaskTransform, maskMat); + maskMat.postConcat(mCanvas->getTotalMatrix()); + if (!maskMat.invert(&layerMat)) { + gfxDebug() << *this << ": PopLayer() failed to invert mask transform"; + } else { + // The layer should not be affected by the current canvas transform, + // even though the mask is. So first we use the inverse of the transform + // affecting the mask, then add back on the layer's origin. + layerMat.preTranslate(layerBounds.x(), layerBounds.y()); + SkShader* shader = SkShader::CreateBitmapShader(layerBitmap, + SkShader::kClamp_TileMode, + SkShader::kClamp_TileMode, + &layerMat); + SkSafeUnref(paint.setShader(shader)); + + SkBitmap mask = GetBitmapForSurface(layer.mMask); + if (mask.colorType() != kAlpha_8_SkColorType && + !mask.extractAlpha(&mask)) { + gfxDebug() << *this << ": PopLayer() failed to extract alpha for mask"; + } else { + mCanvas->save(); + + // The layer may be smaller than the canvas size, so make sure drawing is + // clipped to within the bounds of the layer. + mCanvas->resetMatrix(); + mCanvas->clipRect(SkRect::Make(layerBounds)); + + mCanvas->setMatrix(maskMat); + mCanvas->drawBitmap(mask, 0, 0, &paint); + + mCanvas->restore(); + } + } + } else { + mCanvas->restore(); + } + + SetPermitSubpixelAA(layer.mOldPermitSubpixelAA); + + mPushedLayers.pop_back(); +} + already_AddRefed DrawTargetSkia::CreateGradientStops(GradientStop *aStops, uint32_t aNumStops, ExtendMode aExtendMode) const { @@ -1028,14 +1113,6 @@ DrawTargetSkia::MarkChanged() } } -// Return a rect (in user space) that covers the entire surface by applying -// the inverse of GetTransform() to (0, 0, mSize.width, mSize.height). -SkRect -DrawTargetSkia::SkRectCoveringWholeSurface() const -{ - return RectToSkRect(mTransform.TransformBounds(Rect(0, 0, mSize.width, mSize.height))); -} - void DrawTargetSkia::SnapshotDestroyed() { diff --git a/gfx/2d/DrawTargetSkia.h b/gfx/2d/DrawTargetSkia.h index e28a20411b..a9c1aff931 100644 --- a/gfx/2d/DrawTargetSkia.h +++ b/gfx/2d/DrawTargetSkia.h @@ -37,7 +37,8 @@ public: virtual already_AddRefed Snapshot() override; virtual IntSize GetSize() override { return mSize; } virtual bool LockBits(uint8_t** aData, IntSize* aSize, - int32_t* aStride, SurfaceFormat* aFormat) override; + int32_t* aStride, SurfaceFormat* aFormat, + IntPoint* aOrigin = nullptr) override; virtual void ReleaseBits(uint8_t* aData) override; virtual void Flush() override; virtual void DrawSurface(SourceSurface *aSurface, @@ -93,6 +94,12 @@ public: virtual void PushClip(const Path *aPath) override; virtual void PushClipRect(const Rect& aRect) override; virtual void PopClip() override; + virtual void PushLayer(bool aOpaque, Float aOpacity, + SourceSurface* aMask, + const Matrix& aMaskTransform, + const IntRect& aBounds = IntRect(), + bool aCopyBackground = false) override; + virtual void PopLayer() override; virtual already_AddRefed CreateSourceSurfaceFromData(unsigned char *aData, const IntSize &aSize, int32_t aStride, @@ -136,10 +143,29 @@ private: bool ShouldLCDRenderText(FontType aFontType, AntialiasMode aAntialiasMode); - SkRect SkRectCoveringWholeSurface() const; - bool UsingSkiaGPU() const; + struct PushedLayer + { + PushedLayer(bool aOldPermitSubpixelAA, + bool aOpaque, + Float aOpacity, + SourceSurface* aMask, + const Matrix& aMaskTransform) + : mOldPermitSubpixelAA(aOldPermitSubpixelAA), + mOpaque(aOpaque), + mOpacity(aOpacity), + mMask(aMask), + mMaskTransform(aMaskTransform) + {} + bool mOldPermitSubpixelAA; + bool mOpaque; + Float mOpacity; + RefPtr mMask; + Matrix mMaskTransform; + }; + std::vector mPushedLayers; + #ifdef USE_SKIA_GPU RefPtrSkia mGrContext; GrGLuint mTexture; diff --git a/gfx/2d/DrawTargetTiled.cpp b/gfx/2d/DrawTargetTiled.cpp index d969b00d30..bcdfc71f20 100644 --- a/gfx/2d/DrawTargetTiled.cpp +++ b/gfx/2d/DrawTargetTiled.cpp @@ -305,5 +305,29 @@ DrawTargetTiled::Fill(const Path* aPath, const Pattern& aPattern, const DrawOpti } } +void +DrawTargetTiled::PushLayer(bool aOpaque, Float aOpacity, SourceSurface* aMask, + const Matrix& aMaskTransform, const IntRect& aBounds, + bool aCopyBackground) +{ + // XXX - not sure this is what we want or whether we want to continue drawing to a larger + // intermediate surface, that would require tweaking the code in here a little though. + for (size_t i = 0; i < mTiles.size(); i++) { + IntRect bounds = aBounds; + bounds.MoveBy(-mTiles[i].mTileOrigin); + mTiles[i].mDrawTarget->PushLayer(aOpaque, aOpacity, aMask, aMaskTransform, aBounds); + } +} + +void +DrawTargetTiled::PopLayer() +{ + // XXX - not sure this is what we want or whether we want to continue drawing to a larger + // intermediate surface, that would require tweaking the code in here a little though. + for (size_t i = 0; i < mTiles.size(); i++) { + mTiles[i].mDrawTarget->PopLayer(); + } +} + } // namespace gfx } // namespace mozilla diff --git a/gfx/2d/DrawTargetTiled.h b/gfx/2d/DrawTargetTiled.h index 1e4534e74d..0ebe2f1d4c 100644 --- a/gfx/2d/DrawTargetTiled.h +++ b/gfx/2d/DrawTargetTiled.h @@ -103,6 +103,13 @@ public: virtual void PushClip(const Path *aPath) override; virtual void PushClipRect(const Rect &aRect) override; virtual void PopClip() override; + virtual void PushLayer(bool aOpaque, Float aOpacity, + SourceSurface* aMask, + const Matrix& aMaskTransform, + const IntRect& aBounds = IntRect(), + bool aCopyBackground = false) override; + virtual void PopLayer() override; + virtual void SetTransform(const Matrix &aTransform) override; diff --git a/gfx/2d/HelpersD2D.h b/gfx/2d/HelpersD2D.h index 7100ad127f..bde54fc650 100644 --- a/gfx/2d/HelpersD2D.h +++ b/gfx/2d/HelpersD2D.h @@ -696,6 +696,267 @@ CreatePartialBitmapForSurface(DataSourceSurface *aSurface, const Matrix &aDestin } } +static inline void AddRectToSink(ID2D1GeometrySink* aSink, const D2D1_RECT_F& aRect) +{ + aSink->BeginFigure(D2D1::Point2F(aRect.left, aRect.top), D2D1_FIGURE_BEGIN_FILLED); + aSink->AddLine(D2D1::Point2F(aRect.right, aRect.top)); + aSink->AddLine(D2D1::Point2F(aRect.right, aRect.bottom)); + aSink->AddLine(D2D1::Point2F(aRect.left, aRect.bottom)); + aSink->EndFigure(D2D1_FIGURE_END_CLOSED); +} + +class DCCommandSink : public ID2D1CommandSink +{ +public: + DCCommandSink(ID2D1DeviceContext* aCtx) : mCtx(aCtx) + { + } + + HRESULT STDMETHODCALLTYPE QueryInterface(const IID &aIID, void **aPtr) + { + if (!aPtr) { + return E_POINTER; + } + + if (aIID == IID_IUnknown) { + *aPtr = static_cast(this); + return S_OK; + } else if (aIID == IID_ID2D1CommandSink) { + *aPtr = static_cast(this); + return S_OK; + } + + return E_NOINTERFACE; + } + + ULONG STDMETHODCALLTYPE AddRef() + { + return 1; + } + + ULONG STDMETHODCALLTYPE Release() + { + return 1; + } + + STDMETHODIMP BeginDraw() + { + // We don't want to do anything here! + return S_OK; + } + STDMETHODIMP EndDraw() + { + // We don't want to do anything here! + return S_OK; + } + + STDMETHODIMP SetAntialiasMode( + D2D1_ANTIALIAS_MODE antialiasMode + ) + { + mCtx->SetAntialiasMode(antialiasMode); + return S_OK; + } + + STDMETHODIMP SetTags(D2D1_TAG tag1, D2D1_TAG tag2) + { + mCtx->SetTags(tag1, tag2); + return S_OK; + } + + STDMETHODIMP SetTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE textAntialiasMode) + { + mCtx->SetTextAntialiasMode(textAntialiasMode); + return S_OK; + } + + STDMETHODIMP SetTextRenderingParams(_In_opt_ IDWriteRenderingParams *textRenderingParams) + { + mCtx->SetTextRenderingParams(textRenderingParams); + return S_OK; + } + + STDMETHODIMP SetTransform(_In_ CONST D2D1_MATRIX_3X2_F *transform) + { + mCtx->SetTransform(transform); + return S_OK; + } + + STDMETHODIMP SetPrimitiveBlend(D2D1_PRIMITIVE_BLEND primitiveBlend) + { + mCtx->SetPrimitiveBlend(primitiveBlend); + return S_OK; + } + + STDMETHODIMP SetUnitMode(D2D1_UNIT_MODE unitMode) + { + mCtx->SetUnitMode(unitMode); + return S_OK; + } + + STDMETHODIMP Clear(_In_opt_ CONST D2D1_COLOR_F *color) + { + mCtx->Clear(color); + return S_OK; + } + + STDMETHODIMP DrawGlyphRun( + D2D1_POINT_2F baselineOrigin, + _In_ CONST DWRITE_GLYPH_RUN *glyphRun, + _In_opt_ CONST DWRITE_GLYPH_RUN_DESCRIPTION *glyphRunDescription, + _In_ ID2D1Brush *foregroundBrush, + DWRITE_MEASURING_MODE measuringMode + ) + { + mCtx->DrawGlyphRun(baselineOrigin, glyphRun, glyphRunDescription, + foregroundBrush, measuringMode); + return S_OK; + } + + STDMETHODIMP DrawLine( + D2D1_POINT_2F point0, + D2D1_POINT_2F point1, + _In_ ID2D1Brush *brush, + FLOAT strokeWidth, + _In_opt_ ID2D1StrokeStyle *strokeStyle + ) + { + mCtx->DrawLine(point0, point1, brush, strokeWidth, strokeStyle); + return S_OK; + } + + STDMETHODIMP DrawGeometry( + _In_ ID2D1Geometry *geometry, + _In_ ID2D1Brush *brush, + FLOAT strokeWidth, + _In_opt_ ID2D1StrokeStyle *strokeStyle + ) + { + mCtx->DrawGeometry(geometry, brush, strokeWidth, strokeStyle); + return S_OK; + } + + STDMETHODIMP DrawRectangle( + _In_ CONST D2D1_RECT_F *rect, + _In_ ID2D1Brush *brush, + FLOAT strokeWidth, + _In_opt_ ID2D1StrokeStyle *strokeStyle + ) + { + mCtx->DrawRectangle(rect, brush, strokeWidth, strokeStyle); + return S_OK; + } + + STDMETHODIMP DrawBitmap( + _In_ ID2D1Bitmap *bitmap, + _In_opt_ CONST D2D1_RECT_F *destinationRectangle, + FLOAT opacity, + D2D1_INTERPOLATION_MODE interpolationMode, + _In_opt_ CONST D2D1_RECT_F *sourceRectangle, + _In_opt_ CONST D2D1_MATRIX_4X4_F *perspectiveTransform + ) + { + mCtx->DrawBitmap(bitmap, destinationRectangle, opacity, + interpolationMode, sourceRectangle, + perspectiveTransform); + return S_OK; + } + + STDMETHODIMP DrawImage( + _In_ ID2D1Image *image, + _In_opt_ CONST D2D1_POINT_2F *targetOffset, + _In_opt_ CONST D2D1_RECT_F *imageRectangle, + D2D1_INTERPOLATION_MODE interpolationMode, + D2D1_COMPOSITE_MODE compositeMode + ) + { + mCtx->DrawImage(image, targetOffset, imageRectangle, + interpolationMode, compositeMode); + return S_OK; + } + + STDMETHODIMP DrawGdiMetafile( + _In_ ID2D1GdiMetafile *gdiMetafile, + _In_opt_ CONST D2D1_POINT_2F *targetOffset + ) + { + mCtx->DrawGdiMetafile(gdiMetafile, targetOffset); + return S_OK; + } + + STDMETHODIMP FillMesh( + _In_ ID2D1Mesh *mesh, + _In_ ID2D1Brush *brush + ) + { + mCtx->FillMesh(mesh, brush); + return S_OK; + } + + STDMETHODIMP FillOpacityMask( + _In_ ID2D1Bitmap *opacityMask, + _In_ ID2D1Brush *brush, + _In_opt_ CONST D2D1_RECT_F *destinationRectangle, + _In_opt_ CONST D2D1_RECT_F *sourceRectangle + ) + { + mCtx->FillOpacityMask(opacityMask, brush, destinationRectangle, + sourceRectangle); + return S_OK; + } + + STDMETHODIMP FillGeometry( + _In_ ID2D1Geometry *geometry, + _In_ ID2D1Brush *brush, + _In_opt_ ID2D1Brush *opacityBrush + ) + { + mCtx->FillGeometry(geometry, brush, opacityBrush); + return S_OK; + } + + STDMETHODIMP FillRectangle( + _In_ CONST D2D1_RECT_F *rect, + _In_ ID2D1Brush *brush + ) + { + mCtx->FillRectangle(rect, brush); + return S_OK; + } + + STDMETHODIMP PushAxisAlignedClip( + _In_ CONST D2D1_RECT_F *clipRect, + D2D1_ANTIALIAS_MODE antialiasMode + ) + { + mCtx->PushAxisAlignedClip(clipRect, antialiasMode); + return S_OK; + } + + STDMETHODIMP PushLayer( + _In_ CONST D2D1_LAYER_PARAMETERS1 *layerParameters1, + _In_opt_ ID2D1Layer *layer + ) + { + mCtx->PushLayer(layerParameters1, layer); + return S_OK; + } + + STDMETHODIMP PopAxisAlignedClip() + { + mCtx->PopAxisAlignedClip(); + return S_OK; + } + + STDMETHODIMP PopLayer() + { + mCtx->PopLayer(); + return S_OK; + } + + ID2D1DeviceContext* mCtx; +}; + } } diff --git a/gfx/2d/HelpersSkia.h b/gfx/2d/HelpersSkia.h index 252860825c..92cb9b4f03 100644 --- a/gfx/2d/HelpersSkia.h +++ b/gfx/2d/HelpersSkia.h @@ -156,9 +156,9 @@ StrokeOptionsToPaint(SkPaint& aPaint, const StrokeOptions &aOptions) pattern[i] = SkScalarMulDiv(SK_ScalarNearlyZero, 33, 32); } - SkDashPathEffect* dash = SkDashPathEffect::Create(&pattern.front(), - dashCount, - SkFloatToScalar(aOptions.mDashOffset)); + SkPathEffect* dash = SkDashPathEffect::Create(&pattern.front(), + dashCount, + SkFloatToScalar(aOptions.mDashOffset)); SkSafeUnref(aPaint.setPathEffect(dash)); } diff --git a/gfx/2d/Preferences.cpp b/gfx/2d/Preferences.cpp index 87a2a496ef..73904cbc7c 100644 --- a/gfx/2d/Preferences.cpp +++ b/gfx/2d/Preferences.cpp @@ -31,7 +31,9 @@ int32_t PreferenceAccess::RegisterLivePref(const char* aName, int32_t* aVar, int32_t aDefault) { - Int32Prefs().append(Int32Pref{ aName, aVar }); + if (!Int32Prefs().append(Int32Pref{ aName, aVar })) { + MOZ_CRASH(); + } return aDefault; } @@ -57,4 +59,4 @@ PreferenceAccess::SetAccess(PreferenceAccess* aAccess) } } // namespace gfx -} // namespace mozilla \ No newline at end of file +} // namespace mozilla diff --git a/gfx/2d/RecordedEvent.cpp b/gfx/2d/RecordedEvent.cpp index c386453d83..b0e5d0853a 100644 --- a/gfx/2d/RecordedEvent.cpp +++ b/gfx/2d/RecordedEvent.cpp @@ -80,6 +80,8 @@ RecordedEvent::LoadEventFromStream(std::istream &aStream, EventType aType) LOAD_EVENT_TYPE(FILTERNODESETINPUT, RecordedFilterNodeSetInput); LOAD_EVENT_TYPE(CREATESIMILARDRAWTARGET, RecordedCreateSimilarDrawTarget); LOAD_EVENT_TYPE(FONTDATA, RecordedFontData); + LOAD_EVENT_TYPE(PUSHLAYER, RecordedPushLayer); + LOAD_EVENT_TYPE(POPLAYER, RecordedPopLayer); default: return nullptr; } @@ -157,6 +159,10 @@ RecordedEvent::GetEventName(EventType aType) return "CreateSimilarDrawTarget"; case FONTDATA: return "FontData"; + case PUSHLAYER: + return "PushLayer"; + case POPLAYER: + return "PopLayer"; default: return "Unknown"; } @@ -927,6 +933,68 @@ RecordedPopClip::OutputSimpleEventInfo(stringstream &aStringStream) const aStringStream << "[" << mDT << "] PopClip"; } +void +RecordedPushLayer::PlayEvent(Translator *aTranslator) const +{ + SourceSurface* mask = mMask ? aTranslator->LookupSourceSurface(mMask) + : nullptr; + aTranslator->LookupDrawTarget(mDT)-> + PushLayer(mOpaque, mOpacity, mask, mMaskTransform, mBounds, mCopyBackground); +} + +void +RecordedPushLayer::RecordToStream(ostream &aStream) const +{ + RecordedDrawingEvent::RecordToStream(aStream); + WriteElement(aStream, mOpaque); + WriteElement(aStream, mOpacity); + WriteElement(aStream, mMask); + WriteElement(aStream, mMaskTransform); + WriteElement(aStream, mBounds); + WriteElement(aStream, mCopyBackground); +} + +RecordedPushLayer::RecordedPushLayer(istream &aStream) + : RecordedDrawingEvent(PUSHLAYER, aStream) +{ + ReadElement(aStream, mOpaque); + ReadElement(aStream, mOpacity); + ReadElement(aStream, mMask); + ReadElement(aStream, mMaskTransform); + ReadElement(aStream, mBounds); + ReadElement(aStream, mCopyBackground); +} + +void +RecordedPushLayer::OutputSimpleEventInfo(stringstream &aStringStream) const +{ + aStringStream << "[" << mDT << "] PushPLayer (Opaque=" << mOpaque << + ", Opacity=" << mOpacity << ", Mask Ref=" << mMask << ") "; +} + +void +RecordedPopLayer::PlayEvent(Translator *aTranslator) const +{ + aTranslator->LookupDrawTarget(mDT)->PopLayer(); +} + +void +RecordedPopLayer::RecordToStream(ostream &aStream) const +{ + RecordedDrawingEvent::RecordToStream(aStream); +} + +RecordedPopLayer::RecordedPopLayer(istream &aStream) + : RecordedDrawingEvent(POPLAYER, aStream) +{ +} + +void +RecordedPopLayer::OutputSimpleEventInfo(stringstream &aStringStream) const +{ + aStringStream << "[" << mDT << "] PopLayer"; +} + void RecordedSetTransform::PlayEvent(Translator *aTranslator) const { diff --git a/gfx/2d/RecordedEvent.h b/gfx/2d/RecordedEvent.h index da94124a15..390ad02d50 100644 --- a/gfx/2d/RecordedEvent.h +++ b/gfx/2d/RecordedEvent.h @@ -27,7 +27,7 @@ const uint32_t kMagicInt = 0xc001feed; const uint16_t kMajorRevision = 4; // A change in minor revision means additions of new events. New streams will // not play in older players. -const uint16_t kMinorRevision = 0; +const uint16_t kMinorRevision = 1; struct ReferencePtr { @@ -192,6 +192,8 @@ public: FILTERNODESETINPUT, CREATESIMILARDRAWTARGET, FONTDATA, + PUSHLAYER, + POPLAYER, }; static const uint32_t kTotalEventTypes = RecordedEvent::FILTERNODESETINPUT + 1; @@ -624,6 +626,55 @@ private: MOZ_IMPLICIT RecordedPopClip(std::istream &aStream); }; +class RecordedPushLayer : public RecordedDrawingEvent { +public: + RecordedPushLayer(DrawTarget* aDT, bool aOpaque, Float aOpacity, + SourceSurface* aMask, const Matrix& aMaskTransform, + const IntRect& aBounds, bool aCopyBackground) + : RecordedDrawingEvent(PUSHLAYER, aDT), mOpaque(aOpaque) + , mOpacity(aOpacity), mMask(aMask), mMaskTransform(aMaskTransform) + , mBounds(aBounds), mCopyBackground(aCopyBackground) + { + } + + virtual void PlayEvent(Translator *aTranslator) const; + + virtual void RecordToStream(std::ostream &aStream) const; + virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const; + + virtual std::string GetName() const { return "PushLayer"; } +private: + friend class RecordedEvent; + + MOZ_IMPLICIT RecordedPushLayer(std::istream &aStream); + + bool mOpaque; + Float mOpacity; + ReferencePtr mMask; + Matrix mMaskTransform; + IntRect mBounds; + bool mCopyBackground; +}; + +class RecordedPopLayer : public RecordedDrawingEvent { +public: + RecordedPopLayer(DrawTarget* aDT) + : RecordedDrawingEvent(POPLAYER, aDT) + { + } + + virtual void PlayEvent(Translator *aTranslator) const; + + virtual void RecordToStream(std::ostream &aStream) const; + virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const; + + virtual std::string GetName() const { return "PopLayer"; } +private: + friend class RecordedEvent; + + MOZ_IMPLICIT RecordedPopLayer(std::istream &aStream); +}; + class RecordedSetTransform : public RecordedDrawingEvent { public: RecordedSetTransform(DrawTarget *aDT, const Matrix &aTransform) diff --git a/gfx/2d/SFNTData.cpp b/gfx/2d/SFNTData.cpp index 23c42331e1..1978577eb0 100644 --- a/gfx/2d/SFNTData.cpp +++ b/gfx/2d/SFNTData.cpp @@ -194,7 +194,9 @@ SFNTData::GetU16FullNames(Vector& aU16FullNames) if (mFonts[i]->GetU16FullName(name)) { fontFound = true; } - aU16FullNames.append(Move(name)); + if (!aU16FullNames.append(Move(name))) { + return false; + } } return fontFound; diff --git a/gfx/2d/SFNTNameTable.cpp b/gfx/2d/SFNTNameTable.cpp index 1977c27371..082ccfa7f4 100644 --- a/gfx/2d/SFNTNameTable.cpp +++ b/gfx/2d/SFNTNameTable.cpp @@ -128,37 +128,45 @@ CreateCanonicalU16Matchers(const BigEndianUint16& aNameID) NameRecordMatchers *matchers = new NameRecordMatchers(); // First, look for the English name (this will normally succeed). - matchers->append( + if (!matchers->append( [=](const NameRecord *aNameRecord) { return aNameRecord->nameID == aNameID && aNameRecord->languageID == CANONICAL_LANG_ID && aNameRecord->platformID == PLATFORM_ID && IsUTF16Encoding(aNameRecord); - }); + })) { + MOZ_CRASH(); + } // Second, look for all languages. - matchers->append( + if (!matchers->append( [=](const NameRecord *aNameRecord) { return aNameRecord->nameID == aNameID && aNameRecord->platformID == PLATFORM_ID && IsUTF16Encoding(aNameRecord); - }); + })) { + MOZ_CRASH(); + } #if defined(XP_MACOSX) // On Mac may be dealing with font that only has Microsoft name entries. - matchers->append( + if (!matchers->append( [=](const NameRecord *aNameRecord) { return aNameRecord->nameID == aNameID && aNameRecord->languageID == LANG_ID_MICROSOFT_EN_US && aNameRecord->platformID == PLATFORM_ID_MICROSOFT && IsUTF16Encoding(aNameRecord); - }); - matchers->append( + })) { + MOZ_CRASH(); + } + if (!matchers->append( [=](const NameRecord *aNameRecord) { return aNameRecord->nameID == aNameID && aNameRecord->platformID == PLATFORM_ID_MICROSOFT && IsUTF16Encoding(aNameRecord); - }); + })) { + MOZ_CRASH(); + } #endif return matchers; diff --git a/gfx/layers/AtomicRefCountedWithFinalize.h b/gfx/layers/AtomicRefCountedWithFinalize.h index b7e9ba5e73..67ee252744 100644 --- a/gfx/layers/AtomicRefCountedWithFinalize.h +++ b/gfx/layers/AtomicRefCountedWithFinalize.h @@ -66,7 +66,7 @@ public: friend class ::mozilla::StaticRefPtr; template - friend class ::RefPtr; + friend struct mozilla::RefPtrTraits; template friend struct ::RunnableMethodTraits; diff --git a/gfx/layers/Layers.cpp b/gfx/layers/Layers.cpp index 0705d99be4..6992daee85 100644 --- a/gfx/layers/Layers.cpp +++ b/gfx/layers/Layers.cpp @@ -2430,7 +2430,7 @@ void SetAntialiasingFlags(Layer* aLayer, DrawTarget* aTarget) { bool permitSubpixelAA = !(aLayer->GetContentFlags() & Layer::CONTENT_DISABLE_SUBPIXEL_AA); - if (aTarget->GetFormat() != SurfaceFormat::B8G8R8A8) { + if (aTarget->IsCurrentGroupOpaque()) { aTarget->SetPermitSubpixelAA(permitSubpixelAA); return; } diff --git a/gfx/layers/apz/src/AsyncPanZoomController.cpp b/gfx/layers/apz/src/AsyncPanZoomController.cpp index a4424c13e4..71c5808a09 100644 --- a/gfx/layers/apz/src/AsyncPanZoomController.cpp +++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp @@ -516,9 +516,11 @@ public: // while holding mMonitor, because otherwise, if the overscrolled APZC // is this one, then the SetState(NOTHING) in UpdateAnimation will // stomp on the SetState(SNAP_BACK) it does. - mDeferredTasks.append(NewRunnableMethod(mOverscrollHandoffChain.get(), - &OverscrollHandoffChain::SnapBackOverscrolledApzc, - &mApzc)); + if (!mDeferredTasks.append(NewRunnableMethod(mOverscrollHandoffChain.get(), + &OverscrollHandoffChain::SnapBackOverscrolledApzc, + &mApzc))) { + MOZ_CRASH(); + } return false; } @@ -567,11 +569,13 @@ public: // the lock ordering. Instead we schedule HandleFlingOverscroll() to be // called after mMonitor is released. APZC_LOG("%p fling went into overscroll, handing off with velocity %s\n", &mApzc, Stringify(velocity).c_str()); - mDeferredTasks.append(NewRunnableMethod(&mApzc, - &AsyncPanZoomController::HandleFlingOverscroll, - velocity, - mOverscrollHandoffChain, - mScrolledApzc)); + if (!mDeferredTasks.append(NewRunnableMethod(&mApzc, + &AsyncPanZoomController::HandleFlingOverscroll, + velocity, + mOverscrollHandoffChain, + mScrolledApzc))) { + MOZ_CRASH(); + } // If there is a remaining velocity on this APZC, continue this fling // as well. (This fling and the handed-off fling will run concurrently.) @@ -796,9 +800,11 @@ public: // HandleSmoothScrollOverscroll() (which acquires the tree lock) would violate // the lock ordering. Instead we schedule HandleSmoothScrollOverscroll() to be // called after mMonitor is released. - mDeferredTasks.append(NewRunnableMethod(&mApzc, - &AsyncPanZoomController::HandleSmoothScrollOverscroll, - velocity)); + if (!mDeferredTasks.append(NewRunnableMethod(&mApzc, + &AsyncPanZoomController::HandleSmoothScrollOverscroll, + velocity))) { + MOZ_CRASH(); + } return false; } diff --git a/gfx/skia/Makefile.in b/gfx/skia/Makefile.in deleted file mode 100644 index c2793d5889..0000000000 --- a/gfx/skia/Makefile.in +++ /dev/null @@ -1,11 +0,0 @@ -# -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -ifeq ($(CPU_ARCH)_$(GNU_CC),arm_1) -# The assembly uses the frame pointer register (r7 in Thumb/r11 in -# ARM), the compiler doesn't like that. -CXXFLAGS := $(filter-out -fno-omit-frame-pointer,$(CXXFLAGS)) -fomit-frame-pointer -CFLAGS := $(filter-out -fno-omit-frame-pointer,$(CFLAGS)) -fomit-frame-pointer -endif diff --git a/gfx/skia/generate_mozbuild.py b/gfx/skia/generate_mozbuild.py index da3ee16433..9135fe56d0 100755 --- a/gfx/skia/generate_mozbuild.py +++ b/gfx/skia/generate_mozbuild.py @@ -37,7 +37,6 @@ LOCAL_INCLUDES += [ 'skia/include/gpu', 'skia/include/images', 'skia/include/pathops', - 'skia/include/pipe', 'skia/include/ports', 'skia/include/private', 'skia/include/utils', @@ -112,6 +111,9 @@ elif CONFIG['CLANG_CL']: DEFINES['SKIA_IMPLEMENTATION'] = 1 +if not CONFIG['MOZ_ENABLE_SKIA_GPU']: + DEFINES['SK_SUPPORT_GPU'] = 0 + if CONFIG['GNU_CXX']: CXXFLAGS += [ '-Wno-deprecated-declarations', @@ -240,7 +242,8 @@ def generate_separated_sources(platform_sources): 'skia/src/opts/SkOpts_neon.cpp', 'skia/src/opts/SkBitmapProcState_arm_neon.cpp', }, - 'none': set() + 'none': set(), + 'gpu': set() }) for plat in platform_sources.keys(): @@ -261,6 +264,8 @@ def generate_separated_sources(platform_sources): key = 'arm' elif '_none' in value: key = 'none' + elif 'gpu' in value or 'Gpu' in value: + key = 'gpu' elif all(value in platform_sources.get(p, {}) for p in platforms if p != plat): key = 'common' @@ -371,6 +376,9 @@ def write_mozbuild(sources): write_sources(f, sources['common'], 0) + f.write("if CONFIG['MOZ_ENABLE_SKIA_GPU']:\n") + write_sources(f, sources['gpu'], 4) + f.write("if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('android', 'gonk'):\n") write_sources(f, sources['android'], 4) diff --git a/gfx/skia/moz.build b/gfx/skia/moz.build index 6a17c043ac..b9c727ff94 100644 --- a/gfx/skia/moz.build +++ b/gfx/skia/moz.build @@ -25,7 +25,6 @@ UNIFIED_SOURCES += [ 'skia/src/core/SkBitmapCache.cpp', 'skia/src/core/SkBitmapController.cpp', 'skia/src/core/SkBitmapDevice.cpp', - 'skia/src/core/SkBitmapFilter.cpp', 'skia/src/core/SkBitmapHeap.cpp', 'skia/src/core/SkBitmapProcShader.cpp', 'skia/src/core/SkBitmapProcState.cpp', @@ -43,6 +42,7 @@ UNIFIED_SOURCES += [ 'skia/src/core/SkClipStack.cpp', 'skia/src/core/SkColor.cpp', 'skia/src/core/SkColorFilter.cpp', + 'skia/src/core/SkColorFilterShader.cpp', 'skia/src/core/SkColorTable.cpp', 'skia/src/core/SkComposeShader.cpp', 'skia/src/core/SkConfig8888.cpp', @@ -65,7 +65,6 @@ UNIFIED_SOURCES += [ 'skia/src/core/SkEdgeClipper.cpp', 'skia/src/core/SkError.cpp', 'skia/src/core/SkFilterProc.cpp', - 'skia/src/core/SkFilterShader.cpp', 'skia/src/core/SkFlattenable.cpp', 'skia/src/core/SkFlattenableSerialization.cpp', 'skia/src/core/SkFloatBits.cpp', @@ -76,6 +75,7 @@ UNIFIED_SOURCES += [ 'skia/src/core/SkFontStyle.cpp', 'skia/src/core/SkForceCPlusPlusLinking.cpp', 'skia/src/core/SkGeometry.cpp', + 'skia/src/core/SkGlobalInitialization_core.cpp', 'skia/src/core/SkGlyphCache.cpp', 'skia/src/core/SkGraphics.cpp', 'skia/src/core/SkHalf.cpp', @@ -98,6 +98,7 @@ UNIFIED_SOURCES += [ 'skia/src/core/SkMetaData.cpp', 'skia/src/core/SkMiniRecorder.cpp', 'skia/src/core/SkMipMap.cpp', + 'skia/src/core/SkModeColorFilter.cpp', 'skia/src/core/SkMultiPictureDraw.cpp', 'skia/src/core/SkNinePatchIter.cpp', 'skia/src/core/SkPackBits.cpp', @@ -181,7 +182,6 @@ UNIFIED_SOURCES += [ 'skia/src/effects/gradients/SkRadialGradient.cpp', 'skia/src/effects/gradients/SkSweepGradient.cpp', 'skia/src/effects/gradients/SkTwoPointConicalGradient.cpp', - 'skia/src/effects/gradients/SkTwoPointConicalGradient_gpu.cpp', 'skia/src/effects/GrCircleBlurFragmentProcessor.cpp', 'skia/src/effects/Sk1DPathEffect.cpp', 'skia/src/effects/Sk2DPathEffect.cpp', @@ -194,7 +194,6 @@ UNIFIED_SOURCES += [ 'skia/src/effects/SkBlurMaskFilter.cpp', 'skia/src/effects/SkColorCubeFilter.cpp', 'skia/src/effects/SkColorFilterImageFilter.cpp', - 'skia/src/effects/SkColorFilters.cpp', 'skia/src/effects/SkColorMatrix.cpp', 'skia/src/effects/SkColorMatrixFilter.cpp', 'skia/src/effects/SkComposeImageFilter.cpp', @@ -205,7 +204,6 @@ UNIFIED_SOURCES += [ 'skia/src/effects/SkDropShadowImageFilter.cpp', 'skia/src/effects/SkEmbossMask.cpp', 'skia/src/effects/SkEmbossMaskFilter.cpp', - 'skia/src/effects/SkGpuBlurUtils.cpp', 'skia/src/effects/SkImageSource.cpp', 'skia/src/effects/SkLayerDrawLooper.cpp', 'skia/src/effects/SkLayerRasterizer.cpp', @@ -218,10 +216,10 @@ UNIFIED_SOURCES += [ 'skia/src/effects/SkMorphologyImageFilter.cpp', 'skia/src/effects/SkOffsetImageFilter.cpp', 'skia/src/effects/SkPaintFlagsDrawFilter.cpp', + 'skia/src/effects/SkPaintImageFilter.cpp', 'skia/src/effects/SkPerlinNoiseShader.cpp', 'skia/src/effects/SkPictureImageFilter.cpp', 'skia/src/effects/SkPixelXorXfermode.cpp', - 'skia/src/effects/SkRectShaderImageFilter.cpp', 'skia/src/effects/SkTableColorFilter.cpp', 'skia/src/effects/SkTableMaskFilter.cpp', 'skia/src/effects/SkTestImageFilters.cpp', @@ -232,133 +230,11 @@ UNIFIED_SOURCES += [ 'skia/src/fonts/SkRandomScalerContext.cpp', 'skia/src/fonts/SkRemotableFontMgr.cpp', 'skia/src/fonts/SkTestScalerContext.cpp', - 'skia/src/gpu/batches/GrAtlasTextBatch.cpp', - 'skia/src/gpu/batches/GrBatch.cpp', - 'skia/src/gpu/batches/GrCopySurfaceBatch.cpp', - 'skia/src/gpu/batches/GrDashLinePathRenderer.cpp', - 'skia/src/gpu/batches/GrDefaultPathRenderer.cpp', - 'skia/src/gpu/batches/GrDrawAtlasBatch.cpp', - 'skia/src/gpu/batches/GrDrawBatch.cpp', - 'skia/src/gpu/batches/GrDrawPathBatch.cpp', - 'skia/src/gpu/batches/GrDrawVerticesBatch.cpp', - 'skia/src/gpu/batches/GrNinePatch.cpp', - 'skia/src/gpu/batches/GrNonAAFillRectBatch.cpp', - 'skia/src/gpu/batches/GrNonAAStrokeRectBatch.cpp', - 'skia/src/gpu/batches/GrRectBatchFactory.cpp', - 'skia/src/gpu/batches/GrStencilAndCoverPathRenderer.cpp', - 'skia/src/gpu/batches/GrTessellatingPathRenderer.cpp', - 'skia/src/gpu/batches/GrVertexBatch.cpp', - 'skia/src/gpu/effects/GrBezierEffect.cpp', - 'skia/src/gpu/effects/GrBicubicEffect.cpp', - 'skia/src/gpu/effects/GrBitmapTextGeoProc.cpp', - 'skia/src/gpu/effects/GrConfigConversionEffect.cpp', - 'skia/src/gpu/effects/GrConstColorProcessor.cpp', - 'skia/src/gpu/effects/GrConvexPolyEffect.cpp', - 'skia/src/gpu/effects/GrConvolutionEffect.cpp', - 'skia/src/gpu/effects/GrCoverageSetOpXP.cpp', - 'skia/src/gpu/effects/GrCustomXfermode.cpp', - 'skia/src/gpu/effects/GrDashingEffect.cpp', - 'skia/src/gpu/effects/GrDisableColorXP.cpp', - 'skia/src/gpu/effects/GrDistanceFieldGeoProc.cpp', - 'skia/src/gpu/effects/GrDitherEffect.cpp', - 'skia/src/gpu/effects/GrMatrixConvolutionEffect.cpp', - 'skia/src/gpu/effects/GrOvalEffect.cpp', - 'skia/src/gpu/effects/GrPorterDuffXferProcessor.cpp', - 'skia/src/gpu/effects/GrRRectEffect.cpp', - 'skia/src/gpu/effects/GrSimpleTextureEffect.cpp', - 'skia/src/gpu/effects/GrSingleTextureEffect.cpp', - 'skia/src/gpu/effects/GrTextureDomain.cpp', - 'skia/src/gpu/effects/GrTextureStripAtlas.cpp', - 'skia/src/gpu/effects/GrXfermodeFragmentProcessor.cpp', - 'skia/src/gpu/effects/GrYUVtoRGBEffect.cpp', - 'skia/src/gpu/gl/debug/GrBufferObj.cpp', - 'skia/src/gpu/gl/debug/GrDebugGL.cpp', - 'skia/src/gpu/gl/debug/GrFrameBufferObj.cpp', - 'skia/src/gpu/gl/debug/GrProgramObj.cpp', - 'skia/src/gpu/gl/debug/GrShaderObj.cpp', - 'skia/src/gpu/gl/debug/GrTextureObj.cpp', - 'skia/src/gpu/gl/debug/GrTextureUnitObj.cpp', - 'skia/src/gpu/gl/debug/SkDebugGLContext.cpp', - 'skia/src/gpu/gl/SkGLContext.cpp', - 'skia/src/gpu/gl/SkNullGLContext.cpp', - 'skia/src/gpu/GrAtlasTextBlob.cpp', - 'skia/src/gpu/GrAtlasTextContext.cpp', - 'skia/src/gpu/GrBatchFlushState.cpp', - 'skia/src/gpu/GrBatchFontCache.cpp', - 'skia/src/gpu/GrBatchTest.cpp', - 'skia/src/gpu/GrBlend.cpp', - 'skia/src/gpu/GrBlurUtils.cpp', - 'skia/src/gpu/GrBufferAllocPool.cpp', - 'skia/src/gpu/GrCaps.cpp', - 'skia/src/gpu/GrClip.cpp', - 'skia/src/gpu/GrClipMaskManager.cpp', - 'skia/src/gpu/GrContext.cpp', - 'skia/src/gpu/GrCoordTransform.cpp', - 'skia/src/gpu/GrDefaultGeoProcFactory.cpp', - 'skia/src/gpu/GrDrawingManager.cpp', - 'skia/src/gpu/GrDrawTarget.cpp', - 'skia/src/gpu/GrFontScaler.cpp', - 'skia/src/gpu/GrFragmentProcessor.cpp', - 'skia/src/gpu/GrGpu.cpp', - 'skia/src/gpu/GrGpuFactory.cpp', - 'skia/src/gpu/GrGpuResource.cpp', - 'skia/src/gpu/GrGpuResourceRef.cpp', - 'skia/src/gpu/GrImageIDTextureAdjuster.cpp', - 'skia/src/gpu/GrInvariantOutput.cpp', - 'skia/src/gpu/GrLayerAtlas.cpp', - 'skia/src/gpu/GrLayerCache.cpp', - 'skia/src/gpu/GrLayerHoister.cpp', - 'skia/src/gpu/GrMemoryPool.cpp', - 'skia/src/gpu/GrOvalRenderer.cpp', - 'skia/src/gpu/GrPaint.cpp', - 'skia/src/gpu/GrPath.cpp', - 'skia/src/gpu/GrPathProcessor.cpp', - 'skia/src/gpu/GrPathRange.cpp', - 'skia/src/gpu/GrPathRenderer.cpp', - 'skia/src/gpu/GrPathRendererChain.cpp', - 'skia/src/gpu/GrPathRendering.cpp', - 'skia/src/gpu/GrPathUtils.cpp', - 'skia/src/gpu/GrPipeline.cpp', - 'skia/src/gpu/GrPipelineBuilder.cpp', - 'skia/src/gpu/GrPrimitiveProcessor.cpp', - 'skia/src/gpu/GrProcessor.cpp', - 'skia/src/gpu/GrProcessorUnitTest.cpp', - 'skia/src/gpu/GrProcOptInfo.cpp', - 'skia/src/gpu/GrProgramElement.cpp', - 'skia/src/gpu/GrRecordReplaceDraw.cpp', - 'skia/src/gpu/GrRectanizer_pow2.cpp', - 'skia/src/gpu/GrRectanizer_skyline.cpp', - 'skia/src/gpu/GrReducedClip.cpp', - 'skia/src/gpu/GrRenderTarget.cpp', - 'skia/src/gpu/GrResourceProvider.cpp', - 'skia/src/gpu/GrSoftwarePathRenderer.cpp', - 'skia/src/gpu/GrStencil.cpp', - 'skia/src/gpu/GrStencilAndCoverTextContext.cpp', - 'skia/src/gpu/GrStencilAttachment.cpp', - 'skia/src/gpu/GrStrokeInfo.cpp', - 'skia/src/gpu/GrSurface.cpp', - 'skia/src/gpu/GrSWMaskHelper.cpp', - 'skia/src/gpu/GrTestUtils.cpp', - 'skia/src/gpu/GrTextBlobCache.cpp', - 'skia/src/gpu/GrTextContext.cpp', - 'skia/src/gpu/GrTexture.cpp', - 'skia/src/gpu/GrTextureAccess.cpp', - 'skia/src/gpu/GrTextureParamsAdjuster.cpp', - 'skia/src/gpu/GrTextureProvider.cpp', - 'skia/src/gpu/GrTraceMarker.cpp', - 'skia/src/gpu/GrXferProcessor.cpp', - 'skia/src/gpu/GrYUVProvider.cpp', - 'skia/src/gpu/SkGpuDevice.cpp', - 'skia/src/gpu/SkGpuDevice_drawTexture.cpp', - 'skia/src/gpu/SkGr.cpp', - 'skia/src/gpu/SkGrPixelRef.cpp', - 'skia/src/gpu/SkGrTexturePixelRef.cpp', 'skia/src/image/SkImage.cpp', 'skia/src/image/SkImage_Generator.cpp', 'skia/src/image/SkImage_Raster.cpp', 'skia/src/image/SkImageShader.cpp', 'skia/src/image/SkSurface.cpp', - 'skia/src/image/SkSurface_Gpu.cpp', 'skia/src/image/SkSurface_Raster.cpp', 'skia/src/images/bmpdecoderhelper.cpp', 'skia/src/images/SkDecodingImageGenerator.cpp', @@ -397,17 +273,15 @@ UNIFIED_SOURCES += [ 'skia/src/pathops/SkPathOpsWinding.cpp', 'skia/src/pathops/SkPathWriter.cpp', 'skia/src/pathops/SkReduceOrder.cpp', - 'skia/src/pipe/SkGPipeRead.cpp', - 'skia/src/pipe/SkGPipeWrite.cpp', 'skia/src/ports/SkDiscardableMemory_none.cpp', 'skia/src/ports/SkGlobalInitialization_default.cpp', 'skia/src/ports/SkImageDecoder_empty.cpp', 'skia/src/ports/SkImageGenerator_skia.cpp', 'skia/src/ports/SkMemory_mozalloc.cpp', + 'skia/src/ports/SkOSEnvironment.cpp', 'skia/src/ports/SkOSFile_stdio.cpp', 'skia/src/sfnt/SkOTTable_name.cpp', 'skia/src/sfnt/SkOTUtils.cpp', - 'skia/src/utils/android/SkAndroidSDKCanvas.cpp', 'skia/src/utils/SkBase64.cpp', 'skia/src/utils/SkBitmapSourceDeserializer.cpp', 'skia/src/utils/SkBitSet.cpp', @@ -415,8 +289,6 @@ UNIFIED_SOURCES += [ 'skia/src/utils/SkCamera.cpp', 'skia/src/utils/SkCanvasStack.cpp', 'skia/src/utils/SkCanvasStateUtils.cpp', - 'skia/src/utils/SkCubicInterval.cpp', - 'skia/src/utils/SkCullPoints.cpp', 'skia/src/utils/SkDashPath.cpp', 'skia/src/utils/SkDumpCanvas.cpp', 'skia/src/utils/SkEventTracer.cpp', @@ -455,71 +327,205 @@ SOURCES += [ 'skia/src/core/SkPictureData.cpp', 'skia/src/core/SkRecorder.cpp', 'skia/src/core/SkScan_Antihair.cpp', - 'skia/src/effects/SkArithmeticMode_gpu.cpp', - 'skia/src/gpu/batches/GrAAConvexPathRenderer.cpp', - 'skia/src/gpu/batches/GrAAConvexTessellator.cpp', - 'skia/src/gpu/batches/GrAADistanceFieldPathRenderer.cpp', - 'skia/src/gpu/batches/GrAAFillRectBatch.cpp', - 'skia/src/gpu/batches/GrAAHairLinePathRenderer.cpp', - 'skia/src/gpu/batches/GrAALinearizingConvexPathRenderer.cpp', - 'skia/src/gpu/batches/GrAAStrokeRectBatch.cpp', - 'skia/src/gpu/gl/builders/GrGLProgramBuilder.cpp', - 'skia/src/gpu/gl/builders/GrGLShaderStringBuilder.cpp', - 'skia/src/gpu/gl/builders/GrGLSLPrettyPrint.cpp', - 'skia/src/gpu/gl/debug/GrGLCreateDebugInterface.cpp', - 'skia/src/gpu/gl/GrGLAssembleInterface.cpp', - 'skia/src/gpu/gl/GrGLBufferImpl.cpp', - 'skia/src/gpu/gl/GrGLCaps.cpp', - 'skia/src/gpu/gl/GrGLContext.cpp', 'skia/src/gpu/gl/GrGLCreateNativeInterface_none.cpp', - 'skia/src/gpu/gl/GrGLCreateNullInterface.cpp', - 'skia/src/gpu/gl/GrGLDefaultInterface_native.cpp', - 'skia/src/gpu/gl/GrGLExtensions.cpp', - 'skia/src/gpu/gl/GrGLGLSL.cpp', - 'skia/src/gpu/gl/GrGLGpu.cpp', - 'skia/src/gpu/gl/GrGLGpuProgramCache.cpp', - 'skia/src/gpu/gl/GrGLIndexBuffer.cpp', - 'skia/src/gpu/gl/GrGLInterface.cpp', - 'skia/src/gpu/gl/GrGLNameAllocator.cpp', - 'skia/src/gpu/gl/GrGLNoOpInterface.cpp', - 'skia/src/gpu/gl/GrGLPath.cpp', - 'skia/src/gpu/gl/GrGLPathRange.cpp', - 'skia/src/gpu/gl/GrGLPathRendering.cpp', - 'skia/src/gpu/gl/GrGLProgram.cpp', - 'skia/src/gpu/gl/GrGLProgramDataManager.cpp', - 'skia/src/gpu/gl/GrGLProgramDesc.cpp', - 'skia/src/gpu/gl/GrGLRenderTarget.cpp', - 'skia/src/gpu/gl/GrGLStencilAttachment.cpp', - 'skia/src/gpu/gl/GrGLTexture.cpp', - 'skia/src/gpu/gl/GrGLTextureRenderTarget.cpp', - 'skia/src/gpu/gl/GrGLUtil.cpp', - 'skia/src/gpu/gl/GrGLVaryingHandler.cpp', - 'skia/src/gpu/gl/GrGLVertexArray.cpp', - 'skia/src/gpu/gl/GrGLVertexBuffer.cpp', - 'skia/src/gpu/glsl/GrGLSL.cpp', - 'skia/src/gpu/glsl/GrGLSLBlend.cpp', - 'skia/src/gpu/glsl/GrGLSLCaps.cpp', - 'skia/src/gpu/glsl/GrGLSLFragmentProcessor.cpp', - 'skia/src/gpu/glsl/GrGLSLFragmentShaderBuilder.cpp', - 'skia/src/gpu/glsl/GrGLSLGeometryProcessor.cpp', - 'skia/src/gpu/glsl/GrGLSLGeometryShaderBuilder.cpp', - 'skia/src/gpu/glsl/GrGLSLPrimitiveProcessor.cpp', - 'skia/src/gpu/glsl/GrGLSLProgramBuilder.cpp', - 'skia/src/gpu/glsl/GrGLSLShaderBuilder.cpp', - 'skia/src/gpu/glsl/GrGLSLUtil.cpp', - 'skia/src/gpu/glsl/GrGLSLVarying.cpp', - 'skia/src/gpu/glsl/GrGLSLVertexShaderBuilder.cpp', - 'skia/src/gpu/glsl/GrGLSLXferProcessor.cpp', - 'skia/src/gpu/GrBatchAtlas.cpp', - 'skia/src/gpu/GrDrawContext.cpp', - 'skia/src/gpu/GrResourceCache.cpp', - 'skia/src/image/SkImage_Gpu.cpp', 'skia/src/pathops/SkPathOpsDebug.cpp', 'skia/src/utils/SkMD5.cpp', 'skia/src/utils/SkParse.cpp', 'skia/src/utils/SkParsePath.cpp', 'skia/src/utils/SkSHA1.cpp', ] +if CONFIG['MOZ_ENABLE_SKIA_GPU']: + UNIFIED_SOURCES += [ + 'skia/src/effects/gradients/SkTwoPointConicalGradient_gpu.cpp', + 'skia/src/effects/SkGpuBlurUtils.cpp', + 'skia/src/gpu/batches/GrAtlasTextBatch.cpp', + 'skia/src/gpu/batches/GrBatch.cpp', + 'skia/src/gpu/batches/GrCopySurfaceBatch.cpp', + 'skia/src/gpu/batches/GrDashLinePathRenderer.cpp', + 'skia/src/gpu/batches/GrDefaultPathRenderer.cpp', + 'skia/src/gpu/batches/GrDrawAtlasBatch.cpp', + 'skia/src/gpu/batches/GrDrawBatch.cpp', + 'skia/src/gpu/batches/GrDrawPathBatch.cpp', + 'skia/src/gpu/batches/GrDrawVerticesBatch.cpp', + 'skia/src/gpu/batches/GrNinePatch.cpp', + 'skia/src/gpu/batches/GrNonAAFillRectBatch.cpp', + 'skia/src/gpu/batches/GrNonAAStrokeRectBatch.cpp', + 'skia/src/gpu/batches/GrRectBatchFactory.cpp', + 'skia/src/gpu/batches/GrStencilAndCoverPathRenderer.cpp', + 'skia/src/gpu/batches/GrTessellatingPathRenderer.cpp', + 'skia/src/gpu/batches/GrVertexBatch.cpp', + 'skia/src/gpu/effects/GrBezierEffect.cpp', + 'skia/src/gpu/effects/GrBicubicEffect.cpp', + 'skia/src/gpu/effects/GrBitmapTextGeoProc.cpp', + 'skia/src/gpu/effects/GrConfigConversionEffect.cpp', + 'skia/src/gpu/effects/GrConstColorProcessor.cpp', + 'skia/src/gpu/effects/GrConvexPolyEffect.cpp', + 'skia/src/gpu/effects/GrConvolutionEffect.cpp', + 'skia/src/gpu/effects/GrCoverageSetOpXP.cpp', + 'skia/src/gpu/effects/GrCustomXfermode.cpp', + 'skia/src/gpu/effects/GrDashingEffect.cpp', + 'skia/src/gpu/effects/GrDisableColorXP.cpp', + 'skia/src/gpu/effects/GrDistanceFieldGeoProc.cpp', + 'skia/src/gpu/effects/GrDitherEffect.cpp', + 'skia/src/gpu/effects/GrMatrixConvolutionEffect.cpp', + 'skia/src/gpu/effects/GrOvalEffect.cpp', + 'skia/src/gpu/effects/GrPorterDuffXferProcessor.cpp', + 'skia/src/gpu/effects/GrRRectEffect.cpp', + 'skia/src/gpu/effects/GrSimpleTextureEffect.cpp', + 'skia/src/gpu/effects/GrSingleTextureEffect.cpp', + 'skia/src/gpu/effects/GrTextureDomain.cpp', + 'skia/src/gpu/effects/GrTextureStripAtlas.cpp', + 'skia/src/gpu/effects/GrXfermodeFragmentProcessor.cpp', + 'skia/src/gpu/effects/GrYUVtoRGBEffect.cpp', + 'skia/src/gpu/gl/debug/GrBufferObj.cpp', + 'skia/src/gpu/gl/debug/GrDebugGL.cpp', + 'skia/src/gpu/gl/debug/GrFrameBufferObj.cpp', + 'skia/src/gpu/gl/debug/GrProgramObj.cpp', + 'skia/src/gpu/gl/debug/GrShaderObj.cpp', + 'skia/src/gpu/gl/debug/GrTextureObj.cpp', + 'skia/src/gpu/gl/debug/GrTextureUnitObj.cpp', + 'skia/src/gpu/gl/debug/SkDebugGLContext.cpp', + 'skia/src/gpu/gl/SkGLContext.cpp', + 'skia/src/gpu/gl/SkNullGLContext.cpp', + 'skia/src/gpu/GrAuditTrail.cpp', + 'skia/src/gpu/GrBatchFlushState.cpp', + 'skia/src/gpu/GrBatchTest.cpp', + 'skia/src/gpu/GrBlend.cpp', + 'skia/src/gpu/GrBlurUtils.cpp', + 'skia/src/gpu/GrBufferAllocPool.cpp', + 'skia/src/gpu/GrCaps.cpp', + 'skia/src/gpu/GrClip.cpp', + 'skia/src/gpu/GrClipMaskManager.cpp', + 'skia/src/gpu/GrContext.cpp', + 'skia/src/gpu/GrCoordTransform.cpp', + 'skia/src/gpu/GrDefaultGeoProcFactory.cpp', + 'skia/src/gpu/GrDrawingManager.cpp', + 'skia/src/gpu/GrDrawTarget.cpp', + 'skia/src/gpu/GrFragmentProcessor.cpp', + 'skia/src/gpu/GrGpu.cpp', + 'skia/src/gpu/GrGpuFactory.cpp', + 'skia/src/gpu/GrGpuResource.cpp', + 'skia/src/gpu/GrGpuResourceRef.cpp', + 'skia/src/gpu/GrImageIDTextureAdjuster.cpp', + 'skia/src/gpu/GrInvariantOutput.cpp', + 'skia/src/gpu/GrLayerAtlas.cpp', + 'skia/src/gpu/GrLayerCache.cpp', + 'skia/src/gpu/GrLayerHoister.cpp', + 'skia/src/gpu/GrMemoryPool.cpp', + 'skia/src/gpu/GrOvalRenderer.cpp', + 'skia/src/gpu/GrPaint.cpp', + 'skia/src/gpu/GrPath.cpp', + 'skia/src/gpu/GrPathProcessor.cpp', + 'skia/src/gpu/GrPathRange.cpp', + 'skia/src/gpu/GrPathRenderer.cpp', + 'skia/src/gpu/GrPathRendererChain.cpp', + 'skia/src/gpu/GrPathRendering.cpp', + 'skia/src/gpu/GrPathUtils.cpp', + 'skia/src/gpu/GrPipeline.cpp', + 'skia/src/gpu/GrPipelineBuilder.cpp', + 'skia/src/gpu/GrPrimitiveProcessor.cpp', + 'skia/src/gpu/GrProcessor.cpp', + 'skia/src/gpu/GrProcessorUnitTest.cpp', + 'skia/src/gpu/GrProcOptInfo.cpp', + 'skia/src/gpu/GrProgramElement.cpp', + 'skia/src/gpu/GrRecordReplaceDraw.cpp', + 'skia/src/gpu/GrRectanizer_pow2.cpp', + 'skia/src/gpu/GrRectanizer_skyline.cpp', + 'skia/src/gpu/GrReducedClip.cpp', + 'skia/src/gpu/GrRenderTarget.cpp', + 'skia/src/gpu/GrResourceProvider.cpp', + 'skia/src/gpu/GrSoftwarePathRenderer.cpp', + 'skia/src/gpu/GrStencil.cpp', + 'skia/src/gpu/GrStencilAttachment.cpp', + 'skia/src/gpu/GrStrokeInfo.cpp', + 'skia/src/gpu/GrSurface.cpp', + 'skia/src/gpu/GrSWMaskHelper.cpp', + 'skia/src/gpu/GrTessellator.cpp', + 'skia/src/gpu/GrTestUtils.cpp', + 'skia/src/gpu/GrTexture.cpp', + 'skia/src/gpu/GrTextureAccess.cpp', + 'skia/src/gpu/GrTextureParamsAdjuster.cpp', + 'skia/src/gpu/GrTextureProvider.cpp', + 'skia/src/gpu/GrTraceMarker.cpp', + 'skia/src/gpu/GrXferProcessor.cpp', + 'skia/src/gpu/GrYUVProvider.cpp', + 'skia/src/gpu/SkGpuDevice.cpp', + 'skia/src/gpu/SkGpuDevice_drawTexture.cpp', + 'skia/src/gpu/SkGr.cpp', + 'skia/src/gpu/SkGrPixelRef.cpp', + 'skia/src/gpu/SkGrTexturePixelRef.cpp', + 'skia/src/gpu/text/GrAtlasTextBlob.cpp', + 'skia/src/gpu/text/GrAtlasTextContext.cpp', + 'skia/src/gpu/text/GrBatchFontCache.cpp', + 'skia/src/gpu/text/GrDistanceFieldAdjustTable.cpp', + 'skia/src/gpu/text/GrFontScaler.cpp', + 'skia/src/gpu/text/GrStencilAndCoverTextContext.cpp', + 'skia/src/gpu/text/GrTextBlobCache.cpp', + 'skia/src/gpu/text/GrTextContext.cpp', + 'skia/src/gpu/text/GrTextUtils.cpp', + 'skia/src/image/SkSurface_Gpu.cpp', + ] + SOURCES += [ + 'skia/src/effects/SkArithmeticMode_gpu.cpp', + 'skia/src/gpu/batches/GrAAConvexPathRenderer.cpp', + 'skia/src/gpu/batches/GrAAConvexTessellator.cpp', + 'skia/src/gpu/batches/GrAADistanceFieldPathRenderer.cpp', + 'skia/src/gpu/batches/GrAAFillRectBatch.cpp', + 'skia/src/gpu/batches/GrAAHairLinePathRenderer.cpp', + 'skia/src/gpu/batches/GrAALinearizingConvexPathRenderer.cpp', + 'skia/src/gpu/batches/GrAAStrokeRectBatch.cpp', + 'skia/src/gpu/gl/builders/GrGLProgramBuilder.cpp', + 'skia/src/gpu/gl/builders/GrGLShaderStringBuilder.cpp', + 'skia/src/gpu/gl/builders/GrGLSLPrettyPrint.cpp', + 'skia/src/gpu/gl/debug/GrGLCreateDebugInterface.cpp', + 'skia/src/gpu/gl/GrGLAssembleInterface.cpp', + 'skia/src/gpu/gl/GrGLBufferImpl.cpp', + 'skia/src/gpu/gl/GrGLCaps.cpp', + 'skia/src/gpu/gl/GrGLContext.cpp', + 'skia/src/gpu/gl/GrGLCreateNullInterface.cpp', + 'skia/src/gpu/gl/GrGLDefaultInterface_native.cpp', + 'skia/src/gpu/gl/GrGLExtensions.cpp', + 'skia/src/gpu/gl/GrGLGLSL.cpp', + 'skia/src/gpu/gl/GrGLGpu.cpp', + 'skia/src/gpu/gl/GrGLGpuProgramCache.cpp', + 'skia/src/gpu/gl/GrGLIndexBuffer.cpp', + 'skia/src/gpu/gl/GrGLInterface.cpp', + 'skia/src/gpu/gl/GrGLNoOpInterface.cpp', + 'skia/src/gpu/gl/GrGLPath.cpp', + 'skia/src/gpu/gl/GrGLPathRange.cpp', + 'skia/src/gpu/gl/GrGLPathRendering.cpp', + 'skia/src/gpu/gl/GrGLProgram.cpp', + 'skia/src/gpu/gl/GrGLProgramDataManager.cpp', + 'skia/src/gpu/gl/GrGLProgramDesc.cpp', + 'skia/src/gpu/gl/GrGLRenderTarget.cpp', + 'skia/src/gpu/gl/GrGLStencilAttachment.cpp', + 'skia/src/gpu/gl/GrGLTexture.cpp', + 'skia/src/gpu/gl/GrGLTextureRenderTarget.cpp', + 'skia/src/gpu/gl/GrGLTransferBuffer.cpp', + 'skia/src/gpu/gl/GrGLUniformHandler.cpp', + 'skia/src/gpu/gl/GrGLUtil.cpp', + 'skia/src/gpu/gl/GrGLVaryingHandler.cpp', + 'skia/src/gpu/gl/GrGLVertexArray.cpp', + 'skia/src/gpu/gl/GrGLVertexBuffer.cpp', + 'skia/src/gpu/glsl/GrGLSL.cpp', + 'skia/src/gpu/glsl/GrGLSLBlend.cpp', + 'skia/src/gpu/glsl/GrGLSLCaps.cpp', + 'skia/src/gpu/glsl/GrGLSLFragmentProcessor.cpp', + 'skia/src/gpu/glsl/GrGLSLFragmentShaderBuilder.cpp', + 'skia/src/gpu/glsl/GrGLSLGeometryProcessor.cpp', + 'skia/src/gpu/glsl/GrGLSLGeometryShaderBuilder.cpp', + 'skia/src/gpu/glsl/GrGLSLPrimitiveProcessor.cpp', + 'skia/src/gpu/glsl/GrGLSLProgramBuilder.cpp', + 'skia/src/gpu/glsl/GrGLSLShaderBuilder.cpp', + 'skia/src/gpu/glsl/GrGLSLUtil.cpp', + 'skia/src/gpu/glsl/GrGLSLVarying.cpp', + 'skia/src/gpu/glsl/GrGLSLVertexShaderBuilder.cpp', + 'skia/src/gpu/glsl/GrGLSLXferProcessor.cpp', + 'skia/src/gpu/GrBatchAtlas.cpp', + 'skia/src/gpu/GrDrawContext.cpp', + 'skia/src/gpu/GrResourceCache.cpp', + 'skia/src/image/SkImage_Gpu.cpp', + ] if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('android', 'gonk'): UNIFIED_SOURCES += [ 'skia/src/ports/SkDebug_android.cpp', @@ -649,7 +655,6 @@ LOCAL_INCLUDES += [ 'skia/include/gpu', 'skia/include/images', 'skia/include/pathops', - 'skia/include/pipe', 'skia/include/ports', 'skia/include/private', 'skia/include/utils', @@ -730,6 +735,9 @@ elif CONFIG['CLANG_CL']: DEFINES['SKIA_IMPLEMENTATION'] = 1 +if not CONFIG['MOZ_ENABLE_SKIA_GPU']: + DEFINES['SK_SUPPORT_GPU'] = 0 + if CONFIG['GNU_CXX']: CXXFLAGS += [ '-Wno-deprecated-declarations', diff --git a/gfx/skia/skia/include/codec/SkAndroidCodec.h b/gfx/skia/skia/include/codec/SkAndroidCodec.h index f979886a43..c5578d3964 100644 --- a/gfx/skia/skia/include/codec/SkAndroidCodec.h +++ b/gfx/skia/skia/include/codec/SkAndroidCodec.h @@ -50,7 +50,25 @@ public: /** * Format of the encoded data. */ - SkEncodedFormat getEncodedFormat() const { return this->onGetEncodedFormat(); } + SkEncodedFormat getEncodedFormat() const { return fCodec->getEncodedFormat(); } + + /** + * @param requestedColorType Color type requested by the client + * + * If it is possible to decode to requestedColorType, this returns + * requestedColorType. Otherwise, this returns whichever color type + * is suggested by the codec as the best match for the encoded data. + */ + SkColorType computeOutputColorType(SkColorType requestedColorType); + + /** + * @param requestedUnpremul Indicates if the client requested + * unpremultiplied output + * + * Returns the appropriate alpha type to decode to. If the image + * has alpha, the value of requestedUnpremul will be honored. + */ + SkAlphaType computeOutputAlphaType(bool requestedUnpremul); /** * Returns the dimensions of the scaled output image, for an input @@ -210,9 +228,9 @@ public: protected: - SkAndroidCodec(const SkImageInfo&); + SkAndroidCodec(SkCodec*); - virtual SkEncodedFormat onGetEncodedFormat() const = 0; + SkCodec* codec() const { return fCodec.get(); } virtual SkISize onGetSampledDimensions(int sampleSize) const = 0; @@ -226,5 +244,7 @@ private: // This will always be a reference to the info that is contained by the // embedded SkCodec. const SkImageInfo& fInfo; + + SkAutoTDelete fCodec; }; #endif // SkAndroidCodec_DEFINED diff --git a/gfx/skia/skia/include/codec/SkCodec.h b/gfx/skia/skia/include/codec/SkCodec.h index 9f28af010e..597ebd039e 100644 --- a/gfx/skia/skia/include/codec/SkCodec.h +++ b/gfx/skia/skia/include/codec/SkCodec.h @@ -25,10 +25,35 @@ class SkSampler; */ class SkCodec : SkNoncopyable { public: + /** + * Minimum number of bytes that must be buffered in SkStream input. + * + * An SkStream passed to NewFromStream must be able to use this many + * bytes to determine the image type. Then the same SkStream must be + * passed to the correct decoder to read from the beginning. + * + * This can be accomplished by implementing peek() to support peeking + * this many bytes, or by implementing rewind() to be able to rewind() + * after reading this many bytes. + */ + static size_t MinBufferedBytesNeeded(); + /** * If this stream represents an encoded image that we know how to decode, * return an SkCodec that can decode it. Otherwise return NULL. * + * As stated above, this call must be able to peek or read + * MinBufferedBytesNeeded to determine the correct format, and then start + * reading from the beginning. First it will attempt to peek, and it + * assumes that if less than MinBufferedBytesNeeded bytes (but more than + * zero) are returned, this is because the stream is shorter than this, + * so falling back to reading would not provide more data. If peek() + * returns zero bytes, this call will instead attempt to read(). This + * will require that the stream can be rewind()ed. + * + * If SkPngChunkReader is not NULL, take a ref and pass it to libpng if + * the image is a png. + * * If the SkPngChunkReader is not NULL then: * If the image is not a PNG, the SkPngChunkReader will be ignored. * If the image is a PNG, the SkPngChunkReader will be reffed. @@ -252,18 +277,6 @@ public: */ Result getPixels(const SkImageInfo& info, void* pixels, size_t rowBytes); - /** - * Some images may initially report that they have alpha due to the format - * of the encoded data, but then never use any colors which have alpha - * less than 100%. This function can be called *after* decoding to - * determine if such an image truly had alpha. Calling it before decoding - * is undefined. - * FIXME: see skbug.com/3582. - */ - bool reallyHasAlpha() const { - return this->onReallyHasAlpha(); - } - /** * The remaining functions revolve around decoding scanlines. */ @@ -400,6 +413,8 @@ public: /** * An enum representing the order in which scanlines will be returned by * the scanline decoder. + * + * This is undefined before startScanlineDecode() is called. */ SkScanlineOrder getScanlineOrder() const { return this->onGetScanlineOrder(); } @@ -415,9 +430,9 @@ public: int nextScanline() const { return this->outputScanline(fCurrScanline); } /** - * Returns the output y-coordinate of the row that corresponds to an input - * y-coordinate. The input y-coordinate represents where the scanline - * is located in the encoded data. + * Returns the output y-coordinate of the row that corresponds to an input + * y-coordinate. The input y-coordinate represents where the scanline + * is located in the encoded data. * * This will equal inputScanline, except in the case of strangely * encoded image types (bottom-up bmps, interlaced gifs). @@ -459,8 +474,6 @@ protected: return false; } - virtual bool onReallyHasAlpha() const { return false; } - /** * If the stream was previously read, attempt to rewind. * @@ -529,14 +542,22 @@ protected: virtual SkScanlineOrder onGetScanlineOrder() const { return kTopDown_SkScanlineOrder; } /** - * Update the next scanline. Used by interlaced png. + * Update the current scanline. Used by interlaced png. */ - void updateNextScanline(int newY) { fCurrScanline = newY; } + void updateCurrScanline(int newY) { fCurrScanline = newY; } const SkImageInfo& dstInfo() const { return fDstInfo; } const SkCodec::Options& options() const { return fOptions; } + /** + * Returns the number of scanlines that have been decoded so far. + * This is unaffected by the SkScanlineOrder. + * + * Returns -1 if we have not started a scanline decode. + */ + int currScanline() const { return fCurrScanline; } + virtual int onOutputScanline(int inputScanline) const; private: @@ -610,5 +631,6 @@ private: virtual SkSampler* getSampler(bool /*createIfNecessary*/) { return nullptr; } friend class SkSampledCodec; + friend class SkIcoCodec; }; #endif // SkCodec_DEFINED diff --git a/gfx/skia/skia/include/config/SkUserConfig.h b/gfx/skia/skia/include/config/SkUserConfig.h index f1a4959cad..c0186122e2 100644 --- a/gfx/skia/skia/include/config/SkUserConfig.h +++ b/gfx/skia/skia/include/config/SkUserConfig.h @@ -162,6 +162,7 @@ #define SK_ALLOW_STATIC_GLOBAL_INITIALIZERS 0 #define SK_SUPPORT_LEGACY_GETDEVICE +#define SK_SUPPORT_LEGACY_GETTOPDEVICE #define SK_IGNORE_ETC1_SUPPORT diff --git a/gfx/skia/skia/include/core/SkBitmap.h b/gfx/skia/skia/include/core/SkBitmap.h index eda13b3c52..a81e03eefa 100644 --- a/gfx/skia/skia/include/core/SkBitmap.h +++ b/gfx/skia/skia/include/core/SkBitmap.h @@ -54,12 +54,24 @@ public: */ SkBitmap(const SkBitmap& src); + /** + * Copy the settings from the src into this bitmap. If the src has pixels + * allocated, ownership of the pixels will be taken. + */ + SkBitmap(SkBitmap&& src); + ~SkBitmap(); - /** Copies the src bitmap into this bitmap. Ownership of the src bitmap's pixels remains - with the src bitmap. + /** Copies the src bitmap into this bitmap. Ownership of the src + bitmap's pixels is shared with the src bitmap. */ SkBitmap& operator=(const SkBitmap& src); + + /** Copies the src bitmap into this bitmap. Takes ownership of the src + bitmap's pixels. + */ + SkBitmap& operator=(SkBitmap&& src); + /** Swap the fields of the two bitmaps. This routine is guaranteed to never fail or throw. */ // This method is not exported to java. @@ -294,6 +306,14 @@ public: return this->installPixels(info, pixels, rowBytes, NULL, NULL, NULL); } + /** + * Call installPixels with no ReleaseProc specified. This means + * that the caller must ensure that the specified pixels and + * colortable are valid for the lifetime of the created bitmap + * (and its pixelRef). + */ + bool installPixels(const SkPixmap&); + /** * Calls installPixels() with the value in the SkMask. The caller must * ensure that the specified mask pixels are valid for the lifetime diff --git a/gfx/skia/skia/include/core/SkCanvas.h b/gfx/skia/skia/include/core/SkCanvas.h index 53f6dda88b..d1de626315 100644 --- a/gfx/skia/skia/include/core/SkCanvas.h +++ b/gfx/skia/skia/include/core/SkCanvas.h @@ -37,6 +37,16 @@ class SkSurface; class SkSurface_Base; class SkTextBlob; +/* + * If you want the legacy cliptolayer flag (i.e. android), then you must have the new + * legacy saveflags. + */ +#ifdef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG +#ifndef SK_SUPPORT_LEGACY_SAVEFLAGS + #define SK_SUPPORT_LEGACY_SAVEFLAGS +#endif +#endif + /** \class SkCanvas A Canvas encapsulates all of the state about drawing into a device (bitmap). @@ -53,6 +63,10 @@ class SkTextBlob; etc. */ class SK_API SkCanvas : public SkRefCnt { + enum PrivateSaveLayerFlags { + kDontClipToLayer_PrivateSaveLayerFlag = 1 << 31, + }; + public: /** * Attempt to allocate raster canvas, matching the ImageInfo, that will draw directly into the @@ -281,6 +295,7 @@ public: /////////////////////////////////////////////////////////////////////////// +#ifdef SK_SUPPORT_LEGACY_SAVEFLAGS enum SaveFlags { /** save the matrix state, restoring it on restore() */ // [deprecated] kMatrix_SaveFlag = 0x01, @@ -307,6 +322,7 @@ public: #endif kARGB_ClipLayer_SaveFlag = 0x1F }; +#endif /** This call saves the current matrix, clip, and drawFilter, and pushes a copy onto a private stack. Subsequent calls to translate, scale, @@ -336,6 +352,14 @@ public: return this->saveLayer(&bounds, paint); } + /** + * Temporary name. + * Will allow any requests for LCD text to be respected, so the caller must be careful to + * only draw on top of opaque sections of the layer to get good results. + */ + int saveLayerPreserveLCDTextRequests(const SkRect* bounds, const SkPaint* paint); + +#ifdef SK_SUPPORT_LEGACY_SAVEFLAGS /** DEPRECATED - use saveLayer(const SkRect*, const SkPaint*) instead. This behaves the same as saveLayer(const SkRect*, const SkPaint*), @@ -353,6 +377,7 @@ public: */ SK_ATTR_EXTERNALLY_DEPRECATED("SaveFlags use is deprecated") int saveLayer(const SkRect* bounds, const SkPaint* paint, SaveFlags flags); +#endif /** This behaves the same as save(), but in addition it allocates an offscreen bitmap. All drawing calls are directed there, and only when @@ -367,6 +392,7 @@ public: */ int saveLayerAlpha(const SkRect* bounds, U8CPU alpha); +#ifdef SK_SUPPORT_LEGACY_SAVEFLAGS /** DEPRECATED - use saveLayerAlpha(const SkRect*, U8CPU) instead. This behaves the same as saveLayerAlpha(const SkRect*, U8CPU), @@ -383,6 +409,43 @@ public: */ SK_ATTR_EXTERNALLY_DEPRECATED("SaveFlags use is deprecated") int saveLayerAlpha(const SkRect* bounds, U8CPU alpha, SaveFlags flags); +#endif + + enum { + kIsOpaque_SaveLayerFlag = 1 << 0, + kPreserveLCDText_SaveLayerFlag = 1 << 1, + +#ifdef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG + kDontClipToLayer_Legacy_SaveLayerFlag = kDontClipToLayer_PrivateSaveLayerFlag, +#endif + }; + typedef uint32_t SaveLayerFlags; + + struct SaveLayerRec { + SaveLayerRec() + : fBounds(nullptr), fPaint(nullptr), fBackdrop(nullptr), fSaveLayerFlags(0) + {} + SaveLayerRec(const SkRect* bounds, const SkPaint* paint, SaveLayerFlags saveLayerFlags = 0) + : fBounds(bounds) + , fPaint(paint) + , fBackdrop(nullptr) + , fSaveLayerFlags(saveLayerFlags) + {} + SaveLayerRec(const SkRect* bounds, const SkPaint* paint, const SkImageFilter* backdrop, + SaveLayerFlags saveLayerFlags) + : fBounds(bounds) + , fPaint(paint) + , fBackdrop(backdrop) + , fSaveLayerFlags(saveLayerFlags) + {} + + const SkRect* fBounds; // optional + const SkPaint* fPaint; // optional + const SkImageFilter* fBackdrop; // optional + SaveLayerFlags fSaveLayerFlags; + }; + + int saveLayer(const SaveLayerRec&); /** This call balances a previous call to save(), and is used to remove all modifications to the matrix/clip/drawFilter state since the last save @@ -606,8 +669,8 @@ public: * This makes the contents of the canvas undefined. Subsequent calls that * require reading the canvas contents will produce undefined results. Examples * include blending and readPixels. The actual implementation is backend- - * dependent and one legal implementation is to do nothing. Like clear(), this - * ignores the clip. + * dependent and one legal implementation is to do nothing. This method + * ignores the current clip. * * This function should only be called if the caller intends to subsequently * draw to the canvas. The canvas may do real work at discard() time in order @@ -617,7 +680,7 @@ public: void discard() { this->onDiscard(); } /** - * Fill the entire canvas' bitmap (restricted to the current clip) with the + * Fill the entire canvas (restricted to the current clip) with the * specified paint. * @param paint The paint used to fill the canvas */ @@ -904,19 +967,6 @@ public: void drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst, const SkPaint* paint = NULL); - /** Draw the specified bitmap, with its top/left corner at (x,y), - NOT transformed by the current matrix. Note: if the paint - contains a maskfilter that generates a mask which extends beyond the - bitmap's original width/height, then the bitmap will be drawn as if it - were in a Shader with CLAMP mode. Thus the color outside of the original - width/height will be the edge color replicated. - @param bitmap The bitmap to be drawn - @param left The position of the left side of the bitmap being drawn - @param top The position of the top side of the bitmap being drawn - @param paint The paint used to draw the bitmap, or NULL - */ - void drawSprite(const SkBitmap& bitmap, int left, int top, const SkPaint* paint = NULL); - /** Draw the text, with origin at (x,y), using the specified paint. The origin is interpreted based on the Align setting in the paint. @param text The text to be drawn @@ -1223,14 +1273,15 @@ protected: // Subclass save/restore notifiers. // Overriders should call the corresponding INHERITED method up the inheritance chain. - // willSaveLayer()'s return value may suppress full layer allocation. + // getSaveLayerStrategy()'s return value may suppress full layer allocation. enum SaveLayerStrategy { kFullLayer_SaveLayerStrategy, - kNoLayer_SaveLayerStrategy + kNoLayer_SaveLayerStrategy, }; virtual void willSave() {} - virtual SaveLayerStrategy willSaveLayer(const SkRect*, const SkPaint*, SaveFlags) { + // Overriders should call the corresponding INHERITED method up the inheritance chain. + virtual SaveLayerStrategy getSaveLayerStrategy(const SaveLayerRec&) { return kFullLayer_SaveLayerStrategy; } virtual void willRestore() {} @@ -1285,7 +1336,6 @@ protected: SrcRectConstraint); virtual void onDrawBitmapNine(const SkBitmap&, const SkIRect& center, const SkRect& dst, const SkPaint*); - virtual void onDrawSprite(const SkBitmap&, int left, int top, const SkPaint*); enum ClipEdgeStyle { kHard_ClipEdgeStyle, @@ -1311,11 +1361,21 @@ protected: // returns false if the entire rectangle is entirely clipped out // If non-NULL, The imageFilter parameter will be used to expand the clip // and offscreen bounds for any margin required by the filter DAG. - bool clipRectBounds(const SkRect* bounds, SaveFlags flags, - SkIRect* intersection, + bool clipRectBounds(const SkRect* bounds, SaveLayerFlags, SkIRect* intersection, const SkImageFilter* imageFilter = NULL); +#ifdef SK_SUPPORT_LEGACY_SAVEFLAGS + // Needed by SkiaCanvasProxy in Android. Make sure that class is updated + // before removing this method. + static uint32_t SaveLayerFlagsToSaveFlags(SaveLayerFlags); +#endif private: + static bool BoundsAffectsClip(SaveLayerFlags); +#ifdef SK_SUPPORT_LEGACY_SAVEFLAGS + static uint32_t SaveFlagsToSaveLayerFlags(SaveFlags); +#endif + static SaveLayerFlags LegacySaveFlagsToSaveLayerFlags(uint32_t legacySaveFlags); + enum ShaderOverrideOpacity { kNone_ShaderOverrideOpacity, //!< there is no overriding shader (bitmap or image) kOpaque_ShaderOverrideOpacity, //!< the overriding shader is opaque @@ -1375,6 +1435,7 @@ private: friend class SkNoSaveLayerCanvas; // InitFlags friend class SkPictureImageFilter; // SkCanvas(SkBaseDevice*, SkSurfaceProps*, InitFlags) friend class SkPictureRecord; // predrawNotify (why does it need it? ) + friend class SkPicturePlayback; // SaveFlagsToSaveLayerFlags enum InitFlags { kDefault_InitFlags = 0, @@ -1406,7 +1467,7 @@ private: const SkRect& dst, const SkPaint* paint, SrcRectConstraint); void internalDrawPaint(const SkPaint& paint); - void internalSaveLayer(const SkRect* bounds, const SkPaint*, SaveFlags, SaveLayerStrategy); + void internalSaveLayer(const SaveLayerRec&, SaveLayerStrategy); void internalDrawDevice(SkBaseDevice*, int x, int y, const SkPaint*, bool isBitmapDevice); // shared by save() and saveLayer() @@ -1428,6 +1489,10 @@ private: */ bool wouldOverwriteEntireSurface(const SkRect*, const SkPaint*, ShaderOverrideOpacity) const; + /** + * Returns true if the paint's imagefilter can be invoked directly, without needed a layer. + */ + bool canDrawBitmapAsSprite(SkScalar x, SkScalar y, int w, int h, const SkPaint&); /* These maintain a cache of the clip bounds in local coordinates, (converted to 2s-compliment if floats are slow). @@ -1505,49 +1570,7 @@ private: }; #define SkAutoCanvasRestore(...) SK_REQUIRE_LOCAL_VAR(SkAutoCanvasRestore) -/** - * If the caller wants read-only access to the pixels in a canvas, it can just - * call canvas->peekPixels(), since that is the fastest way to "peek" at the - * pixels on a raster-backed canvas. - * - * If the canvas has pixels, but they are not readily available to the CPU - * (e.g. gpu-backed), then peekPixels() will fail, but readPixels() will - * succeed (though be slower, since it will return a copy of the pixels). - * - * SkAutoROCanvasPixels encapsulates these two techniques, trying first to call - * peekPixels() (for performance), but if that fails, calling readPixels() and - * storing the copy locally. - * - * The caller must respect the restrictions associated with peekPixels(), since - * that may have been called: The returned information is invalidated if... - * - any API is called on the canvas (or its parent surface if present) - * - the canvas goes out of scope - */ -class SkAutoROCanvasPixels : SkNoncopyable { -public: - SkAutoROCanvasPixels(SkCanvas* canvas); - - // returns NULL on failure - const void* addr() const { return fAddr; } - - // undefined if addr() == NULL - size_t rowBytes() const { return fRowBytes; } - - // undefined if addr() == NULL - const SkImageInfo& info() const { return fInfo; } - - // helper that, if returns true, installs the pixels into the bitmap. Note - // that the bitmap may reference the address returned by peekPixels(), so - // the caller must respect the restrictions associated with peekPixels(). - bool asROBitmap(SkBitmap*) const; - -private: - SkBitmap fBitmap; // used if peekPixels() fails - const void* fAddr; // NULL on failure - SkImageInfo fInfo; - size_t fRowBytes; -}; - +#ifdef SK_SUPPORT_LEGACY_SAVEFLAGS static inline SkCanvas::SaveFlags operator|(const SkCanvas::SaveFlags lhs, const SkCanvas::SaveFlags rhs) { return static_cast(static_cast(lhs) | static_cast(rhs)); @@ -1558,6 +1581,7 @@ static inline SkCanvas::SaveFlags& operator|=(SkCanvas::SaveFlags& lhs, lhs = lhs | rhs; return lhs; } +#endif class SkCanvasClipVisitor { public: diff --git a/gfx/skia/skia/include/core/SkColorFilter.h b/gfx/skia/skia/include/core/SkColorFilter.h index 57650fbd98..c5d084a22d 100644 --- a/gfx/skia/skia/include/core/SkColorFilter.h +++ b/gfx/skia/skia/include/core/SkColorFilter.h @@ -106,13 +106,6 @@ public: */ static SkColorFilter* CreateModeFilter(SkColor c, SkXfermode::Mode mode); - /** Create a colorfilter that multiplies the RGB channels by one color, and - then adds a second color, pinning the result for each component to - [0..255]. The alpha components of the mul and add arguments - are ignored. - */ - static SkColorFilter* CreateLightingFilter(SkColor mul, SkColor add); - /** Construct a colorfilter whose effect is to first apply the inner filter and then apply * the outer filter to the result of the inner's. * The reference counts for outer and inner are incremented. diff --git a/gfx/skia/skia/include/core/SkDevice.h b/gfx/skia/skia/include/core/SkDevice.h index c52c579424..65ec56f902 100644 --- a/gfx/skia/skia/include/core/SkDevice.h +++ b/gfx/skia/skia/include/core/SkDevice.h @@ -333,16 +333,26 @@ protected: const SkPaint*); struct CreateInfo { - static SkPixelGeometry AdjustGeometry(const SkImageInfo&, TileUsage, SkPixelGeometry); + static SkPixelGeometry AdjustGeometry(const SkImageInfo&, TileUsage, SkPixelGeometry, + bool preserveLCDText); // The constructor may change the pixel geometry based on other parameters. CreateInfo(const SkImageInfo& info, TileUsage tileUsage, - SkPixelGeometry geo, - bool forImageFilter = false) + SkPixelGeometry geo) : fInfo(info) , fTileUsage(tileUsage) - , fPixelGeometry(AdjustGeometry(info, tileUsage, geo)) + , fPixelGeometry(AdjustGeometry(info, tileUsage, geo, false)) + , fForImageFilter(false) {} + + CreateInfo(const SkImageInfo& info, + TileUsage tileUsage, + SkPixelGeometry geo, + bool preserveLCDText, + bool forImageFilter) + : fInfo(info) + , fTileUsage(tileUsage) + , fPixelGeometry(AdjustGeometry(info, tileUsage, geo, preserveLCDText)) , fForImageFilter(forImageFilter) {} const SkImageInfo fInfo; @@ -374,9 +384,13 @@ private: friend class SkDeviceFilteredPaint; friend class SkImageFilter::DeviceProxy; friend class SkNoPixelsBitmapDevice; - friend class SkSurface_Raster; + /** + * Calls through to drawSprite, processing imagefilter as needed. + */ + void drawBitmapAsSprite(const SkDraw&, const SkBitmap&, int x, int y, const SkPaint&); + // used to change the backend's pixels (and possibly config/rowbytes) // but cannot change the width/height, so there should be no change to // any clip information. diff --git a/gfx/skia/skia/include/core/SkDocument.h b/gfx/skia/skia/include/core/SkDocument.h index 316d15a253..6ee96b9ce3 100644 --- a/gfx/skia/skia/include/core/SkDocument.h +++ b/gfx/skia/skia/include/core/SkDocument.h @@ -16,6 +16,7 @@ #include "SkTime.h" class SkCanvas; +class SkPixelSerializer; class SkWStream; /** SK_ScalarDefaultDPI is 72 DPI. @@ -57,6 +58,24 @@ public: static SkDocument* CreatePDF(SkWStream*, SkScalar dpi = SK_ScalarDefaultRasterDPI); + /** + * @param jpegEncoder For PDF documents, if a jpegEncoder is set, + * use it to encode SkImages and SkBitmaps as [JFIF]JPEGs. + * This feature is deprecated and is only supplied for + * backwards compatability. + * + * The prefered method to create PDFs with JPEG images is + * to use SkImage::NewFromEncoded() and not jpegEncoder. + * Chromium uses NewFromEncoded. + * + * If the encoder is unset, or if jpegEncoder->onEncode() + * returns NULL, fall back on encoding images losslessly + * with Deflate. + */ + static SkDocument* CreatePDF(SkWStream*, + SkScalar dpi, + SkPixelSerializer* jpegEncoder); + /** * Create a PDF-backed document, writing the results into a file. */ diff --git a/gfx/skia/skia/include/core/SkDrawFilter.h b/gfx/skia/skia/include/core/SkDrawFilter.h index 865df5f1e1..2812017b71 100644 --- a/gfx/skia/skia/include/core/SkDrawFilter.h +++ b/gfx/skia/skia/include/core/SkDrawFilter.h @@ -16,6 +16,8 @@ class SkCanvas; class SkPaint; /** + * DEPRECATED - use SkPaintFilterCanvas instead. + * * Right before something is being draw, filter() is called with the * paint. The filter may modify the paint as it wishes, which will then be * used for the actual drawing. Note: this modification only lasts for the diff --git a/gfx/skia/skia/include/core/SkFixed.h b/gfx/skia/skia/include/core/SkFixed.h index fefa718d0f..f6dd3d6002 100644 --- a/gfx/skia/skia/include/core/SkFixed.h +++ b/gfx/skia/skia/include/core/SkFixed.h @@ -86,9 +86,9 @@ typedef int32_t SkFixed; #if defined(SK_SUPPORT_LEGACY_DIVBITS_UB) #define SkFixedDiv(numer, denom) SkDivBits(numer, denom, 16) #else - // TODO(reed): this clamp shouldn't be needed. Use SkToS32(). + // The divide may exceed 32 bits. Clamp to a signed 32 bit result. #define SkFixedDiv(numer, denom) \ - SkTPin(((int64_t)numer << 16) / denom, SK_MinS32, SK_MaxS32) + SkToS32(SkTPin((SkLeftShift((int64_t)numer, 16) / denom), SK_MinS32, SK_MaxS32)) #endif ////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -145,9 +145,9 @@ inline SkFixed SkFixedMul_longlong(SkFixed a, SkFixed b) { typedef int64_t SkFixed3232; // 32.32 -#define SkIntToFixed3232(x) ((SkFixed3232)(x) << 32) +#define SkIntToFixed3232(x) (SkLeftShift((SkFixed3232)(x), 32)) #define SkFixed3232ToInt(x) ((int)((x) >> 32)) -#define SkFixedToFixed3232(x) ((SkFixed3232)(x) << 16) +#define SkFixedToFixed3232(x) (SkLeftShift((SkFixed3232)(x), 16)) #define SkFixed3232ToFixed(x) ((SkFixed)((x) >> 16)) #define SkFloatToFixed3232(x) ((SkFixed3232)((x) * (65536.0f * 65536.0f))) diff --git a/gfx/skia/skia/include/core/SkFlattenable.h b/gfx/skia/skia/include/core/SkFlattenable.h index bccabc18fa..10cba1a2ec 100644 --- a/gfx/skia/skia/include/core/SkFlattenable.h +++ b/gfx/skia/skia/include/core/SkFlattenable.h @@ -49,7 +49,7 @@ class SkPrivateEffectInitializer; #define SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(flattenable) \ private: \ static SkFlattenable* CreateProc(SkReadBuffer&); \ - friend class ::SkPrivateEffectInitializer; \ + friend class SkFlattenable::PrivateInitializer; \ public: \ Factory getFactory() const override { return CreateProc; } @@ -108,6 +108,13 @@ public: */ virtual void flatten(SkWriteBuffer&) const {} +protected: + class PrivateInitializer { + public: + static void InitCore(); + static void InitEffects(); + }; + private: static void InitializeFlattenablesIfNeeded(); diff --git a/gfx/skia/skia/include/core/SkImage.h b/gfx/skia/skia/include/core/SkImage.h index d6b10a8e18..62a9e9cf42 100644 --- a/gfx/skia/skia/include/core/SkImage.h +++ b/gfx/skia/skia/include/core/SkImage.h @@ -267,7 +267,7 @@ public: * attempt to reuse existing encoded data (as returned by refEncoded). * * We defer to the SkPixelSerializer both for vetting existing encoded data - * (useEncodedData) and for encoding the image (encodePixels) when no such data is + * (useEncodedData) and for encoding the image (encode) when no such data is * present or is rejected by the serializer. * * If not specified, we use a default serializer which 1) always accepts existing data @@ -324,28 +324,6 @@ public: */ bool isLazyGenerated() const; - /** - * Apply the specified filter to this image, and return the result as a new image. - * - * if forceResultToOriginalSize is true, then the resulting image will be the same size as the - * src, regardless of the normal output of the filter. - * - * If offset is non-null, it is set to the relative offset needed to draw the resulting image - * in the same logical place as the original. - * - * e.g. - * If the filter makes the result larger by a margin of 4 the output would be: - * result->width() == this->width + 8 - * result->height() == this->height + 8 - * offset.x() == -4 - * offset.y() == -4 - * - * If the filter fails to create a resulting image, null is returned, and the offset parameter - * (if specified) will be undefined. - */ - SkImage* applyFilter(SkImageFilter* filter, SkIPoint* offset, - bool forceResultToOriginalSize) const; - protected: SkImage(int width, int height, uint32_t uniqueID); diff --git a/gfx/skia/skia/include/core/SkImageDecoder.h b/gfx/skia/skia/include/core/SkImageDecoder.h index 30323b59ee..5060a1e599 100644 --- a/gfx/skia/skia/include/core/SkImageDecoder.h +++ b/gfx/skia/skia/include/core/SkImageDecoder.h @@ -16,8 +16,6 @@ #include "SkTRegistry.h" #include "SkTypes.h" -//#define SK_LEGACY_PEEKER - class SkStream; class SkStreamRewindable; @@ -129,18 +127,6 @@ public: */ bool getRequireUnpremultipliedColors() const { return fRequireUnpremultipliedColors; } -#ifdef SK_LEGACY_PEEKER - // Android subclasses SkImageDecoder::Peeker, which has been changed into SkPngChunkReader. - // Temporarily use this class until Android can be updated to directly inherit from - // SkPngChunkReader. - class Peeker : public SkPngChunkReader { - public: - bool readChunk(const char tag[], const void* data, size_t length) final { - return this->peek(tag, data, length); - } - virtual bool peek(const char tag[], const void* data, size_t length) = 0; - }; -#endif SkPngChunkReader* getPeeker() const { return fPeeker; } SkPngChunkReader* setPeeker(SkPngChunkReader*); diff --git a/gfx/skia/skia/include/core/SkImageEncoder.h b/gfx/skia/skia/include/core/SkImageEncoder.h index fc999dcc18..bb3341f836 100644 --- a/gfx/skia/skia/include/core/SkImageEncoder.h +++ b/gfx/skia/skia/include/core/SkImageEncoder.h @@ -12,6 +12,8 @@ #include "SkTRegistry.h" class SkBitmap; +class SkPixelSerializer; +class SkPixmap; class SkData; class SkWStream; @@ -64,11 +66,17 @@ public: Type, int quality); static SkData* EncodeData(const SkBitmap&, Type, int quality); + static SkData* EncodeData(const SkPixmap&, Type, int quality); + static bool EncodeFile(const char file[], const SkBitmap&, Type, int quality); static bool EncodeStream(SkWStream*, const SkBitmap&, Type, int quality); + /** Uses SkImageEncoder to serialize images that are not already + encoded as SkImageEncoder::kPNG_Type images. */ + static SkPixelSerializer* CreatePixelSerializer(); + protected: /** * Encode bitmap 'bm' in the desired format, writing results to diff --git a/gfx/skia/skia/include/core/SkImageFilter.h b/gfx/skia/skia/include/core/SkImageFilter.h index d90df29c8b..8153bacb79 100644 --- a/gfx/skia/skia/include/core/SkImageFilter.h +++ b/gfx/skia/skia/include/core/SkImageFilter.h @@ -42,33 +42,25 @@ public: virtual bool get(const Key& key, SkBitmap* result, SkIPoint* offset) const = 0; virtual void set(const Key& key, const SkBitmap& result, const SkIPoint& offset) = 0; virtual void purge() {} - }; - - enum SizeConstraint { - kExact_SizeConstraint, - kApprox_SizeConstraint, + virtual void purgeByImageFilterId(uint32_t) {} }; class Context { public: - Context(const SkMatrix& ctm, const SkIRect& clipBounds, Cache* cache, - SizeConstraint constraint) + Context(const SkMatrix& ctm, const SkIRect& clipBounds, Cache* cache) : fCTM(ctm) , fClipBounds(clipBounds) , fCache(cache) - , fSizeConstraint(constraint) {} const SkMatrix& ctm() const { return fCTM; } const SkIRect& clipBounds() const { return fClipBounds; } Cache* cache() const { return fCache; } - SizeConstraint sizeConstraint() const { return fSizeConstraint; } private: SkMatrix fCTM; SkIRect fClipBounds; Cache* fCache; - SizeConstraint fSizeConstraint; }; class CropRect { @@ -106,11 +98,17 @@ public: uint32_t fFlags; }; + enum TileUsage { + kPossible_TileUsage, //!< the created device may be drawn tiled + kNever_TileUsage, //!< the created device will never be drawn tiled + }; + class Proxy { public: virtual ~Proxy() {} - virtual SkBaseDevice* createDevice(int width, int height) = 0; + virtual SkBaseDevice* createDevice(int width, int height, + TileUsage usage = kNever_TileUsage) = 0; // Returns true if the proxy handled the filter itself. If this returns // false then the filter's code will be called. @@ -123,7 +121,8 @@ public: public: DeviceProxy(SkBaseDevice* device) : fDevice(device) {} - SkBaseDevice* createDevice(int width, int height) override; + SkBaseDevice* createDevice(int width, int height, + TileUsage usage = kNever_TileUsage) override; // Returns true if the proxy handled the filter itself. If this returns // false then the filter's code will be called. @@ -199,12 +198,7 @@ public: * replaced by the returned colorfilter. i.e. the two effects will affect drawing in the * same way. */ - bool asAColorFilter(SkColorFilter** filterPtr) const { - return this->countInputs() > 0 && - NULL == this->getInput(0) && - !this->affectsTransparentBlack() && - this->isColorFilterNode(filterPtr); - } + bool asAColorFilter(SkColorFilter** filterPtr) const; /** * Returns the number of inputs this filter will accept (some inputs can @@ -240,7 +234,7 @@ public: virtual void computeFastBounds(const SkRect&, SkRect*) const; // Can this filter DAG compute the resulting bounds of an object-space rectangle? - bool canComputeFastBounds() const; + virtual bool canComputeFastBounds() const; /** * If this filter can be represented by another filter + a localMatrix, return that filter, @@ -256,11 +250,6 @@ public: SkImageFilter* input = NULL); #if SK_SUPPORT_GPU - /** - * Wrap the given texture in a texture-backed SkBitmap. - */ - static void WrapTexture(GrTexture* texture, int width, int height, SkBitmap* result); - // Helper function which invokes GPU filter processing on the // input at the specified "index". If the input is null, it leaves // "result" and "offset" untouched, and returns true. If the input @@ -268,7 +257,7 @@ public: // Otherwise, the filter will be processed in software and // uploaded to the GPU. bool filterInputGPU(int index, SkImageFilter::Proxy* proxy, const SkBitmap& src, const Context&, - SkBitmap* result, SkIPoint* offset, bool relaxSizeConstraint = true) const; + SkBitmap* result, SkIPoint* offset) const; #endif SK_TO_STRING_PUREVIRT() @@ -351,6 +340,25 @@ protected: // implementation recursively unions all input bounds, or returns false if // no inputs. virtual bool onFilterBounds(const SkIRect&, const SkMatrix&, SkIRect*) const; + enum MapDirection { + kForward_MapDirection, + kReverse_MapDirection + }; + + /** + * Performs a forwards or reverse mapping of the given rect to accommodate + * this filter's margin requirements. kForward_MapDirection is used to + * determine the destination pixels which would be touched by filtering + * the given given source rect (e.g., given source bitmap bounds, + * determine the optimal bounds of the filtered offscreen bitmap). + * kReverse_MapDirection is used to determine which pixels of the + * input(s) would be required to fill the given destination rect + * (e.g., clip bounds). NOTE: these operations may not be the + * inverse of the other. For example, blurring expands the given rect + * in both forward and reverse directions. Unlike + * onFilterBounds(), this function is non-recursive. + */ + virtual void onFilterNodeBounds(const SkIRect&, const SkMatrix&, SkIRect*, MapDirection) const; // Helper function which invokes filter processing on the input at the // specified "index". If the input is null, it leaves "result" and @@ -358,7 +366,7 @@ protected: // calls filterImage() on that input, and returns true on success. // i.e., return !getInput(index) || getInput(index)->filterImage(...); bool filterInput(int index, Proxy*, const SkBitmap& src, const Context&, - SkBitmap* result, SkIPoint* offset, bool relaxSizeConstraint = true) const; + SkBitmap* result, SkIPoint* offset) const; /** * Return true (and return a ref'd colorfilter) if this node in the DAG is just a @@ -411,12 +419,12 @@ protected: const SkIRect& bounds) const; /** - * Returns true if this filter can cause transparent black pixels to become - * visible (ie., alpha > 0). The default implementation returns false. This - * function is non-recursive, i.e., only queries this filter and not its - * inputs. + * Creates a modified Context for use when recursing up the image filter DAG. + * The clip bounds are adjusted to accommodate any margins that this + * filter requires by calling this node's + * onFilterNodeBounds(..., kReverse_MapDirection). */ - virtual bool affectsTransparentBlack() const; + Context mapContext(const Context& ctx) const; private: friend class SkGraphics; diff --git a/gfx/skia/skia/include/core/SkImageGenerator.h b/gfx/skia/skia/include/core/SkImageGenerator.h index 66db5e4884..86e3053a06 100644 --- a/gfx/skia/skia/include/core/SkImageGenerator.h +++ b/gfx/skia/skia/include/core/SkImageGenerator.h @@ -22,6 +22,12 @@ class SkMatrix; class SkPaint; class SkPicture; +#ifdef SK_SUPPORT_LEGACY_REFENCODEDDATA_NOCTX + #define SK_REFENCODEDDATA_CTXPARAM +#else + #define SK_REFENCODEDDATA_CTXPARAM GrContext* ctx +#endif + /** * Takes ownership of SkImageGenerator. If this method fails for * whatever reason, it will return false and immediatetely delete @@ -64,12 +70,20 @@ public: /** * Return a ref to the encoded (i.e. compressed) representation, - * of this data. + * of this data. If the GrContext is non-null, then the caller is only interested in + * gpu-specific formats, so the impl may return null even if they have encoded data, + * assuming they know it is not suitable for the gpu. * * If non-NULL is returned, the caller is responsible for calling * unref() on the data when it is finished. */ - SkData* refEncodedData() { return this->onRefEncodedData(); } + SkData* refEncodedData(GrContext* ctx = nullptr) { +#ifdef SK_SUPPORT_LEGACY_REFENCODEDDATA_NOCTX + return this->onRefEncodedData(); +#else + return this->onRefEncodedData(ctx); +#endif + } /** * Return the ImageInfo associated with this generator. @@ -230,7 +244,7 @@ public: protected: SkImageGenerator(const SkImageInfo& info); - virtual SkData* onRefEncodedData(); + virtual SkData* onRefEncodedData(SK_REFENCODEDDATA_CTXPARAM); virtual bool onGetPixels(const SkImageInfo& info, void* pixels, size_t rowBytes, SkPMColor ctable[], int* ctableCount); diff --git a/gfx/skia/skia/include/core/SkMask.h b/gfx/skia/skia/include/core/SkMask.h index 7be6aff614..a6d5606478 100644 --- a/gfx/skia/skia/include/core/SkMask.h +++ b/gfx/skia/skia/include/core/SkMask.h @@ -17,6 +17,8 @@ the 3-channel 3D format. These are passed to SkMaskFilter objects. */ struct SkMask { + SkMask() : fImage(nullptr) {} + enum Format { kBW_Format, //!< 1bit per pixel mask (e.g. monochrome) kA8_Format, //!< 8bits per pixel mask (e.g. antialiasing) diff --git a/gfx/skia/skia/include/core/SkMaskFilter.h b/gfx/skia/skia/include/core/SkMaskFilter.h index 1fe1d9adaf..8d3d1caa67 100644 --- a/gfx/skia/skia/include/core/SkMaskFilter.h +++ b/gfx/skia/skia/include/core/SkMaskFilter.h @@ -184,9 +184,7 @@ protected: class NinePatch : ::SkNoncopyable { public: - NinePatch() : fCache(NULL) { - fMask.fImage = NULL; - } + NinePatch() : fCache(nullptr) { } ~NinePatch(); SkMask fMask; // fBounds must have [0,0] in its top-left diff --git a/gfx/skia/skia/include/core/SkMatrix.h b/gfx/skia/skia/include/core/SkMatrix.h index 97a53505ef..0ebe3280e2 100644 --- a/gfx/skia/skia/include/core/SkMatrix.h +++ b/gfx/skia/skia/include/core/SkMatrix.h @@ -635,15 +635,17 @@ public: /** * Calculates the minimum scaling factor of the matrix as computed from the SVD of the upper - * left 2x2. If the matrix has perspective -1 is returned. + * left 2x2. If the max scale factor cannot be computed (for example overflow or perspective) + * -1 is returned. * - * @return minumum scale factor + * @return minimum scale factor */ SkScalar getMinScale() const; /** * Calculates the maximum scaling factor of the matrix as computed from the SVD of the upper - * left 2x2. If the matrix has perspective -1 is returned. + * left 2x2. If the max scale factor cannot be computed (for example overflow or perspective) + * -1 is returned. * * @return maximum scale factor */ @@ -651,10 +653,10 @@ public: /** * Gets both the min and max scale factors. The min scale factor is scaleFactors[0] and the max - * is scaleFactors[1]. If the matrix has perspective false will be returned and scaleFactors - * will be unchanged. + * is scaleFactors[1]. If the min/max scale factors cannot be computed false is returned and the + * values of scaleFactors[] are undefined. */ - bool getMinMaxScales(SkScalar scaleFactors[2]) const; + bool SK_WARN_UNUSED_RESULT getMinMaxScales(SkScalar scaleFactors[2]) const; /** * Attempt to decompose this matrix into a scale-only component and whatever remains, where diff --git a/gfx/skia/skia/include/core/SkOSFile.h b/gfx/skia/skia/include/core/SkOSFile.h index 39a1646458..f977327e25 100644 --- a/gfx/skia/skia/include/core/SkOSFile.h +++ b/gfx/skia/skia/include/core/SkOSFile.h @@ -41,6 +41,7 @@ size_t sk_fwrite(const void* buffer, size_t byteCount, FILE*); char* sk_fgets(char* str, int size, FILE* f); void sk_fflush(FILE*); +void sk_fsync(FILE*); bool sk_fseek(FILE*, size_t); bool sk_fmove(FILE*, long); diff --git a/gfx/skia/skia/include/core/SkPaint.h b/gfx/skia/skia/include/core/SkPaint.h index 2090ed2337..51e8848a00 100644 --- a/gfx/skia/skia/include/core/SkPaint.h +++ b/gfx/skia/skia/include/core/SkPaint.h @@ -631,13 +631,6 @@ public: SkAnnotation* getAnnotation() const { return fAnnotation; } SkAnnotation* setAnnotation(SkAnnotation*); - /** - * Returns true if there is an annotation installed on this paint, and - * the annotation specifics no-drawing. - */ - SK_ATTR_DEPRECATED("use getAnnotation and check for non-null") - bool isNoDrawAnnotation() const { return this->getAnnotation() != NULL; } - /** * Return the paint's SkDrawLooper (if any). Does not affect the looper's * reference count. @@ -1089,12 +1082,11 @@ private: friend class SkCanvas; friend class SkDraw; friend class SkPDFDevice; - friend class GrBitmapTextContext; + friend class GrAtlasTextBlob; friend class GrAtlasTextContext; - friend class GrDistanceFieldTextContext; friend class GrStencilAndCoverTextContext; friend class GrPathRendering; - friend class GrTextContext; + friend class GrTextUtils; friend class GrGLPathRendering; friend class SkScalerContext; friend class SkTextToPathIter; diff --git a/gfx/skia/skia/include/core/SkPath.h b/gfx/skia/skia/include/core/SkPath.h index 33db8ac249..4ce816f92f 100644 --- a/gfx/skia/skia/include/core/SkPath.h +++ b/gfx/skia/skia/include/core/SkPath.h @@ -956,6 +956,14 @@ public: return (Verb) fRawIter.next(pts); } + /** Return what the next verb will be, but do not visit the next segment. + + @return The verb for the next segment + */ + Verb peek() const { + return (Verb) fRawIter.peek(); + } + SkScalar conicWeight() const { return fRawIter.conicWeight(); } diff --git a/gfx/skia/skia/include/core/SkPathMeasure.h b/gfx/skia/skia/include/core/SkPathMeasure.h index f6e606f0c0..7528736ba8 100644 --- a/gfx/skia/skia/include/core/SkPathMeasure.h +++ b/gfx/skia/skia/include/core/SkPathMeasure.h @@ -89,8 +89,12 @@ private: struct Segment { SkScalar fDistance; // total distance up to this point - unsigned fPtIndex : 15; // index into the fPts array + unsigned fPtIndex; // index into the fPts array +#ifdef SK_SUPPORT_LEGACY_PATH_MEASURE_TVALUE unsigned fTValue : 15; +#else + unsigned fTValue : 30; +#endif unsigned fType : 2; SkScalar getScalarT() const; diff --git a/gfx/skia/skia/include/core/SkPathRef.h b/gfx/skia/skia/include/core/SkPathRef.h index 86f55c9bca..6d0ec627e8 100644 --- a/gfx/skia/skia/include/core/SkPathRef.h +++ b/gfx/skia/skia/include/core/SkPathRef.h @@ -124,6 +124,7 @@ public: @return The verb for the current segment */ uint8_t next(SkPoint pts[4]); + uint8_t peek() const; SkScalar conicWeight() const { return *fConicWeights; } diff --git a/gfx/skia/skia/include/core/SkPixelRef.h b/gfx/skia/skia/include/core/SkPixelRef.h index 4591ed82bf..9cdcd85a79 100644 --- a/gfx/skia/skia/include/core/SkPixelRef.h +++ b/gfx/skia/skia/include/core/SkPixelRef.h @@ -225,7 +225,8 @@ public: return this->onGetYUV8Planes(sizes, planes, rowBytes, colorSpace); } - bool readPixels(SkBitmap* dst, const SkIRect* subset = NULL); + /** Populates dst with the pixels of this pixelRef, converting them to colorType. */ + bool readPixels(SkBitmap* dst, SkColorType colorType, const SkIRect* subset = NULL); /** * Makes a deep copy of this PixelRef, respecting the requested config. @@ -299,7 +300,7 @@ protected: * * The base class implementation returns false; */ - virtual bool onReadPixels(SkBitmap* dst, const SkIRect* subsetOrNull); + virtual bool onReadPixels(SkBitmap* dst, SkColorType colorType, const SkIRect* subsetOrNull); // default impl returns NULL. virtual SkData* onRefEncodedData(); diff --git a/gfx/skia/skia/include/core/SkPixelSerializer.h b/gfx/skia/skia/include/core/SkPixelSerializer.h index d089209909..b168f79dd1 100644 --- a/gfx/skia/skia/include/core/SkPixelSerializer.h +++ b/gfx/skia/skia/include/core/SkPixelSerializer.h @@ -9,9 +9,9 @@ #define SkPixelSerializer_DEFINED #include "SkRefCnt.h" +#include "SkPixmap.h" class SkData; -struct SkImageInfo; /** * Interface for serializing pixels, e.g. SkBitmaps in an SkPicture. @@ -32,14 +32,12 @@ public: * Call to get the client's version of encoding these pixels. If it * returns NULL, serialize the raw pixels. */ - SkData* encodePixels(const SkImageInfo& info, const void* pixels, size_t rowBytes) { - return this->onEncodePixels(info, pixels, rowBytes); - } + SkData* encode(const SkPixmap& pixmap) { return this->onEncode(pixmap); } protected: /** * Return true if you want to serialize the encoded data, false if you want - * another version serialized (e.g. the result of encodePixels). + * another version serialized (e.g. the result of this->encode()). */ virtual bool onUseEncodedData(const void* data, size_t len) = 0; @@ -47,6 +45,6 @@ protected: * If you want to encode these pixels, return the encoded data as an SkData * Return null if you want to serialize the raw pixels. */ - virtual SkData* onEncodePixels(const SkImageInfo&, const void* pixels, size_t rowBytes) = 0; + virtual SkData* onEncode(const SkPixmap&) = 0; }; #endif // SkPixelSerializer_DEFINED diff --git a/gfx/skia/skia/include/core/SkPostConfig.h b/gfx/skia/skia/include/core/SkPostConfig.h index d23eddf2e6..387ea5b154 100644 --- a/gfx/skia/skia/include/core/SkPostConfig.h +++ b/gfx/skia/skia/include/core/SkPostConfig.h @@ -300,14 +300,6 @@ # endif #endif -#if defined(SK_BUILD_FOR_WIN) && SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE2 - #define SK_VECTORCALL __vectorcall -#elif defined(SK_CPU_ARM32) - #define SK_VECTORCALL __attribute__((pcs("aapcs-vfp"))) -#else - #define SK_VECTORCALL -#endif - ////////////////////////////////////////////////////////////////////// #if SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE1 diff --git a/gfx/skia/skia/include/core/SkPreConfig.h b/gfx/skia/skia/include/core/SkPreConfig.h index a1a3037b94..c17cc18e11 100644 --- a/gfx/skia/skia/include/core/SkPreConfig.h +++ b/gfx/skia/skia/include/core/SkPreConfig.h @@ -30,7 +30,8 @@ #define SK_BUILD_FOR_ANDROID #elif defined(linux) || defined(__linux) || defined(__FreeBSD__) || \ defined(__OpenBSD__) || defined(__sun) || defined(__NetBSD__) || \ - defined(__DragonFly__) || defined(__GLIBC__) || defined(__GNU__) + defined(__DragonFly__) || defined(__GLIBC__) || defined(__GNU__) || \ + defined(__unix__) #define SK_BUILD_FOR_UNIX #elif TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR #define SK_BUILD_FOR_IOS @@ -192,7 +193,7 @@ // All 64-bit ARM chips have NEON. Many 32-bit ARM chips do too. // TODO: Why don't we want NEON on iOS? -#if !defined(SK_ARM_HAS_NEON) && !defined(SK_BUILD_FOR_IOS) && defined(__ARM_NEON) +#if !defined(SK_ARM_HAS_NEON) && !defined(SK_BUILD_FOR_IOS) && (defined(__ARM_NEON__) || defined(__ARM_NEON)) #define SK_ARM_HAS_NEON #endif diff --git a/gfx/skia/skia/include/core/SkRRect.h b/gfx/skia/skia/include/core/SkRRect.h index 8d8cdbf02c..994edd2cc1 100644 --- a/gfx/skia/skia/include/core/SkRRect.h +++ b/gfx/skia/skia/include/core/SkRRect.h @@ -153,6 +153,12 @@ public: return rr; } + static SkRRect MakeRectXY(const SkRect& rect, SkScalar xRad, SkScalar yRad) { + SkRRect rr; + rr.setRectXY(rect, xRad, yRad); + return rr; + } + /** * Set this RR to match the supplied oval. All x radii will equal half the * width and all y radii will equal half the height. diff --git a/gfx/skia/skia/include/core/SkRect.h b/gfx/skia/skia/include/core/SkRect.h index fe276e6710..4f649b7c73 100644 --- a/gfx/skia/skia/include/core/SkRect.h +++ b/gfx/skia/skia/include/core/SkRect.h @@ -824,24 +824,6 @@ public: SkScalarRoundToInt(fRight), SkScalarRoundToInt(fBottom)); } - /** - * Variant of round() that explicitly performs the rounding step (i.e. floor(x + 0.5)) using - * double instead of SkScalar (float). It does this by calling SkDScalarRoundToInt(), which - * may be slower than calling SkScalarRountToInt(), but gives slightly more accurate results. - * - * e.g. - * SkScalar x = 0.49999997f; - * int ix = SkScalarRoundToInt(x); - * SkASSERT(0 == ix); // <--- fails - * ix = SkDScalarRoundToInt(x); - * SkASSERT(0 == ix); // <--- succeeds - */ - void dround(SkIRect* dst) const { - SkASSERT(dst); - dst->set(SkDScalarRoundToInt(fLeft), SkDScalarRoundToInt(fTop), - SkDScalarRoundToInt(fRight), SkDScalarRoundToInt(fBottom)); - } - /** * Set the dst rectangle by rounding "out" this rectangle, choosing the * SkScalarFloor of top and left, and the SkScalarCeil of right and bottom. diff --git a/gfx/skia/skia/include/core/SkRefCnt.h b/gfx/skia/skia/include/core/SkRefCnt.h index 6c608ac939..7590032968 100644 --- a/gfx/skia/skia/include/core/SkRefCnt.h +++ b/gfx/skia/skia/include/core/SkRefCnt.h @@ -216,7 +216,7 @@ public: void unref() const { if (1 == sk_atomic_fetch_add(&fRefCnt, -1, sk_memory_order_acq_rel)) { SkDEBUGCODE(fRefCnt = 1;) // restore the 1 for our destructor's assert - delete (const Derived*)this; + delete (const Derived*)this; } } void deref() const { this->unref(); } diff --git a/gfx/skia/skia/include/core/SkShader.h b/gfx/skia/skia/include/core/SkShader.h index 34cb648022..60ef280d5e 100644 --- a/gfx/skia/skia/include/core/SkShader.h +++ b/gfx/skia/skia/include/core/SkShader.h @@ -15,6 +15,7 @@ #include "SkPaint.h" #include "../gpu/GrColor.h" +class SkColorFilter; class SkPath; class SkPicture; class SkXfermode; @@ -72,31 +73,14 @@ public: enum Flags { //!< set if all of the colors will be opaque - kOpaqueAlpha_Flag = 0x01, - - //! set if this shader's shadeSpan16() method can be called - kHasSpan16_Flag = 0x02, - - /** Set this bit if the shader's native data type is instrinsically 16 - bit, meaning that calling the 32bit shadeSpan() entry point will - mean the the impl has to up-sample 16bit data into 32bit. Used as a - a means of clearing a dither request if the it will have no effect - */ - kIntrinsicly16_Flag = 0x04, + kOpaqueAlpha_Flag = 1 << 0, /** set if the spans only vary in X (const in Y). e.g. an Nx1 bitmap that is being tiled in Y, or a linear-gradient that varies from left-to-right. This flag specifies this for shadeSpan(). */ - kConstInY32_Flag = 0x08, - - /** same as kConstInY32_Flag, but is set if this is true for shadeSpan16 - which may not always be the case, since shadeSpan16 may be - predithered, which would mean it was not const in Y, even though - the 32bit shadeSpan() would be const. - */ - kConstInY16_Flag = 0x10 + kConstInY32_Flag = 1 << 1, }; /** @@ -136,12 +120,6 @@ public: */ virtual uint32_t getFlags() const { return 0; } - /** - * Return the alpha associated with the data returned by shadeSpan16(). If - * kHasSpan16_Flag is not set, this value is meaningless. - */ - virtual uint8_t getSpan16Alpha() const { return fPaintAlpha; } - /** * Called for each span of the object being drawn. Your subclass should * set the appropriate colors (with premultiplied alpha) that correspond @@ -149,14 +127,12 @@ public: */ virtual void shadeSpan(int x, int y, SkPMColor[], int count) = 0; - typedef void (*ShadeProc)(void* ctx, int x, int y, SkPMColor[], int count); - virtual ShadeProc asAShadeProc(void** ctx); - /** - * Called only for 16bit devices when getFlags() returns - * kOpaqueAlphaFlag | kHasSpan16_Flag + * The const void* ctx is only const because all the implementations are const. + * This can be changed to non-const if a new shade proc needs to change the ctx. */ - virtual void shadeSpan16(int x, int y, uint16_t[], int count); + typedef void (*ShadeProc)(const void* ctx, int x, int y, SkPMColor[], int count); + virtual ShadeProc asAShadeProc(void** ctx); /** * Similar to shadeSpan, but only returns the alpha-channel for a span. @@ -165,14 +141,6 @@ public: */ virtual void shadeSpanAlpha(int x, int y, uint8_t alpha[], int count); - /** - * Helper function that returns true if this shader's shadeSpan16() method - * can be called. - */ - bool canCallShadeSpan16() { - return SkShader::CanCallShadeSpan16(this->getFlags()); - } - // Notification from blitter::blitMask in case we need to see the non-alpha channels virtual void set3DMask(const SkMask*) {} @@ -215,13 +183,6 @@ public: */ virtual size_t contextSize() const; - /** - * Helper to check the flags to know if it is legal to call shadeSpan16() - */ - static bool CanCallShadeSpan16(uint32_t flags) { - return (flags & kHasSpan16_Flag) != 0; - } - /** * Returns true if this shader is just a bitmap, and if not null, returns the bitmap, * localMatrix, and tilemodes. If this is not a bitmap, returns false and ignores the @@ -342,8 +303,23 @@ public: #endif ////////////////////////////////////////////////////////////////////////// - // Factory methods for stock shaders + // Methods to create combinations or variants of shaders + /** + * Return a shader that will apply the specified localMatrix to this shader. + * The specified matrix will be applied before any matrix associated with this shader. + */ + SkShader* newWithLocalMatrix(const SkMatrix&) const; + + /** + * Create a new shader that produces the same colors as invoking this shader and then applying + * the colorfilter. + */ + SkShader* newWithColorFilter(SkColorFilter*) const; + + ////////////////////////////////////////////////////////////////////////// + // Factory methods for stock shaders + /** * Call this to create a new "empty" shader, that will not draw anything. */ @@ -394,14 +370,6 @@ public: const SkMatrix* localMatrix, const SkRect* tile); - /** - * Return a shader that will apply the specified localMatrix to the proxy shader. - * The specified matrix will be applied before any matrix associated with the proxy. - * - * Note: ownership of the proxy is not transferred (though a ref is taken). - */ - static SkShader* CreateLocalMatrixShader(SkShader* proxy, const SkMatrix& localMatrix); - /** * If this shader can be represented by another shader + a localMatrix, return that shader * and, if not NULL, the localMatrix. If not, return NULL and ignore the localMatrix parameter. diff --git a/gfx/skia/skia/include/core/SkStream.h b/gfx/skia/skia/include/core/SkStream.h index 2279f9ff10..4502416fd9 100644 --- a/gfx/skia/skia/include/core/SkStream.h +++ b/gfx/skia/skia/include/core/SkStream.h @@ -65,17 +65,18 @@ public: /** * Attempt to peek at size bytes. - * If this stream supports peeking, and it can peek size bytes, copy size - * bytes into buffer, and return true. - * If the stream does not support peeking, or cannot peek size bytes, - * return false and leave buffer unchanged. + * If this stream supports peeking, copy min(size, peekable bytes) into + * buffer, and return the number of bytes copied. + * If the stream does not support peeking, or cannot peek any bytes, + * return 0 and leave buffer unchanged. * The stream is guaranteed to be in the same visible state after this * call, regardless of success or failure. - * @param buffer Must not be NULL. Destination to copy bytes. + * @param buffer Must not be NULL, and must be at least size bytes. Destination + * to copy bytes. * @param size Number of bytes to copy. - * @return Whether the peek was performed. + * @return The number of bytes peeked/copied. */ - virtual bool peek(void* /* buffer */, size_t /* size */) const { return false; } + virtual size_t peek(void* /*buffer*/, size_t /*size*/) const { return 0; } /** Returns true when all the bytes in the stream have been read. * This may return true early (when there are no more bytes to be read) @@ -325,7 +326,7 @@ public: size_t read(void* buffer, size_t size) override; bool isAtEnd() const override; - bool peek(void* buffer, size_t size) const override; + size_t peek(void* buffer, size_t size) const override; bool rewind() override; SkMemoryStream* duplicate() const override; @@ -359,6 +360,7 @@ public: bool write(const void* buffer, size_t size) override; void flush() override; + void fsync(); size_t bytesWritten() const override; private: diff --git a/gfx/skia/skia/include/core/SkString.h b/gfx/skia/skia/include/core/SkString.h index 9229d808a6..93514f2659 100644 --- a/gfx/skia/skia/include/core/SkString.h +++ b/gfx/skia/skia/include/core/SkString.h @@ -267,7 +267,22 @@ template <> inline void SkTSwap(SkString& a, SkString& b) { a.swap(b); } +enum SkStrSplitMode { + // Strictly return all results. If the input is ",," and the separator is ',' this will return + // an array of three empty strings. + kStrict_SkStrSplitMode, + + // Only nonempty results will be added to the results. Multiple separators will be + // coalesced. Separators at the beginning and end of the input will be ignored. If the input is + // ",," and the separator is ',', this will return an empty vector. + kCoalesce_SkStrSplitMode +}; + // Split str on any characters in delimiters into out. (Think, strtok with a sane API.) -void SkStrSplit(const char* str, const char* delimiters, SkTArray* out); +void SkStrSplit(const char* str, const char* delimiters, SkStrSplitMode splitMode, + SkTArray* out); +inline void SkStrSplit(const char* str, const char* delimiters, SkTArray* out) { + SkStrSplit(str, delimiters, kCoalesce_SkStrSplitMode, out); +} #endif diff --git a/gfx/skia/skia/include/core/SkTArray.h b/gfx/skia/skia/include/core/SkTArray.h index 401f7084d6..17cbc5c05c 100644 --- a/gfx/skia/skia/include/core/SkTArray.h +++ b/gfx/skia/skia/include/core/SkTArray.h @@ -12,6 +12,7 @@ #include "SkTypes.h" #include +#include template class SkTArray; @@ -23,11 +24,11 @@ inline void copy(SkTArray* self, int dst, int src) { } template inline void copy(SkTArray* self, const T* array) { - memcpy(self->fMemArray, array, self->fCount * sizeof(T)); + sk_careful_memcpy(self->fMemArray, array, self->fCount * sizeof(T)); } template inline void copyAndDelete(SkTArray* self, char* newMemArray) { - memcpy(newMemArray, self->fMemArray, self->fCount * sizeof(T)); + sk_careful_memcpy(newMemArray, self->fMemArray, self->fCount * sizeof(T)); } template @@ -198,7 +199,7 @@ public: */ template T& emplace_back(Args&&... args) { T* newT = reinterpret_cast(this->push_back_raw(1)); - return *new (newT) T(skstd::forward(args)...); + return *new (newT) T(std::forward(args)...); } /** diff --git a/gfx/skia/skia/include/core/SkTDArray.h b/gfx/skia/skia/include/core/SkTDArray.h index 840cb8e577..0b71beed55 100644 --- a/gfx/skia/skia/include/core/SkTDArray.h +++ b/gfx/skia/skia/include/core/SkTDArray.h @@ -45,7 +45,7 @@ public: SkTDArray tmp(src.fArray, src.fCount); this->swap(tmp); } else { - memcpy(fArray, src.fArray, sizeof(T) * src.fCount); + sk_careful_memcpy(fArray, src.fArray, sizeof(T) * src.fCount); fCount = src.fCount; } } diff --git a/gfx/skia/skia/include/core/SkTLazy.h b/gfx/skia/skia/include/core/SkTLazy.h index 60d816147f..8032538c44 100644 --- a/gfx/skia/skia/include/core/SkTLazy.h +++ b/gfx/skia/skia/include/core/SkTLazy.h @@ -11,6 +11,7 @@ #include "../private/SkTemplates.h" #include "SkTypes.h" #include +#include /** * Efficient way to defer allocating/initializing a class until it is needed @@ -50,7 +51,7 @@ public: if (this->isValid()) { fPtr->~T(); } - fPtr = new (SkTCast(fStorage.get())) T(skstd::forward(args)...); + fPtr = new (SkTCast(fStorage.get())) T(std::forward(args)...); return fPtr; } @@ -130,6 +131,8 @@ class SkTCopyOnFirstWrite { public: SkTCopyOnFirstWrite(const T& initial) : fObj(&initial) {} + SkTCopyOnFirstWrite(const T* initial) : fObj(initial) {} + // Constructor for delayed initialization. SkTCopyOnFirstWrite() : fObj(NULL) {} diff --git a/gfx/skia/skia/include/core/SkTime.h b/gfx/skia/skia/include/core/SkTime.h index 2cfe3efcb4..3ff29aa25e 100644 --- a/gfx/skia/skia/include/core/SkTime.h +++ b/gfx/skia/skia/include/core/SkTime.h @@ -38,10 +38,6 @@ public: static double GetNSecs(); }; -#if defined(SK_DEBUG) && defined(SK_BUILD_FOR_WIN32) - extern SkMSec gForceTickCount; -#endif - #define SK_TIME_FACTOR 1 /////////////////////////////////////////////////////////////////////////////// diff --git a/gfx/skia/skia/include/core/SkTypes.h b/gfx/skia/skia/include/core/SkTypes.h index d3f4391d56..c4361ace04 100644 --- a/gfx/skia/skia/include/core/SkTypes.h +++ b/gfx/skia/skia/include/core/SkTypes.h @@ -24,6 +24,28 @@ #include +/** + * sk_careful_memcpy() is just like memcpy(), but guards against undefined behavior. + * + * It is undefined behavior to call memcpy() with null dst or src, even if len is 0. + * If an optimizer is "smart" enough, it can exploit this to do unexpected things. + * memcpy(dst, src, 0); + * if (src) { + * printf("%x\n", *src); + * } + * In this code the compiler can assume src is not null and omit the if (src) {...} check, + * unconditionally running the printf, crashing the program if src really is null. + * Of the compilers we pay attention to only GCC performs this optimization in practice. + */ +static inline void* sk_careful_memcpy(void* dst, const void* src, size_t len) { + // When we pass >0 len we had better already be passing valid pointers. + // So we just need to skip calling memcpy when len == 0. + if (len) { + memcpy(dst,src,len); + } + return dst; +} + /** \file SkTypes.h */ @@ -83,7 +105,10 @@ SK_API extern void* sk_realloc_throw(void* buffer, size_t count, size_t elemSize // bzero is safer than memset, but we can't rely on it, so... sk_bzero() static inline void sk_bzero(void* buffer, size_t size) { - memset(buffer, 0, size); + // Please c.f. sk_careful_memcpy. It's undefined behavior to call memset(null, 0, 0). + if (size) { + memset(buffer, 0, size); + } } /////////////////////////////////////////////////////////////////////////////// @@ -227,12 +252,6 @@ typedef int S16CPU; */ typedef unsigned U16CPU; -/** - * Meant to be faster than bool (doesn't promise to be 0 or 1, - * just 0 or non-zero - */ -typedef int SkBool; - /** * Meant to be a small version of bool, for storage purposes. Will be 0 or 1 */ @@ -262,7 +281,7 @@ typedef uint8_t SkBool8; /** Returns 0 or 1 based on the condition */ -#define SkToBool(cond) ((cond) != 0) +#define SkToBool(cond) (!!(cond)) #define SK_MaxS16 32767 #define SK_MinS16 -32767 @@ -287,6 +306,14 @@ static inline bool SkIsU16(long x) { return (uint16_t)x == x; } +static inline int32_t SkLeftShift(int32_t value, int32_t shift) { + return (int32_t) ((uint32_t) value << shift); +} + +static inline int64_t SkLeftShift(int64_t value, int32_t shift) { + return (int64_t) ((uint64_t) value << shift); +} + ////////////////////////////////////////////////////////////////////////////// /** Returns the number of entries in an array (not a pointer) */ @@ -414,17 +441,6 @@ template static inline const T& SkTPin(const T& value, const T& min return SkTMax(SkTMin(value, max), min); } -static inline uint32_t SkSetClearShift(uint32_t bits, bool cond, - unsigned shift) { - SkASSERT((int)cond == 0 || (int)cond == 1); - return (bits & ~(1 << shift)) | ((int)cond << shift); -} - -static inline uint32_t SkSetClearMask(uint32_t bits, bool cond, - uint32_t mask) { - return cond ? bits | mask : bits & ~mask; -} - /////////////////////////////////////////////////////////////////////////////// /** Use to combine multiple bits in a bitmask in a type safe way. diff --git a/gfx/skia/skia/include/core/SkWriter32.h b/gfx/skia/skia/include/core/SkWriter32.h index 86c27a58dc..1e7ec6d348 100644 --- a/gfx/skia/skia/include/core/SkWriter32.h +++ b/gfx/skia/skia/include/core/SkWriter32.h @@ -165,7 +165,7 @@ public: */ void write(const void* values, size_t size) { SkASSERT(SkAlign4(size) == size); - memcpy(this->reserve(size), values, size); + sk_careful_memcpy(this->reserve(size), values, size); } /** @@ -186,7 +186,7 @@ public: * Write size bytes from src, and pad to 4 byte alignment with zeroes. */ void writePad(const void* src, size_t size) { - memcpy(this->reservePad(size), src, size); + sk_careful_memcpy(this->reservePad(size), src, size); } /** diff --git a/gfx/skia/skia/include/effects/Sk1DPathEffect.h b/gfx/skia/skia/include/effects/Sk1DPathEffect.h index 6a7804f111..3419dc23b7 100644 --- a/gfx/skia/skia/include/effects/Sk1DPathEffect.h +++ b/gfx/skia/skia/include/effects/Sk1DPathEffect.h @@ -56,8 +56,7 @@ public: @param style how to transform path at each point (based on the current position and tangent) */ - static SkPath1DPathEffect* Create(const SkPath& path, SkScalar advance, SkScalar phase, - Style style) { + static SkPathEffect* Create(const SkPath& path, SkScalar advance, SkScalar phase, Style style) { return new SkPath1DPathEffect(path, advance, phase, style); } diff --git a/gfx/skia/skia/include/effects/Sk2DPathEffect.h b/gfx/skia/skia/include/effects/Sk2DPathEffect.h index fa12dcf095..73da83c4b9 100644 --- a/gfx/skia/skia/include/effects/Sk2DPathEffect.h +++ b/gfx/skia/skia/include/effects/Sk2DPathEffect.h @@ -55,7 +55,7 @@ private: class SK_API SkLine2DPathEffect : public Sk2DPathEffect { public: - static SkLine2DPathEffect* Create(SkScalar width, const SkMatrix& matrix) { + static SkPathEffect* Create(SkScalar width, const SkMatrix& matrix) { return new SkLine2DPathEffect(width, matrix); } @@ -84,7 +84,7 @@ public: * Stamp the specified path to fill the shape, using the matrix to define * the latice. */ - static SkPath2DPathEffect* Create(const SkMatrix& matrix, const SkPath& path) { + static SkPathEffect* Create(const SkMatrix& matrix, const SkPath& path) { return new SkPath2DPathEffect(matrix, path); } diff --git a/gfx/skia/skia/include/effects/SkAlphaThresholdFilter.h b/gfx/skia/skia/include/effects/SkAlphaThresholdFilter.h index f409ee08c4..17521b6455 100644 --- a/gfx/skia/skia/include/effects/SkAlphaThresholdFilter.h +++ b/gfx/skia/skia/include/effects/SkAlphaThresholdFilter.h @@ -22,6 +22,7 @@ public: */ static SkImageFilter* Create(const SkRegion& region, SkScalar innerThreshold, SkScalar outerThreshold, SkImageFilter* input = NULL); + SK_DECLARE_FLATTENABLE_REGISTRAR_GROUP(); }; #endif diff --git a/gfx/skia/skia/include/effects/SkBlurDrawLooper.h b/gfx/skia/skia/include/effects/SkBlurDrawLooper.h index 808557b9da..930af15ff8 100644 --- a/gfx/skia/skia/include/effects/SkBlurDrawLooper.h +++ b/gfx/skia/skia/include/effects/SkBlurDrawLooper.h @@ -35,8 +35,8 @@ public: kAll_BlurFlag = 0x07 }; - static SkBlurDrawLooper* Create(SkColor color, SkScalar sigma, SkScalar dx, SkScalar dy, - uint32_t flags = kNone_BlurFlag) { + static SkDrawLooper* Create(SkColor color, SkScalar sigma, SkScalar dx, SkScalar dy, + uint32_t flags = kNone_BlurFlag) { return new SkBlurDrawLooper(color, sigma, dx, dy, flags); } diff --git a/gfx/skia/skia/include/effects/SkBlurImageFilter.h b/gfx/skia/skia/include/effects/SkBlurImageFilter.h index c7193a435f..5ae013d78e 100644 --- a/gfx/skia/skia/include/effects/SkBlurImageFilter.h +++ b/gfx/skia/skia/include/effects/SkBlurImageFilter.h @@ -15,6 +15,9 @@ class SK_API SkBlurImageFilter : public SkImageFilter { public: static SkImageFilter* Create(SkScalar sigmaX, SkScalar sigmaY, SkImageFilter* input = NULL, const CropRect* cropRect = NULL) { + if (0 == sigmaX && 0 == sigmaY && nullptr == cropRect) { + return SkSafeRef(input); + } return new SkBlurImageFilter(sigmaX, sigmaY, input, cropRect); } @@ -27,7 +30,8 @@ protected: void flatten(SkWriteBuffer&) const override; bool onFilterImage(Proxy*, const SkBitmap& src, const Context&, SkBitmap* result, SkIPoint* offset) const override; - bool onFilterBounds(const SkIRect& src, const SkMatrix&, SkIRect* dst) const override; + void onFilterNodeBounds(const SkIRect& src, const SkMatrix&, + SkIRect* dst, MapDirection) const override; bool canFilterImageGPU() const override { return true; } bool filterImageGPU(Proxy* proxy, const SkBitmap& src, const Context& ctx, SkBitmap* result, SkIPoint* offset) const override; diff --git a/gfx/skia/skia/include/effects/SkColorCubeFilter.h b/gfx/skia/skia/include/effects/SkColorCubeFilter.h index f4a0c9009d..a8a5e329de 100644 --- a/gfx/skia/skia/include/effects/SkColorCubeFilter.h +++ b/gfx/skia/skia/include/effects/SkColorCubeFilter.h @@ -11,6 +11,7 @@ #include "SkColorFilter.h" #include "SkData.h" #include "../private/SkMutex.h" +#include "../private/SkTemplates.h" class SK_API SkColorCubeFilter : public SkColorFilter { public: @@ -55,7 +56,7 @@ private: SkScalar* fColorToFactors[2]; SkScalar* fColorToScalar; - SkAutoMalloc fLutStorage; + SkAutoTMalloc fLutStorage; const int fCubeDimension; diff --git a/gfx/skia/skia/include/effects/SkColorFilterImageFilter.h b/gfx/skia/skia/include/effects/SkColorFilterImageFilter.h index 200ec86500..db5d842c2f 100644 --- a/gfx/skia/skia/include/effects/SkColorFilterImageFilter.h +++ b/gfx/skia/skia/include/effects/SkColorFilterImageFilter.h @@ -25,7 +25,7 @@ protected: bool onFilterImage(Proxy*, const SkBitmap& src, const Context&, SkBitmap* result, SkIPoint* loc) const override; bool onIsColorFilterNode(SkColorFilter**) const override; - bool affectsTransparentBlack() const override; + bool canComputeFastBounds() const override; private: SkColorFilterImageFilter(SkColorFilter* cf, diff --git a/gfx/skia/skia/include/effects/SkColorMatrixFilter.h b/gfx/skia/skia/include/effects/SkColorMatrixFilter.h index bd2b939a43..7ffbf117cb 100644 --- a/gfx/skia/skia/include/effects/SkColorMatrixFilter.h +++ b/gfx/skia/skia/include/effects/SkColorMatrixFilter.h @@ -13,13 +13,21 @@ class SK_API SkColorMatrixFilter : public SkColorFilter { public: - static SkColorMatrixFilter* Create(const SkColorMatrix& cm) { + static SkColorFilter* Create(const SkColorMatrix& cm) { return new SkColorMatrixFilter(cm); } - static SkColorMatrixFilter* Create(const SkScalar array[20]) { + static SkColorFilter* Create(const SkScalar array[20]) { return new SkColorMatrixFilter(array); } + /** + * Create a colorfilter that multiplies the RGB channels by one color, and + * then adds a second color, pinning the result for each component to + * [0..255]. The alpha components of the mul and add arguments + * are ignored. + */ + static SkColorFilter* CreateLightingFilter(SkColor mul, SkColor add); + void filterSpan(const SkPMColor src[], int count, SkPMColor[]) const override; uint32_t getFlags() const override; bool asColorMatrix(SkScalar matrix[20]) const override; @@ -29,11 +37,6 @@ public: const GrFragmentProcessor* asFragmentProcessor(GrContext*) const override; #endif - struct State { - int32_t fArray[20]; - int fShift; - }; - SK_TO_STRING_OVERRIDE() SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkColorMatrixFilter) @@ -46,13 +49,7 @@ protected: private: SkColorMatrix fMatrix; float fTranspose[SkColorMatrix::kCount]; // for Sk4s - - typedef void (*Proc)(const State&, unsigned r, unsigned g, unsigned b, - unsigned a, int32_t result[4]); - - Proc fProc; - State fState; - uint32_t fFlags; + uint32_t fFlags; void initState(const SkScalar array[20]); diff --git a/gfx/skia/skia/include/effects/SkCornerPathEffect.h b/gfx/skia/skia/include/effects/SkCornerPathEffect.h index 4c8908575d..13095f0e6c 100644 --- a/gfx/skia/skia/include/effects/SkCornerPathEffect.h +++ b/gfx/skia/skia/include/effects/SkCornerPathEffect.h @@ -20,8 +20,7 @@ public: /** radius must be > 0 to have an effect. It specifies the distance from each corner that should be "rounded". */ - static SkCornerPathEffect* Create(SkScalar radius) { return new SkCornerPathEffect(radius); } - virtual ~SkCornerPathEffect(); + static SkPathEffect* Create(SkScalar radius) { return new SkCornerPathEffect(radius); } virtual bool filterPath(SkPath* dst, const SkPath& src, SkStrokeRec*, const SkRect*) const override; @@ -34,6 +33,8 @@ public: #endif protected: + virtual ~SkCornerPathEffect(); + explicit SkCornerPathEffect(SkScalar radius); void flatten(SkWriteBuffer&) const override; diff --git a/gfx/skia/skia/include/effects/SkDashPathEffect.h b/gfx/skia/skia/include/effects/SkDashPathEffect.h index d3adeed99a..3c1407b725 100644 --- a/gfx/skia/skia/include/effects/SkDashPathEffect.h +++ b/gfx/skia/skia/include/effects/SkDashPathEffect.h @@ -36,8 +36,7 @@ public: Note: only affects stroked paths. */ - static SkDashPathEffect* Create(const SkScalar intervals[], int count, - SkScalar phase) { + static SkPathEffect* Create(const SkScalar intervals[], int count, SkScalar phase) { return new SkDashPathEffect(intervals, count, phase); } virtual ~SkDashPathEffect(); diff --git a/gfx/skia/skia/include/effects/SkDiscretePathEffect.h b/gfx/skia/skia/include/effects/SkDiscretePathEffect.h index b55dca8644..a49e2d89a7 100644 --- a/gfx/skia/skia/include/effects/SkDiscretePathEffect.h +++ b/gfx/skia/skia/include/effects/SkDiscretePathEffect.h @@ -29,9 +29,7 @@ public: they can pass in a different seedAssist to get a different set of path segments. */ - static SkDiscretePathEffect* Create(SkScalar segLength, - SkScalar deviation, - uint32_t seedAssist=0) { + static SkPathEffect* Create(SkScalar segLength, SkScalar deviation, uint32_t seedAssist = 0) { return new SkDiscretePathEffect(segLength, deviation, seedAssist); } diff --git a/gfx/skia/skia/include/effects/SkDisplacementMapEffect.h b/gfx/skia/skia/include/effects/SkDisplacementMapEffect.h index 9513a54f15..e94461795b 100644 --- a/gfx/skia/skia/include/effects/SkDisplacementMapEffect.h +++ b/gfx/skia/skia/include/effects/SkDisplacementMapEffect.h @@ -23,11 +23,11 @@ public: ~SkDisplacementMapEffect(); - static SkDisplacementMapEffect* Create(ChannelSelectorType xChannelSelector, - ChannelSelectorType yChannelSelector, - SkScalar scale, SkImageFilter* displacement, - SkImageFilter* color = NULL, - const CropRect* cropRect = NULL); + static SkImageFilter* Create(ChannelSelectorType xChannelSelector, + ChannelSelectorType yChannelSelector, + SkScalar scale, SkImageFilter* displacement, + SkImageFilter* color = NULL, + const CropRect* cropRect = NULL); SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkDisplacementMapEffect) @@ -40,6 +40,7 @@ public: virtual bool onFilterBounds(const SkIRect& src, const SkMatrix&, SkIRect* dst) const override; + void onFilterNodeBounds(const SkIRect&, const SkMatrix&, SkIRect*, MapDirection) const override; #if SK_SUPPORT_GPU bool canFilterImageGPU() const override { return true; } diff --git a/gfx/skia/skia/include/effects/SkDropShadowImageFilter.h b/gfx/skia/skia/include/effects/SkDropShadowImageFilter.h index bf4425e925..bff1a42013 100644 --- a/gfx/skia/skia/include/effects/SkDropShadowImageFilter.h +++ b/gfx/skia/skia/include/effects/SkDropShadowImageFilter.h @@ -35,7 +35,8 @@ protected: void flatten(SkWriteBuffer&) const override; bool onFilterImage(Proxy*, const SkBitmap& source, const Context&, SkBitmap* result, SkIPoint* loc) const override; - bool onFilterBounds(const SkIRect& src, const SkMatrix&, SkIRect* dst) const override; + void onFilterNodeBounds(const SkIRect& src, const SkMatrix&, + SkIRect* dst, MapDirection) const override; private: SkDropShadowImageFilter(SkScalar dx, SkScalar dy, SkScalar sigmaX, SkScalar sigmaY, SkColor, diff --git a/gfx/skia/skia/include/effects/SkEmbossMaskFilter.h b/gfx/skia/skia/include/effects/SkEmbossMaskFilter.h index 41dfa2a66c..72020bf3ce 100644 --- a/gfx/skia/skia/include/effects/SkEmbossMaskFilter.h +++ b/gfx/skia/skia/include/effects/SkEmbossMaskFilter.h @@ -23,14 +23,14 @@ public: uint8_t fSpecular; // exponent, 4.4 right now }; - static SkEmbossMaskFilter* Create(SkScalar blurSigma, const Light& light); + static SkMaskFilter* Create(SkScalar blurSigma, const Light& light); // overrides from SkMaskFilter // This method is not exported to java. SkMask::Format getFormat() const override; // This method is not exported to java. - virtual bool filterMask(SkMask* dst, const SkMask& src, const SkMatrix&, - SkIPoint* margin) const override; + bool filterMask(SkMask* dst, const SkMask& src, const SkMatrix&, + SkIPoint* margin) const override; SK_TO_STRING_OVERRIDE() SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkEmbossMaskFilter) diff --git a/gfx/skia/skia/include/effects/SkLightingImageFilter.h b/gfx/skia/skia/include/effects/SkLightingImageFilter.h index fb356c52e4..33cfceccb2 100644 --- a/gfx/skia/skia/include/effects/SkLightingImageFilter.h +++ b/gfx/skia/skia/include/effects/SkLightingImageFilter.h @@ -49,7 +49,7 @@ protected: void flatten(SkWriteBuffer&) const override; const SkImageFilterLight* light() const { return fLight.get(); } SkScalar surfaceScale() const { return fSurfaceScale; } - bool affectsTransparentBlack() const override { return true; } + bool canComputeFastBounds() const override { return false; } private: typedef SkImageFilter INHERITED; diff --git a/gfx/skia/skia/include/effects/SkMatrixConvolutionImageFilter.h b/gfx/skia/skia/include/effects/SkMatrixConvolutionImageFilter.h index 76349f430c..7a2026c86b 100644 --- a/gfx/skia/skia/include/effects/SkMatrixConvolutionImageFilter.h +++ b/gfx/skia/skia/include/effects/SkMatrixConvolutionImageFilter.h @@ -52,15 +52,15 @@ public: passed to filterImage() is used instead. @param cropRect The rectangle to which the output processing will be limited. */ - static SkMatrixConvolutionImageFilter* Create(const SkISize& kernelSize, - const SkScalar* kernel, - SkScalar gain, - SkScalar bias, - const SkIPoint& kernelOffset, - TileMode tileMode, - bool convolveAlpha, - SkImageFilter* input = NULL, - const CropRect* cropRect = NULL); + static SkImageFilter* Create(const SkISize& kernelSize, + const SkScalar* kernel, + SkScalar gain, + SkScalar bias, + const SkIPoint& kernelOffset, + TileMode tileMode, + bool convolveAlpha, + SkImageFilter* input = NULL, + const CropRect* cropRect = NULL); SK_TO_STRING_OVERRIDE() SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkMatrixConvolutionImageFilter) @@ -79,8 +79,8 @@ protected: bool onFilterImage(Proxy*, const SkBitmap& src, const Context&, SkBitmap* result, SkIPoint* loc) const override; - bool onFilterBounds(const SkIRect&, const SkMatrix&, SkIRect*) const override; - + void onFilterNodeBounds(const SkIRect&, const SkMatrix&, SkIRect*, MapDirection) const override; + bool canComputeFastBounds() const override; #if SK_SUPPORT_GPU bool asFragmentProcessor(GrFragmentProcessor**, GrTexture*, const SkMatrix&, diff --git a/gfx/skia/skia/include/effects/SkMorphologyImageFilter.h b/gfx/skia/skia/include/effects/SkMorphologyImageFilter.h index 29728fd9f7..422bc01943 100644 --- a/gfx/skia/skia/include/effects/SkMorphologyImageFilter.h +++ b/gfx/skia/skia/include/effects/SkMorphologyImageFilter.h @@ -16,7 +16,8 @@ class SK_API SkMorphologyImageFilter : public SkImageFilter { public: void computeFastBounds(const SkRect& src, SkRect* dst) const override; - bool onFilterBounds(const SkIRect& src, const SkMatrix& ctm, SkIRect* dst) const override; + void onFilterNodeBounds(const SkIRect& src, const SkMatrix& ctm, + SkIRect* dst, MapDirection) const override; /** * All morphology procs have the same signature: src is the source buffer, dst the diff --git a/gfx/skia/skia/include/effects/SkOffsetImageFilter.h b/gfx/skia/skia/include/effects/SkOffsetImageFilter.h index 40f2ce3374..66b5515b37 100644 --- a/gfx/skia/skia/include/effects/SkOffsetImageFilter.h +++ b/gfx/skia/skia/include/effects/SkOffsetImageFilter.h @@ -30,7 +30,7 @@ protected: void flatten(SkWriteBuffer&) const override; bool onFilterImage(Proxy*, const SkBitmap& src, const Context&, SkBitmap* result, SkIPoint* loc) const override; - bool onFilterBounds(const SkIRect&, const SkMatrix&, SkIRect*) const override; + void onFilterNodeBounds(const SkIRect&, const SkMatrix&, SkIRect*, MapDirection) const override; private: SkOffsetImageFilter(SkScalar dx, SkScalar dy, SkImageFilter* input, const CropRect*); diff --git a/gfx/skia/skia/include/effects/SkPaintImageFilter.h b/gfx/skia/skia/include/effects/SkPaintImageFilter.h new file mode 100644 index 0000000000..eee3630f31 --- /dev/null +++ b/gfx/skia/skia/include/effects/SkPaintImageFilter.h @@ -0,0 +1,45 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkPaintImageFilter_DEFINED +#define SkPaintImageFilter_DEFINED + +#include "SkImageFilter.h" +#include "SkPaint.h" + +class SK_API SkPaintImageFilter : public SkImageFilter { +public: + /** Create a new image filter which fills the given rectangle using the + * given paint. If no rectangle is specified, an output is produced with + * the same bounds as the input primitive (even though the input + * primitive's pixels are not used for processing). + * @param paint Paint to use when filling the rect. + * @param rect Rectangle of output pixels. If NULL or a given crop edge is + * not specified, the source primitive's bounds are used + * instead. + */ + static SkImageFilter* Create(const SkPaint& paint, const CropRect* rect = NULL); + + bool canComputeFastBounds() const override; + + SK_TO_STRING_OVERRIDE() + SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkPaintImageFilter) + +protected: + void flatten(SkWriteBuffer&) const override; + bool onFilterImage(Proxy*, const SkBitmap& src, const Context&, SkBitmap* result, + SkIPoint* loc) const override; + +private: + SkPaintImageFilter(const SkPaint& paint, const CropRect* rect); + + SkPaint fPaint; + + typedef SkImageFilter INHERITED; +}; + +#endif diff --git a/gfx/skia/skia/include/effects/SkPerlinNoiseShader.h b/gfx/skia/skia/include/effects/SkPerlinNoiseShader.h index 0cb1b47a9f..ec7c08c8ea 100644 --- a/gfx/skia/skia/include/effects/SkPerlinNoiseShader.h +++ b/gfx/skia/skia/include/effects/SkPerlinNoiseShader.h @@ -80,7 +80,6 @@ public: virtual ~PerlinNoiseShaderContext(); void shadeSpan(int x, int y, SkPMColor[], int count) override; - void shadeSpan16(int x, int y, uint16_t[], int count) override; private: SkPMColor shade(const SkPoint& point, StitchData& stitchData) const; diff --git a/gfx/skia/skia/include/effects/SkRectShaderImageFilter.h b/gfx/skia/skia/include/effects/SkRectShaderImageFilter.h deleted file mode 100644 index 9798af2d64..0000000000 --- a/gfx/skia/skia/include/effects/SkRectShaderImageFilter.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2013 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#ifndef SkRectShaderImageFilter_DEFINED -#define SkRectShaderImageFilter_DEFINED - -#include "SkImageFilter.h" -#include "SkRect.h" - -class SkShader; - -class SK_API SkRectShaderImageFilter : public SkImageFilter { -public: - /** Create a new image filter which fills the given rectangle with pixels - * produced by the given SkShader. If no rectangle is specified, an output - * is produced with the same bounds as the input primitive (even though - * the input primitive's pixels are not used for processing). - * @param s Shader to call for processing. Cannot be NULL. Will be - * ref'ed by the new image filter. - * @param rect Rectangle of output pixels in which to apply the shader. - * If NULL or a given crop edge is not specified, the source - * primitive's bounds are used instead. - */ - SK_ATTR_DEPRECATED("use Create(SkShader*, const CropRect*)") - static SkImageFilter* Create(SkShader* s, const SkRect& rect); - static SkImageFilter* Create(SkShader* s, const CropRect* rect = NULL); - - bool affectsTransparentBlack() const override; - - SK_TO_STRING_OVERRIDE() - SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkRectShaderImageFilter) - -protected: - virtual ~SkRectShaderImageFilter(); - - void flatten(SkWriteBuffer&) const override; - bool onFilterImage(Proxy*, const SkBitmap& src, const Context&, SkBitmap* result, - SkIPoint* loc) const override; - -private: - SkRectShaderImageFilter(SkShader* s, const CropRect* rect); - - SkShader* fShader; - - typedef SkImageFilter INHERITED; -}; - -#endif diff --git a/gfx/skia/skia/include/effects/SkTileImageFilter.h b/gfx/skia/skia/include/effects/SkTileImageFilter.h index a2a1bb0fe1..ea75a3ec7f 100644 --- a/gfx/skia/skia/include/effects/SkTileImageFilter.h +++ b/gfx/skia/skia/include/effects/SkTileImageFilter.h @@ -25,6 +25,7 @@ public: SkBitmap* dst, SkIPoint* offset) const override; bool onFilterBounds(const SkIRect& src, const SkMatrix&, SkIRect* dst) const override; + void onFilterNodeBounds(const SkIRect&, const SkMatrix&, SkIRect*, MapDirection) const override; void computeFastBounds(const SkRect& src, SkRect* dst) const override; SK_TO_STRING_OVERRIDE() diff --git a/gfx/skia/skia/include/effects/SkXfermodeImageFilter.h b/gfx/skia/skia/include/effects/SkXfermodeImageFilter.h index 57fd288f24..d59b7a2b25 100644 --- a/gfx/skia/skia/include/effects/SkXfermodeImageFilter.h +++ b/gfx/skia/skia/include/effects/SkXfermodeImageFilter.h @@ -23,9 +23,9 @@ class SK_API SkXfermodeImageFilter : public SkImageFilter { public: virtual ~SkXfermodeImageFilter(); - static SkXfermodeImageFilter* Create(SkXfermode* mode, SkImageFilter* background, - SkImageFilter* foreground = NULL, - const CropRect* cropRect = NULL) { + static SkImageFilter* Create(SkXfermode* mode, SkImageFilter* background, + SkImageFilter* foreground = NULL, + const CropRect* cropRect = NULL) { SkImageFilter* inputs[2] = { background, foreground }; return new SkXfermodeImageFilter(mode, inputs, cropRect); } diff --git a/gfx/skia/skia/include/gpu/GrCaps.h b/gfx/skia/skia/include/gpu/GrCaps.h index 6d3b66af21..679777f9dd 100644 --- a/gfx/skia/skia/include/gpu/GrCaps.h +++ b/gfx/skia/skia/include/gpu/GrCaps.h @@ -70,7 +70,7 @@ public: * called. */ const PrecisionInfo& getFloatShaderPrecisionInfo(GrShaderType shaderType, - GrSLPrecision precision) const { + GrSLPrecision precision) const { return fFloatPrecisions[shaderType][precision]; }; @@ -118,11 +118,7 @@ public: bool twoSidedStencilSupport() const { return fTwoSidedStencilSupport; } bool stencilWrapOpsSupport() const { return fStencilWrapOpsSupport; } bool discardRenderTargetSupport() const { return fDiscardRenderTargetSupport; } -#if GR_FORCE_GPU_TRACE_DEBUGGING - bool gpuTracingSupport() const { return true; } -#else bool gpuTracingSupport() const { return fGpuTracingSupport; } -#endif bool compressedTexSubImageSupport() const { return fCompressedTexSubImageSupport; } bool oversizedStencilSupport() const { return fOversizedStencilSupport; } bool textureBarrierSupport() const { return fTextureBarrierSupport; } @@ -194,15 +190,8 @@ public: // Will be 0 if MSAA is not supported int maxSampleCount() const { return fMaxSampleCount; } - bool isConfigRenderable(GrPixelConfig config, bool withMSAA) const { - SkASSERT(kGrPixelConfigCnt > config); - return fConfigRenderSupport[config][withMSAA]; - } - - bool isConfigTexturable(GrPixelConfig config) const { - SkASSERT(kGrPixelConfigCnt > config); - return fConfigTextureSupport[config]; - } + virtual bool isConfigTexturable(GrPixelConfig config) const = 0; + virtual bool isConfigRenderable(GrPixelConfig config, bool withMSAA) const = 0; bool suppressPrints() const { return fSuppressPrints; } @@ -270,10 +259,6 @@ protected: int fMaxTileSize; int fMaxSampleCount; - // The first entry for each config is without msaa and the second is with. - bool fConfigRenderSupport[kGrPixelConfigCnt][2]; - bool fConfigTextureSupport[kGrPixelConfigCnt]; - private: virtual void onApplyOptionsOverrides(const GrContextOptions&) {}; diff --git a/gfx/skia/skia/include/gpu/GrClip.h b/gfx/skia/skia/include/gpu/GrClip.h index 2019692194..cf6e65c976 100644 --- a/gfx/skia/skia/include/gpu/GrClip.h +++ b/gfx/skia/skia/include/gpu/GrClip.h @@ -85,7 +85,7 @@ public: return this->irect() == other.irect(); break; } - + SkFAIL("This should not occur\n"); return false; } diff --git a/gfx/skia/skia/include/gpu/GrConfig.h b/gfx/skia/skia/include/gpu/GrConfig.h index ffacb93911..34666857e5 100644 --- a/gfx/skia/skia/include/gpu/GrConfig.h +++ b/gfx/skia/skia/include/gpu/GrConfig.h @@ -186,11 +186,11 @@ typedef unsigned __int64 uint64_t; #endif /** - * GR_FORCE_GPU_TRACE_DEBUGGING will force gpu tracing/debug markers to be turned on. The trace - * markers will be printed out instead of making the backend calls to push and pop them. + * Enable batch debugging output as json. The enabler of this flag is responsible for making sure + * GrAuditTrail is reset occasionally. + * TODO make this runtime configurable */ -#if !defined(GR_FORCE_GPU_TRACE_DEBUGGING) - #define GR_FORCE_GPU_TRACE_DEBUGGING 0 +#if !defined(GR_BATCH_DEBUGGING_OUTPUT) + #define GR_BATCH_DEBUGGING_OUTPUT 0 #endif - #endif diff --git a/gfx/skia/skia/include/gpu/GrContext.h b/gfx/skia/skia/include/gpu/GrContext.h index 70e7b9f244..b60aea0813 100644 --- a/gfx/skia/skia/include/gpu/GrContext.h +++ b/gfx/skia/skia/include/gpu/GrContext.h @@ -15,9 +15,11 @@ #include "GrRenderTarget.h" #include "GrTextureProvider.h" #include "SkMatrix.h" -#include "../private/SkMutex.h" #include "SkPathEffect.h" #include "SkTypes.h" +#include "../private/GrAuditTrail.h" +#include "../private/GrSingleOwner.h" +#include "../private/SkMutex.h" struct GrBatchAtlasConfig; class GrBatchFontCache; @@ -354,9 +356,13 @@ public: /** Enumerates all cached GPU resources and dumps their memory to traceMemoryDump. */ void dumpMemoryStatistics(SkTraceMemoryDump* traceMemoryDump) const; - /** Draw font cache texture to render target */ - void drawFontCache(const SkRect& rect, GrMaskFormat format, const SkPaint& paint, - GrRenderTarget* target); + /** Get pointer to atlas texture for given mask format */ + GrTexture* getFontAtlasTexture(GrMaskFormat format); + + GrAuditTrail* getAuditTrail() { return &fAuditTrail; } + + /** This is only useful for debug purposes */ + SkDEBUGCODE(GrSingleOwner* debugSingleOwner() const { return &fSingleOwner; } ) private: GrGpu* fGpu; @@ -391,6 +397,11 @@ private: SkMutex fReadPixelsMutex; SkMutex fTestPMConversionsMutex; + // In debug builds we guard against improper thread handling + // This guard is passed to the GrDrawingManager and, from there to all the + // GrDrawContexts. It is also passed to the GrTextureProvider and SkGpuDevice. + mutable GrSingleOwner fSingleOwner; + struct CleanUpData { PFCleanUpFunc fFunc; void* fInfo; @@ -402,6 +413,8 @@ private: SkAutoTDelete fDrawingManager; + GrAuditTrail fAuditTrail; + // TODO: have the CMM use drawContexts and rm this friending friend class GrClipMaskManager; // the CMM is friended just so it can call 'drawingManager' friend class GrDrawingManager; // for access to drawingManager for ProgramUnitTest diff --git a/gfx/skia/skia/include/gpu/GrContextOptions.h b/gfx/skia/skia/include/gpu/GrContextOptions.h index 52173eb7a0..8e6368a389 100644 --- a/gfx/skia/skia/include/gpu/GrContextOptions.h +++ b/gfx/skia/skia/include/gpu/GrContextOptions.h @@ -21,6 +21,8 @@ struct GrContextOptions { , fUseDrawInsteadOfPartialRenderTargetWrite(false) , fImmediateMode(false) , fClipBatchToBounds(false) + , fDrawBatchBounds(false) + , fMaxBatchLookback(-1) , fUseShaderSwizzling(false) {} // EXPERIMENTAL @@ -57,6 +59,14 @@ struct GrContextOptions { verify that the clip bounds are conservative. */ bool fClipBatchToBounds; + /** For debugging purposes draw a wireframe device bounds rect for each GrBatch. The wire + frame rect is draw before the GrBatch in order to visualize batches that draw outside + of their dev bounds. */ + bool fDrawBatchBounds; + + /** For debugging, override the default maximum look-back window for GrBatch combining. */ + int fMaxBatchLookback; + /** Force us to do all swizzling manually in the shader and don't rely on extensions to do swizzling. */ bool fUseShaderSwizzling; diff --git a/gfx/skia/skia/include/gpu/GrDrawContext.h b/gfx/skia/skia/include/gpu/GrDrawContext.h index 82cf362d57..9276e2b21f 100644 --- a/gfx/skia/skia/include/gpu/GrDrawContext.h +++ b/gfx/skia/skia/include/gpu/GrDrawContext.h @@ -12,16 +12,17 @@ #include "GrRenderTarget.h" #include "SkRefCnt.h" #include "SkSurfaceProps.h" +#include "../private/GrSingleOwner.h" +class GrAuditTrail; class GrClip; class GrContext; class GrDrawBatch; +class GrDrawPathBatchBase; class GrDrawingManager; class GrDrawTarget; class GrPaint; class GrPathProcessor; -class GrPathRange; -class GrPathRangeDraw; class GrPipelineBuilder; class GrRenderTarget; class GrStrokeInfo; @@ -63,17 +64,6 @@ public: SkScalar x, SkScalar y, SkDrawFilter*, const SkIRect& clipBounds); - // drawPathsFromRange is thanks to GrStencilAndCoverTextContext - // TODO: remove once path batches can be created external to GrDrawTarget. - void drawPathsFromRange(const GrPipelineBuilder*, - const SkMatrix& viewMatrix, - const SkMatrix& localMatrix, - GrColor color, - GrPathRange* range, - GrPathRangeDraw* draw, - int /*GrPathRendering::FillType*/ fill, - const SkRect& bounds); - /** * Provides a perfomance hint that the render target's contents are allowed * to become undefined. @@ -274,17 +264,32 @@ public: */ void drawBatch(const GrClip&, const GrPaint&, GrDrawBatch*); + /** + * Draws a path batch. This needs to be separate from drawBatch because we install path stencil + * settings late. + * + * TODO: Figure out a better model that allows us to roll this method into drawBatch. + */ + void drawPathBatch(const GrPipelineBuilder&, GrDrawPathBatchBase*); + int width() const { return fRenderTarget->width(); } int height() const { return fRenderTarget->height(); } int numColorSamples() const { return fRenderTarget->numColorSamples(); } + GrRenderTarget* accessRenderTarget() { return fRenderTarget; } + + /////////////////////////////////////////////////////////////////////////////////////////////// + // Functions intended for internal use only. + void internal_drawBatch(const GrPipelineBuilder& pipelineBuilder, GrDrawBatch* batch); + private: - friend class GrAtlasTextContext; // for access to drawBatch + friend class GrAtlasTextBlob; // for access to drawBatch friend class GrDrawingManager; // for ctor SkDEBUGCODE(void validate() const;) - GrDrawContext(GrDrawingManager*, GrRenderTarget*, const SkSurfaceProps* surfaceProps); + GrDrawContext(GrDrawingManager*, GrRenderTarget*, const SkSurfaceProps* surfaceProps, + GrAuditTrail*, GrSingleOwner*); void internalDrawPath(GrPipelineBuilder*, const SkMatrix& viewMatrix, @@ -308,6 +313,10 @@ private: GrTextContext* fTextContext; // lazily gotten from GrContext::DrawingManager SkSurfaceProps fSurfaceProps; + GrAuditTrail* fAuditTrail; + + // In debug builds we guard against improper thread handling + SkDEBUGCODE(mutable GrSingleOwner* fSingleOwner;) }; #endif diff --git a/gfx/skia/skia/include/gpu/GrResourceKey.h b/gfx/skia/skia/include/gpu/GrResourceKey.h index 9958cfc872..ff83a43104 100644 --- a/gfx/skia/skia/include/gpu/GrResourceKey.h +++ b/gfx/skia/skia/include/gpu/GrResourceKey.h @@ -293,11 +293,13 @@ private: #define GR_DECLARE_STATIC_UNIQUE_KEY(name) SK_DECLARE_STATIC_ONCE(name##_once) /** Place inside function where the key is used. */ -#define GR_DEFINE_STATIC_UNIQUE_KEY(name) \ - static GrUniqueKey name; \ - SkOnce(&name##_once, gr_init_static_unique_key_once, &name) +#define GR_DEFINE_STATIC_UNIQUE_KEY(name) \ + static SkAlignedSTStorage<1, GrUniqueKey> name##_storage; \ + SkOnce(&name##_once, gr_init_static_unique_key_once, &name##_storage); \ + static const GrUniqueKey& name = *reinterpret_cast(name##_storage.get()); -static inline void gr_init_static_unique_key_once(GrUniqueKey* key) { +static inline void gr_init_static_unique_key_once(SkAlignedSTStorage<1,GrUniqueKey>* keyStorage) { + GrUniqueKey* key = new (keyStorage->get()) GrUniqueKey; GrUniqueKey::Builder builder(key, GrUniqueKey::GenerateDomain(), 0); } diff --git a/gfx/skia/skia/include/gpu/GrTestUtils.h b/gfx/skia/skia/include/gpu/GrTestUtils.h index 91f36ea2d9..8ffae09ddd 100644 --- a/gfx/skia/skia/include/gpu/GrTestUtils.h +++ b/gfx/skia/skia/include/gpu/GrTestUtils.h @@ -91,6 +91,7 @@ static inline uint8_t GrRandomCoverage(SkRandom* random) { switch (colorMode) { case kZero_CoverageMode: coverage = 0; + break; case kAllOnes_CoverageMode: coverage = 0xff; break; diff --git a/gfx/skia/skia/include/gpu/GrTextureAccess.h b/gfx/skia/skia/include/gpu/GrTextureAccess.h index e3ded34ff8..124a75aabc 100644 --- a/gfx/skia/skia/include/gpu/GrTextureAccess.h +++ b/gfx/skia/skia/include/gpu/GrTextureAccess.h @@ -14,60 +14,33 @@ #include "SkRefCnt.h" #include "SkShader.h" -/** A class representing the swizzle access pattern for a texture. Note that if the texture is - * an alpha-only texture then the alpha channel is substituted for other components. Any mangling - * to handle the r,g,b->a conversions for alpha textures is automatically included in the stage - * key. However, if a GrProcessor uses different swizzles based on its input then it must - * consider that variation in its key-generation. +/** + * Used to represent a texture that is required by a GrProcessor. It holds a GrTexture along with + * an associated GrTextureParams */ class GrTextureAccess : public SkNoncopyable { public: /** - * A default GrTextureAccess must have reset() called on it in a GrProcessor subclass's - * constructor if it will be accessible via GrProcessor::textureAccess(). + * Must be initialized before adding to a GrProcessor's texture access list. */ GrTextureAccess(); - /** - * Uses the default swizzle, "rgba". - */ GrTextureAccess(GrTexture*, const GrTextureParams&); + explicit GrTextureAccess(GrTexture*, GrTextureParams::FilterMode = GrTextureParams::kNone_FilterMode, SkShader::TileMode tileXAndY = SkShader::kClamp_TileMode); - /** - * swizzle must be a string between one and four (inclusive) characters containing only 'r', - * 'g', 'b', and/or 'a'. - */ - GrTextureAccess(GrTexture*, const char* swizzle, const GrTextureParams&); - GrTextureAccess(GrTexture*, - const char* swizzle, - GrTextureParams::FilterMode = GrTextureParams::kNone_FilterMode, - SkShader::TileMode tileXAndY = SkShader::kClamp_TileMode); - void reset(GrTexture*, const GrTextureParams&); void reset(GrTexture*, GrTextureParams::FilterMode = GrTextureParams::kNone_FilterMode, SkShader::TileMode tileXAndY = SkShader::kClamp_TileMode); - void reset(GrTexture*, const char* swizzle, const GrTextureParams&); - void reset(GrTexture*, - const char* swizzle, - GrTextureParams::FilterMode = GrTextureParams::kNone_FilterMode, - SkShader::TileMode tileXAndY = SkShader::kClamp_TileMode); - bool operator== (const GrTextureAccess& other) const { -#ifdef SK_DEBUG - // below assumes all chars in fSwizzle are initialized even if string is < 4 chars long. - SkASSERT(memcmp(fSwizzle, other.fSwizzle, sizeof(fSwizzle)-1) == - strcmp(fSwizzle, other.fSwizzle)); -#endif - return fParams == other.fParams && - (this->getTexture() == other.getTexture()) && - (0 == memcmp(fSwizzle, other.fSwizzle, sizeof(fSwizzle)-1)); + bool operator==(const GrTextureAccess& that) const { + return this->getTexture() == that.getTexture() && fParams == that.fParams; } - bool operator!= (const GrTextureAccess& other) const { return !(*this == other); } + bool operator!=(const GrTextureAccess& other) const { return !(*this == other); } GrTexture* getTexture() const { return fTexture.get(); } @@ -76,26 +49,14 @@ public: */ const GrGpuResourceRef* getProgramTexture() const { return &fTexture; } - /** - * Returns a string representing the swizzle. The string is is null-terminated. - */ - const char* getSwizzle() const { return fSwizzle; } - - /** Returns a mask indicating which components are referenced in the swizzle. The return - is a bitfield of GrColorComponentFlags. */ - uint32_t swizzleMask() const { return fSwizzleMask; } - const GrTextureParams& getParams() const { return fParams; } private: - void setSwizzle(const char*); typedef GrTGpuResourceRef ProgramTexture; ProgramTexture fTexture; GrTextureParams fParams; - uint32_t fSwizzleMask; - char fSwizzle[5]; typedef SkNoncopyable INHERITED; }; diff --git a/gfx/skia/skia/include/gpu/GrTextureProvider.h b/gfx/skia/skia/include/gpu/GrTextureProvider.h index 5635583d80..338dddbaca 100644 --- a/gfx/skia/skia/include/gpu/GrTextureProvider.h +++ b/gfx/skia/skia/include/gpu/GrTextureProvider.h @@ -11,6 +11,8 @@ #include "GrTexture.h" #include "SkImageFilter.h" +class GrSingleOwner; + class SK_API GrTextureProvider { public: /////////////////////////////////////////////////////////////////////////// @@ -42,15 +44,7 @@ public: } /** Finds a texture by unique key. If the texture is found it is ref'ed and returned. */ - GrTexture* findAndRefTextureByUniqueKey(const GrUniqueKey& key) { - GrGpuResource* resource = this->findAndRefResourceByUniqueKey(key); - if (resource) { - GrTexture* texture = static_cast(resource)->asTexture(); - SkASSERT(texture); - return texture; - } - return NULL; - } + GrTexture* findAndRefTextureByUniqueKey(const GrUniqueKey& key); /** * Determines whether a texture is associated with the unique key. If the texture is found it @@ -70,31 +64,6 @@ public: */ GrTexture* createApproxTexture(const GrSurfaceDesc&); - enum SizeConstraint { - kExact_SizeConstraint, - kApprox_SizeConstraint, - }; - - GrTexture* createTexture(const GrSurfaceDesc& desc, SizeConstraint constraint) { - switch (constraint) { - case kExact_SizeConstraint: - return this->createTexture(desc, true); - case kApprox_SizeConstraint: - return this->createApproxTexture(desc); - } - sk_throw(); - return nullptr; - } - - static SizeConstraint FromImageFilter(SkImageFilter::SizeConstraint constraint) { - if (SkImageFilter::kExact_SizeConstraint == constraint) { - return kExact_SizeConstraint; - } else { - SkASSERT(SkImageFilter::kApprox_SizeConstraint == constraint); - return kApprox_SizeConstraint; - } - } - /** Legacy function that no longer should be used. */ enum ScratchTexMatch { kExact_ScratchTexMatch, @@ -134,7 +103,7 @@ public: GrRenderTarget* wrapBackendRenderTarget(const GrBackendRenderTargetDesc& desc); protected: - GrTextureProvider(GrGpu* gpu, GrResourceCache* cache) : fCache(cache), fGpu(gpu) {} + GrTextureProvider(GrGpu* gpu, GrResourceCache* cache, GrSingleOwner* singleOwner); /** * Assigns a unique key to a resource. If the key is associated with another resource that @@ -186,6 +155,9 @@ protected: private: GrResourceCache* fCache; GrGpu* fGpu; + + // In debug builds we guard against improper thread handling + SkDEBUGCODE(mutable GrSingleOwner* fSingleOwner;) }; #endif diff --git a/gfx/skia/skia/include/gpu/GrTypes.h b/gfx/skia/skia/include/gpu/GrTypes.h index cf3773c89e..dbcb9a6583 100644 --- a/gfx/skia/skia/include/gpu/GrTypes.h +++ b/gfx/skia/skia/include/gpu/GrTypes.h @@ -363,26 +363,6 @@ static inline size_t GrBytesPerPixel(GrPixelConfig config) { } } -static inline size_t GrUnpackAlignment(GrPixelConfig config) { - SkASSERT(!GrPixelConfigIsCompressed(config)); - switch (config) { - case kAlpha_8_GrPixelConfig: - return 1; - case kRGB_565_GrPixelConfig: - case kRGBA_4444_GrPixelConfig: - case kAlpha_half_GrPixelConfig: - case kRGBA_half_GrPixelConfig: - return 2; - case kRGBA_8888_GrPixelConfig: - case kBGRA_8888_GrPixelConfig: - case kSRGBA_8888_GrPixelConfig: - case kRGBA_float_GrPixelConfig: - return 4; - default: - return 0; - } -} - static inline bool GrPixelConfigIsOpaque(GrPixelConfig config) { switch (config) { case kETC1_GrPixelConfig: diff --git a/gfx/skia/skia/include/gpu/GrTypesPriv.h b/gfx/skia/skia/include/gpu/GrTypesPriv.h index 6135c14c12..c46e25bd0d 100644 --- a/gfx/skia/skia/include/gpu/GrTypesPriv.h +++ b/gfx/skia/skia/include/gpu/GrTypesPriv.h @@ -12,10 +12,10 @@ #include "SkTArray.h" #include "SkRect.h" -/** - * Types of shader-language-specific boxed variables we can create. (Currently only GrGLShaderVars, - * but should be applicable to other shader languages.) - */ + /** + * Types of shader-language-specific boxed variables we can create. (Currently only GrGLShaderVars, + * but should be applicable to other shader languages.) + */ enum GrSLType { kVoid_GrSLType, kFloat_GrSLType, @@ -26,8 +26,9 @@ enum GrSLType { kMat44f_GrSLType, kSampler2D_GrSLType, kSamplerExternal_GrSLType, + kSampler2DRect_GrSLType, - kLast_GrSLType = kSamplerExternal_GrSLType + kLast_GrSLType = kSampler2DRect_GrSLType }; static const int kGrSLTypeCount = kLast_GrSLType + 1; @@ -64,7 +65,7 @@ static const int kGrSLPrecisionCount = kLast_GrSLPrecision + 1; */ static inline int GrSLTypeVectorCount(GrSLType type) { SkASSERT(type >= 0 && type < static_cast(kGrSLTypeCount)); - static const int kCounts[] = { -1, 1, 2, 3, 4, -1, -1, -1, -1 }; + static const int kCounts[] = { -1, 1, 2, 3, 4, -1, -1, -1, -1, -1 }; return kCounts[type]; GR_STATIC_ASSERT(0 == kVoid_GrSLType); @@ -76,6 +77,7 @@ static inline int GrSLTypeVectorCount(GrSLType type) { GR_STATIC_ASSERT(6 == kMat44f_GrSLType); GR_STATIC_ASSERT(7 == kSampler2D_GrSLType); GR_STATIC_ASSERT(8 == kSamplerExternal_GrSLType); + GR_STATIC_ASSERT(9 == kSampler2DRect_GrSLType); GR_STATIC_ASSERT(SK_ARRAY_COUNT(kCounts) == kGrSLTypeCount); } @@ -105,8 +107,49 @@ static inline bool GrSLTypeIsFloatType(GrSLType type) { GR_STATIC_ASSERT(6 == kMat44f_GrSLType); GR_STATIC_ASSERT(7 == kSampler2D_GrSLType); GR_STATIC_ASSERT(8 == kSamplerExternal_GrSLType); - GR_STATIC_ASSERT(9 == kGrSLTypeCount); + GR_STATIC_ASSERT(9 == kSampler2DRect_GrSLType); + GR_STATIC_ASSERT(10 == kGrSLTypeCount); } + +/** Returns the size in bytes for floating point GrSLTypes. For non floating point type returns 0 */ +static inline size_t GrSLTypeSize(GrSLType type) { + SkASSERT(GrSLTypeIsFloatType(type)); + static const size_t kSizes[] = { + 0, // kVoid_GrSLType + sizeof(float), // kFloat_GrSLType + 2 * sizeof(float), // kVec2f_GrSLType + 3 * sizeof(float), // kVec3f_GrSLType + 4 * sizeof(float), // kVec4f_GrSLType + 9 * sizeof(float), // kMat33f_GrSLType + 16 * sizeof(float), // kMat44f_GrSLType + 0, // kSampler2D_GrSLType + 0, // kSamplerExternal_GrSLType + 0 // kSampler2DRect_GrSLType + }; + return kSizes[type]; + + GR_STATIC_ASSERT(0 == kVoid_GrSLType); + GR_STATIC_ASSERT(1 == kFloat_GrSLType); + GR_STATIC_ASSERT(2 == kVec2f_GrSLType); + GR_STATIC_ASSERT(3 == kVec3f_GrSLType); + GR_STATIC_ASSERT(4 == kVec4f_GrSLType); + GR_STATIC_ASSERT(5 == kMat33f_GrSLType); + GR_STATIC_ASSERT(6 == kMat44f_GrSLType); + GR_STATIC_ASSERT(7 == kSampler2D_GrSLType); + GR_STATIC_ASSERT(8 == kSamplerExternal_GrSLType); + GR_STATIC_ASSERT(9 == kSampler2DRect_GrSLType); + GR_STATIC_ASSERT(10 == kGrSLTypeCount); +} + +static inline bool GrSLTypeIsSamplerType(GrSLType type) { + SkASSERT(type >= 0 && type < static_cast(kGrSLTypeCount)); + return type >= 7 && type <= 9; + + GR_STATIC_ASSERT(7 == kSampler2D_GrSLType); + GR_STATIC_ASSERT(8 == kSamplerExternal_GrSLType); + GR_STATIC_ASSERT(9 == kSampler2DRect_GrSLType); +} + ////////////////////////////////////////////////////////////////////////////// /** @@ -178,6 +221,7 @@ static inline GrSLType GrVertexAttribTypeToSLType(GrVertexAttribType type) { switch (type) { default: SkFAIL("Unsupported type conversion"); + return kVoid_GrSLType; case kUByte_GrVertexAttribType: case kFloat_GrVertexAttribType: return kFloat_GrSLType; @@ -267,6 +311,17 @@ private: SkIRect fRect; }; +/** + * Indicates the transfer direction for a transfer buffer + */ +enum TransferType { + /** Caller intends to use the buffer to transfer data to the GPU */ + kCpuToGpu_TransferType, + /** Caller intends to use the buffer to transfer data from the GPU */ + kGpuToCpu_TransferType +}; + + #ifdef SK_DEBUG // Takes a pointer to a GrCaps, and will suppress prints if required #define GrCapsDebugf(caps, ...) \ diff --git a/gfx/skia/skia/include/gpu/SkGrPixelRef.h b/gfx/skia/skia/include/gpu/SkGrPixelRef.h index c06154db10..b4dbd9d73f 100644 --- a/gfx/skia/skia/include/gpu/SkGrPixelRef.h +++ b/gfx/skia/skia/include/gpu/SkGrPixelRef.h @@ -49,7 +49,7 @@ public: protected: // overrides from SkPixelRef - bool onReadPixels(SkBitmap* dst, const SkIRect* subset) override; + bool onReadPixels(SkBitmap* dst, SkColorType, const SkIRect* subset) override; SkPixelRef* deepCopy(SkColorType, SkColorProfileType, const SkIRect* subset) override; void onNotifyPixelsChanged() override; diff --git a/gfx/skia/skia/include/gpu/effects/GrPorterDuffXferProcessor.h b/gfx/skia/skia/include/gpu/effects/GrPorterDuffXferProcessor.h index be014a6b04..457c6eabc8 100644 --- a/gfx/skia/skia/include/gpu/effects/GrPorterDuffXferProcessor.h +++ b/gfx/skia/skia/include/gpu/effects/GrPorterDuffXferProcessor.h @@ -21,10 +21,16 @@ public: void getInvariantBlendedColor(const GrProcOptInfo& colorPOI, GrXPFactory::InvariantBlendedColor*) const override; + + /** Because src-over is so common we special case it for performance reasons. If this returns + null then the SimpleSrcOverXP() below should be used. */ static GrXferProcessor* CreateSrcOverXferProcessor(const GrCaps& caps, const GrPipelineOptimizations& optimizations, bool hasMixedSamples, const GrXferProcessor::DstTexture*); + /** This XP implements non-LCD src-over using hw blend with no optimizations. It is returned + by reference because it is global and its ref-cnting methods are not thread safe. */ + static const GrXferProcessor& SimpleSrcOverXP(); static inline void SrcOverInvariantBlendedColor( GrColor inputColor, diff --git a/gfx/skia/skia/include/gpu/gl/SkGLContext.h b/gfx/skia/skia/include/gpu/gl/SkGLContext.h index 3420a47973..77fd325dd8 100644 --- a/gfx/skia/skia/include/gpu/gl/SkGLContext.h +++ b/gfx/skia/skia/include/gpu/gl/SkGLContext.h @@ -17,9 +17,9 @@ * This class is intended for Skia's testing needs and not for general * use. */ -class SK_API SkGLContext : public SkRefCnt { +class SK_API SkGLContext : public SkNoncopyable { public: - ~SkGLContext() override; + virtual ~SkGLContext(); bool isValid() const { return NULL != gl(); } @@ -41,6 +41,11 @@ public: virtual GrEGLImage texture2DToEGLImage(GrGLuint /*texID*/) const { return 0; } virtual void destroyEGLImage(GrEGLImage) const {} + /** Used for testing GL_TEXTURE_RECTANGLE integration. */ + GrGLint createTextureRectangle(int width, int height, GrGLenum internalFormat, + GrGLenum externalFormat, GrGLenum externalType, + GrGLvoid* data); + /** * Used for testing EGLImage integration. Takes a EGLImage and wraps it in a * GL_TEXTURE_EXTERNAL_OES. @@ -106,8 +111,6 @@ private: SkAutoTUnref fGL; friend class GLFenceSync; // For onPlatformGetProcAddress. - - typedef SkRefCnt INHERITED; }; /** Creates platform-dependent GL context object diff --git a/gfx/skia/skia/include/gpu/gl/SkNullGLContext.h b/gfx/skia/skia/include/gpu/gl/SkNullGLContext.h index 1f63438264..9e799a9fea 100644 --- a/gfx/skia/skia/include/gpu/gl/SkNullGLContext.h +++ b/gfx/skia/skia/include/gpu/gl/SkNullGLContext.h @@ -14,7 +14,12 @@ class SK_API SkNullGLContext : public SkGLContext { public: ~SkNullGLContext() override; - static SkNullGLContext* Create(GrGLStandard); + static SkNullGLContext* Create(); + // FIXME: remove once Chromium has been updated. + static SkNullGLContext* Create(GrGLStandard forcedAPI) { + SkASSERT(forcedAPI == kNone_GrGLStandard); + (void)forcedAPI; return Create(); + } class ContextState; diff --git a/gfx/skia/skia/include/gpu/gl/angle/SkANGLEGLContext.h b/gfx/skia/skia/include/gpu/gl/angle/SkANGLEGLContext.h index 7858fff964..ea5e877ca2 100644 --- a/gfx/skia/skia/include/gpu/gl/angle/SkANGLEGLContext.h +++ b/gfx/skia/skia/include/gpu/gl/angle/SkANGLEGLContext.h @@ -15,18 +15,25 @@ class SkANGLEGLContext : public SkGLContext { public: ~SkANGLEGLContext() override; - - static SkANGLEGLContext* Create(GrGLStandard forcedGpuAPI, bool useGLBackend) { - if (kGL_GrGLStandard == forcedGpuAPI) { - return NULL; - } - SkANGLEGLContext* ctx = new SkANGLEGLContext(useGLBackend); +#ifdef SK_BUILD_FOR_WIN + static SkANGLEGLContext* CreateDirectX() { + SkANGLEGLContext* ctx = new SkANGLEGLContext(false); if (!ctx->isValid()) { delete ctx; return NULL; } return ctx; } +#endif + static SkANGLEGLContext* CreateOpenGL() { + SkANGLEGLContext* ctx = new SkANGLEGLContext(true); + if (!ctx->isValid()) { + delete ctx; + return NULL; + } + return ctx; + } + GrEGLImage texture2DToEGLImage(GrGLuint texID) const override; void destroyEGLImage(GrEGLImage) const override; GrGLuint eglImageToExternalTexture(GrEGLImage) const override; diff --git a/gfx/skia/skia/include/gpu/gl/command_buffer/SkCommandBufferGLContext.h b/gfx/skia/skia/include/gpu/gl/command_buffer/SkCommandBufferGLContext.h index 7fece3c83c..47f3fd967a 100644 --- a/gfx/skia/skia/include/gpu/gl/command_buffer/SkCommandBufferGLContext.h +++ b/gfx/skia/skia/include/gpu/gl/command_buffer/SkCommandBufferGLContext.h @@ -16,10 +16,7 @@ class SkCommandBufferGLContext : public SkGLContext { public: ~SkCommandBufferGLContext() override; - static SkCommandBufferGLContext* Create(GrGLStandard forcedGpuAPI) { - if (kGL_GrGLStandard == forcedGpuAPI) { - return nullptr; - } + static SkCommandBufferGLContext* Create() { SkCommandBufferGLContext* ctx = new SkCommandBufferGLContext; if (!ctx->isValid()) { delete ctx; diff --git a/gfx/skia/skia/include/pipe/SkGPipe.h b/gfx/skia/skia/include/pipe/SkGPipe.h deleted file mode 100644 index 9446b8159f..0000000000 --- a/gfx/skia/skia/include/pipe/SkGPipe.h +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Copyright 2011 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - - - -#ifndef SkGPipe_DEFINED -#define SkGPipe_DEFINED - -#include "SkFlattenable.h" -#include "SkPicture.h" -#include "SkWriter32.h" - -class SkCanvas; - -// XLib.h might have defined Status already (ugh) -#ifdef Status - #undef Status -#endif - -class SkGPipeReader { -public: - SkGPipeReader(); - SkGPipeReader(SkCanvas* target); - ~SkGPipeReader(); - - enum Status { - kDone_Status, //!< no more data expected from reader - kEOF_Status, //!< need more data from reader - kError_Status, //!< encountered error - kReadAtom_Status//!< finished reading an atom - }; - - enum PlaybackFlags { - kReadAtom_PlaybackFlag = 0x1, //!< playback a single command from the stream - kSilent_PlaybackFlag = 0x2, //!< playback without drawing - }; - - void setCanvas(SkCanvas*); - - /** - * Set a function for decoding bitmaps that have encoded data. - */ - void setBitmapDecoder(SkPicture::InstallPixelRefProc proc) { fProc = proc; } - - // data must be 4-byte aligned - // length must be a multiple of 4 - Status playback(const void* data, size_t length, uint32_t playbackFlags = 0, - size_t* bytesRead = NULL); -private: - SkCanvas* fCanvas; - class SkGPipeState* fState; - SkPicture::InstallPixelRefProc fProc; -}; - -/////////////////////////////////////////////////////////////////////////////// - -class SkGPipeCanvas; - -class SkGPipeController { -public: - SkGPipeController() : fCanvas(NULL) {} - virtual ~SkGPipeController(); - - /** - * Called periodically by the writer, to get a working buffer of RAM to - * write into. The actual size of the block is also returned, and must be - * actual >= minRequest. If NULL is returned, then actual is ignored and - * writing will stop. - * - * The returned block must be 4-byte aligned, and actual must be a - * multiple of 4. - * minRequest will always be a multiple of 4. - */ - virtual void* requestBlock(size_t minRequest, size_t* actual) = 0; - - /** - * This is called each time some atomic portion of the data has been - * written to the block (most recently returned by requestBlock()). - * If bytes == 0, then the writer has finished. - * - * bytes will always be a multiple of 4. - */ - virtual void notifyWritten(size_t bytes) = 0; - virtual int numberOfReaders() const { return 1; } - - /** - * Release resource references that are held in internal caches. - * This must only be called after the pipe has been completely flushed. - */ - void purgeCaches(); - -private: - friend class SkGPipeWriter; - void setCanvas(SkGPipeCanvas*); - - SkGPipeCanvas* fCanvas; -}; - -class SkGPipeWriter { -public: - SkGPipeWriter(); - ~SkGPipeWriter(); - - bool isRecording() const { return SkToBool(fCanvas); } - - enum Flags { - /** - * Tells the writer that the reader will be in a different process, so - * (for example) we cannot put function pointers in the stream. - */ - kCrossProcess_Flag = 1 << 0, - - /** - * Only meaningful if kCrossProcess_Flag is set. Tells the writer that - * in spite of being cross process, it will have shared address space - * with the reader, so the two can share large objects (like SkBitmaps). - */ - kSharedAddressSpace_Flag = 1 << 1, - - /** - * Tells the writer that there will be multiple threads reading the stream - * simultaneously. - */ - kSimultaneousReaders_Flag = 1 << 2, - }; - - SkCanvas* startRecording(SkGPipeController*, uint32_t flags = 0, - uint32_t width = kDefaultRecordingCanvasSize, - uint32_t height = kDefaultRecordingCanvasSize); - - // called in destructor, but can be called sooner once you know there - // should be no more drawing calls made into the recording canvas. - void endRecording(); - - /** - * Tells the writer to commit all recorded draw commands to the - * controller immediately. - * @param detachCurrentBlock Set to true to request that the next draw - * command be recorded in a new block. - */ - void flushRecording(bool detachCurrentBlock); - - /** - * Return the amount of bytes being used for recording. Note that this - * does not include the amount of storage written to the stream, which is - * controlled by the SkGPipeController. - * Currently only returns the amount used for SkBitmaps, since they are - * potentially unbounded (if the client is not calling playback). - */ - size_t storageAllocatedForRecording() const; - - /** - * Attempt to reduce the storage allocated for recording by evicting - * cache resources. - * @param bytesToFree minimum number of bytes that should be attempted to - * be freed. - * @return number of bytes actually freed. - */ - size_t freeMemoryIfPossible(size_t bytesToFree); - -private: - enum { - kDefaultRecordingCanvasSize = 32767, - }; - - SkGPipeCanvas* fCanvas; - SkWriter32 fWriter; -}; - -#endif diff --git a/gfx/skia/skia/include/ports/SkFontMgr.h b/gfx/skia/skia/include/ports/SkFontMgr.h index 8d57986a13..96a8501c48 100644 --- a/gfx/skia/skia/include/ports/SkFontMgr.h +++ b/gfx/skia/skia/include/ports/SkFontMgr.h @@ -43,6 +43,8 @@ public: * The caller must call unref() on the returned object. * Never returns NULL; will return an empty set if the name is not found. * + * Passing |nullptr| as the parameter will return the default system font. + * * It is possible that this will return a style set not accessible from * createStyleSet(int) due to hidden or auto-activated fonts. */ @@ -54,6 +56,9 @@ public: * object. Will never return NULL, as it will return the default font if * no matching font is found. * + * Passing |nullptr| as the parameter for |familyName| will return the + * default system font. + * * It is possible that this will return a style set not accessible from * createStyleSet(int) or matchFamily(const char[]) due to hidden or * auto-activated fonts. @@ -68,6 +73,9 @@ public: * Will return NULL if no family can be found for the character * in the system fallback. * + * Passing |nullptr| as the parameter for |familyName| will return the + * default system font. + * * bcp47[0] is the least significant fallback, bcp47[bcp47Count-1] is the * most significant. If no specified bcp47 codes match, any font with the * requested character will be matched. diff --git a/gfx/skia/skia/include/private/GrAuditTrail.h b/gfx/skia/skia/include/private/GrAuditTrail.h new file mode 100644 index 0000000000..fbaec84bb3 --- /dev/null +++ b/gfx/skia/skia/include/private/GrAuditTrail.h @@ -0,0 +1,116 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef GrAuditTrail_DEFINED +#define GrAuditTrail_DEFINED + +#include "GrConfig.h" +#include "SkRect.h" +#include "SkString.h" +#include "SkTArray.h" + +/* + * GrAuditTrail collects a list of draw ops, detailed information about those ops, and can dump them + * to json. + */ +class GrAuditTrail { +public: + GrAuditTrail() : fUniqueID(0) {} + + class AutoFrame { + public: + AutoFrame(GrAuditTrail* auditTrail, const char* name) + : fAuditTrail(auditTrail) { + if (GR_BATCH_DEBUGGING_OUTPUT) { + fAuditTrail->pushFrame(name); + } + } + + ~AutoFrame() { + if (GR_BATCH_DEBUGGING_OUTPUT) { + fAuditTrail->popFrame(); + } + } + + private: + GrAuditTrail* fAuditTrail; + }; + + void pushFrame(const char* name) { + SkASSERT(GR_BATCH_DEBUGGING_OUTPUT); + Frame* frame = new Frame; + if (fStack.empty()) { + fFrames.emplace_back(frame); + } else { + fStack.back()->fChildren.emplace_back(frame); + } + + frame->fUniqueID = fUniqueID++; + frame->fName = name; + fStack.push_back(frame); + } + + void popFrame() { + SkASSERT(GR_BATCH_DEBUGGING_OUTPUT); + fStack.pop_back(); + } + + void addBatch(const char* name, const SkRect& bounds) { + SkASSERT(GR_BATCH_DEBUGGING_OUTPUT && !fStack.empty()); + Batch* batch = new Batch; + fStack.back()->fChildren.emplace_back(batch); + batch->fName = name; + batch->fBounds = bounds; + } + + SkString toJson() const; + + void reset() { SkASSERT(GR_BATCH_DEBUGGING_OUTPUT && fStack.empty()); fFrames.reset(); } + +private: + // TODO if performance becomes an issue, we can move to using SkVarAlloc + struct Event { + virtual ~Event() {} + virtual SkString toJson() const=0; + + const char* fName; + uint64_t fUniqueID; + }; + + typedef SkTArray, true> FrameArray; + struct Frame : public Event { + SkString toJson() const override; + FrameArray fChildren; + }; + + struct Batch : public Event { + SkString toJson() const override; + SkRect fBounds; + }; + + static void JsonifyTArray(SkString* json, const char* name, const FrameArray& array); + + FrameArray fFrames; + SkTArray fStack; + uint64_t fUniqueID; +}; + +#define GR_AUDIT_TRAIL_INVOKE_GUARD(invoke, ...) \ + if (GR_BATCH_DEBUGGING_OUTPUT) { \ + invoke(__VA_ARGS__); \ + } + +#define GR_AUDIT_TRAIL_AUTO_FRAME(audit_trail, framename) \ + GrAuditTrail::AutoFrame SK_MACRO_APPEND_LINE(auto_frame)(audit_trail, framename); + +#define GR_AUDIT_TRAIL_RESET(audit_trail) \ + GR_AUDIT_TRAIL_INVOKE_GUARD(audit_trail->reset); + +#define GR_AUDIT_TRAIL_ADDBATCH(audit_trail, batchname, bounds) \ + GR_AUDIT_TRAIL_INVOKE_GUARD(audit_trail->addBatch, batchname, bounds); + +#endif diff --git a/gfx/skia/skia/include/private/GrSingleOwner.h b/gfx/skia/skia/include/private/GrSingleOwner.h new file mode 100644 index 0000000000..64e63d3b19 --- /dev/null +++ b/gfx/skia/skia/include/private/GrSingleOwner.h @@ -0,0 +1,55 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef GrSingleOwner_DEFINED +#define GrSingleOwner_DEFINED + +#include "SkTypes.h" + +#ifdef SK_DEBUG +#include "SkMutex.h" +#include "SkThreadID.h" + +// This is a debug tool to verify an object is only being used from one thread at a time. +class GrSingleOwner { +public: + GrSingleOwner() : fOwner(kIllegalThreadID), fReentranceCount(0) {} + + struct AutoEnforce { + AutoEnforce(GrSingleOwner* so) : fSO(so) { fSO->enter(); } + ~AutoEnforce() { fSO->exit(); } + + GrSingleOwner* fSO; + }; + +private: + void enter() { + SkAutoMutexAcquire lock(fMutex); + SkThreadID self = SkGetThreadID(); + SkASSERT(fOwner == self || fOwner == kIllegalThreadID); + fReentranceCount++; + fOwner = self; + } + + void exit() { + SkAutoMutexAcquire lock(fMutex); + SkASSERT(fOwner == SkGetThreadID()); + fReentranceCount--; + if (fReentranceCount == 0) { + fOwner = kIllegalThreadID; + } + } + + SkMutex fMutex; + SkThreadID fOwner; // guarded by fMutex + int fReentranceCount; // guarded by fMutex +}; +#else +class GrSingleOwner {}; // Provide a dummy implementation so we can pass pointers to constructors +#endif + +#endif diff --git a/gfx/skia/skia/include/private/SkFloatingPoint.h b/gfx/skia/skia/include/private/SkFloatingPoint.h index f7ee816b12..225e6ca958 100644 --- a/gfx/skia/skia/include/private/SkFloatingPoint.h +++ b/gfx/skia/skia/include/private/SkFloatingPoint.h @@ -12,7 +12,7 @@ #include "SkTypes.h" -#include +#include #include // For _POSIX_VERSION @@ -95,9 +95,9 @@ static inline float sk_float_copysign(float x, float y) { return (bits << 1) == (0xFF << 24); } #else - #define sk_float_isfinite(x) isfinite(x) - #define sk_float_isnan(x) isnan(x) - #define sk_float_isinf(x) isinf(x) + #define sk_float_isfinite(x) std::isfinite(x) + #define sk_float_isnan(x) std::isnan(x) + #define sk_float_isinf(x) std::isinf(x) #endif #define sk_double_isnan(a) sk_float_isnan(a) diff --git a/gfx/skia/skia/include/private/SkRecords.h b/gfx/skia/skia/include/private/SkRecords.h index b856647aee..ecd73a12d9 100644 --- a/gfx/skia/skia/include/private/SkRecords.h +++ b/gfx/skia/skia/include/private/SkRecords.h @@ -10,6 +10,7 @@ #include "SkCanvas.h" #include "SkDrawable.h" +#include "SkImageFilter.h" #include "SkMatrix.h" #include "SkPath.h" #include "SkPicture.h" @@ -63,7 +64,6 @@ namespace SkRecords { M(DrawTextOnPath) \ M(DrawRRect) \ M(DrawRect) \ - M(DrawSprite) \ M(DrawTextBlob) \ M(DrawAtlas) \ M(DrawVertices) @@ -198,7 +198,8 @@ RECORD(Save, 0); RECORD(SaveLayer, 0, Optional bounds; Optional paint; - SkCanvas::SaveFlags flags); + RefBox backdrop; + SkCanvas::SaveLayerFlags saveLayerFlags); RECORD(SetMatrix, 0, TypedMatrix matrix); @@ -315,11 +316,6 @@ RECORD(DrawRRect, kDraw_Tag, RECORD(DrawRect, kDraw_Tag, SkPaint paint; SkRect rect); -RECORD(DrawSprite, kDraw_Tag|kHasImage_Tag, - Optional paint; - ImmutableBitmap bitmap; - int left; - int top); RECORD(DrawText, kDraw_Tag|kHasText_Tag, SkPaint paint; PODArray text; diff --git a/gfx/skia/skia/include/core/SkTDict.h b/gfx/skia/skia/include/private/SkTDict.h similarity index 100% rename from gfx/skia/skia/include/core/SkTDict.h rename to gfx/skia/skia/include/private/SkTDict.h diff --git a/gfx/skia/skia/include/private/SkTLogic.h b/gfx/skia/skia/include/private/SkTLogic.h index 9c21db6756..f94d13ecf8 100644 --- a/gfx/skia/skia/include/private/SkTLogic.h +++ b/gfx/skia/skia/include/private/SkTLogic.h @@ -5,9 +5,10 @@ * found in the LICENSE file. * * - * This header provides some of the helpers (std::integral_constant) and - * type transformations (std::conditional) which will become available with - * C++11 in the type_traits header. + * This header provides some of the helpers (like std::enable_if_t) which will + * become available with C++14 in the type_traits header (in the skstd + * namespace). This header also provides several Skia specific additions such + * as SK_WHEN and the sknonstd namespace. */ #ifndef SkTLogic_DEFINED @@ -17,150 +18,111 @@ #include #include +#include + +#if SKIA_IMPLEMENTATION +#include +#endif + +#ifdef MOZ_SKIA +#include "mozilla/Move.h" +#include "mozilla/TypeTraits.h" + +#if SKIA_IMPLEMENTATION +#include "mozilla/Function.h" +#endif + +// In libc++, symbols such as std::forward may be defined in std::__1. +// The _LIBCPP_BEGIN_NAMESPACE_STD and _LIBCPP_END_NAMESPACE_STD macros +// will expand to the correct namespace. +#ifdef _LIBCPP_BEGIN_NAMESPACE_STD +#define MOZ_BEGIN_STD_NAMESPACE _LIBCPP_BEGIN_NAMESPACE_STD +#define MOZ_END_STD_NAMESPACE _LIBCPP_END_NAMESPACE_STD +#else +#define MOZ_BEGIN_STD_NAMESPACE namespace std { +#define MOZ_END_STD_NAMESPACE } +#endif + +MOZ_BEGIN_STD_NAMESPACE + using mozilla::Forward; + #define forward Forward +MOZ_END_STD_NAMESPACE + +namespace std { +#if SKIA_IMPLEMENTATION + using mozilla::IntegralConstant; + using mozilla::IsEmpty; + using mozilla::FalseType; + using mozilla::TrueType; + #define integral_constant IntegralConstant + #define is_empty IsEmpty + #define false_type FalseType + #define true_type TrueType + + using mozilla::Function; + #define function Function +#endif +} namespace skstd { -using nullptr_t = decltype(nullptr); +template using bool_constant = mozilla::IntegralConstant; -template struct integral_constant { - static const/*expr*/ T value = v; - using value_type = T; - using type = integral_constant; - //constexpr operator value_type() const noexcept { return value; } - //constexpr value_type operator()() const noexcept { return value; } -}; +template using conditional_t = typename mozilla::Conditional::Type; +template using enable_if_t = typename mozilla::EnableIf::Type; -template using bool_constant = integral_constant; +} -using true_type = bool_constant; -using false_type = bool_constant; +#else /* !MOZ_SKIA */ -template struct conditional { using type = T; }; -template struct conditional { using type = F; }; -template using conditional_t = typename conditional::type; +#include +#include -template struct enable_if { using type = T; }; -template struct enable_if {}; -template using enable_if_t = typename enable_if::type; +namespace skstd { -template struct remove_const { using type = T; }; -template struct remove_const { using type = T; }; -template using remove_const_t = typename remove_const::type; +template using bool_constant = std::integral_constant; -template struct remove_volatile { using type = T; }; -template struct remove_volatile { using type = T; }; -template using remove_volatile_t = typename remove_volatile::type; +template using conditional_t = typename std::conditional::type; +template using enable_if_t = typename std::enable_if::type; -template struct remove_cv { using type = remove_volatile_t>; }; -template using remove_cv_t = typename remove_cv::type; - -template struct remove_reference { using type = T; }; -template struct remove_reference { using type = T; }; -template struct remove_reference { using type = T; }; -template using remove_reference_t = typename remove_reference::type; - -template struct remove_extent { using type = T; }; -template struct remove_extent { using type = T; }; -template struct remove_extent { using type = T;}; -template using remove_extent_t = typename remove_extent::type; - -template struct is_same : false_type {}; -template struct is_same : true_type {}; - -template struct is_void : is_same> {}; - -template struct is_const : false_type {}; -template struct is_const : true_type {}; - -template struct is_volatile : false_type {}; -template struct is_volatile : true_type {}; - -template struct is_pointer_detector : false_type {}; -template struct is_pointer_detector : true_type {}; -template struct is_pointer : is_pointer_detector> {}; - -template struct is_reference : false_type {}; -template struct is_reference : true_type {}; -template struct is_reference : true_type {}; - -template struct is_lvalue_reference : false_type {}; -template struct is_lvalue_reference : true_type {}; - -template struct is_rvalue_reference : false_type {}; -template struct is_rvalue_reference : true_type {}; - -template struct is_class_detector { - using yes_type = uint8_t; - using no_type = uint16_t; - template static yes_type clazz(int U::*); - template static no_type clazz(...); - static const/*expr*/ bool value = sizeof(clazz(0)) == sizeof(yes_type) /*&& !is_union::value*/; -}; -template struct is_class : bool_constant::value> {}; - -template ::value> struct is_empty_detector { - struct Derived : public T { char unused; }; - static const/*expr*/ bool value = sizeof(Derived) == sizeof(char); -}; -template struct is_empty_detector { - static const/*expr*/ bool value = false; -}; -template struct is_empty : bool_constant::value> {}; - -template struct is_array : false_type {}; -template struct is_array : true_type {}; -template struct is_array : true_type {}; +template using remove_const_t = typename std::remove_const::type; +template using remove_volatile_t = typename std::remove_volatile::type; +template using remove_cv_t = typename std::remove_cv::type; +template using remove_pointer_t = typename std::remove_pointer::type; +template using remove_reference_t = typename std::remove_reference::type; +template using remove_extent_t = typename std::remove_extent::type; // template struct is_function< -// R [calling-convention] (Args...[, ...]) [const] [volatile] [&|&&]> : true_type {}; +// R [calling-convention] (Args...[, ...]) [const] [volatile] [&|&&]> : std::true_type {}; // The cv and ref-qualified versions are strange types we're currently avoiding, so not supported. +// These aren't supported in msvc either until vs2015u2. // On all platforms, variadic functions only exist in the c calling convention. -template struct is_function : false_type { }; +// mcvc 2013 introduced __vectorcall, but it wan't until 2015 that it was added to is_function. +template struct is_function : std::false_type {}; #if !defined(SK_BUILD_FOR_WIN) -template struct is_function : true_type {}; +template struct is_function : std::true_type {}; #else +template struct is_function : std::true_type {}; #if defined(_M_IX86) -template struct is_function : true_type {}; -template struct is_function : true_type {}; -template struct is_function : true_type {}; -#if SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE2 -template struct is_function : true_type {}; +template struct is_function : std::true_type {}; +template struct is_function : std::true_type {}; #endif -#else -template struct is_function : true_type {}; -template struct is_function : true_type {}; +#if defined(_MSC_VER) && SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE2 +template struct is_function : std::true_type {}; #endif #endif -template struct is_function : true_type {}; +template struct is_function : std::true_type {}; -template struct add_const { using type = const T; }; -template using add_const_t = typename add_const::type; +template using add_const_t = typename std::add_const::type; +template using add_volatile_t = typename std::add_volatile::type; +template using add_cv_t = typename std::add_cv::type; +template using add_pointer_t = typename std::add_pointer::type; +template using add_lvalue_reference_t = typename std::add_lvalue_reference::type; -template struct add_volatile { using type = volatile T; }; -template using add_volatile_t = typename add_volatile::type; - -template struct add_cv { using type = add_volatile_t>; }; -template using add_cv_t = typename add_cv::type; - -template struct add_pointer { using type = remove_reference_t*; }; -template using add_pointer_t = typename add_pointer::type; - -template ::value> struct add_lvalue_reference_init { using type = T; }; -template struct add_lvalue_reference_init { using type = T&; }; -template struct add_lvalue_reference : add_lvalue_reference_init { }; -template using add_lvalue_reference_t = typename add_lvalue_reference::type; - -template ::value> struct add_rvalue_reference_init { using type = T; }; -template struct add_rvalue_reference_init { using type = T&&; }; -template struct add_rvalue_reference : add_rvalue_reference_init {}; -template using add_rvalue_reference_t = typename add_rvalue_reference::type; - -/* This is 'just' a forward declaration. */ -template add_rvalue_reference_t declval() /*noexcept*/; - -template ::value||is_function::value||is_array::value> +template ::value || is_function::value || std::is_array::value> struct is_convertible_detector { - static const/*expr*/ bool value = is_void::value; + static const/*expr*/ bool value = std::is_void::value; }; template struct is_convertible_detector { using yes_type = uint8_t; @@ -169,22 +131,16 @@ template struct is_convertible_detector { template static void param_convertable_to(To); template - static decltype(param_convertable_to(declval()), yes_type()) convertible(int); + static decltype(param_convertable_to(std::declval()), yes_type()) convertible(int); template static no_type convertible(...); static const/*expr*/ bool value = sizeof(convertible(0)) == sizeof(yes_type); }; +// std::is_convertable is known to be broken (not work with incomplete types) in Android clang NDK. +// This is currently what prevents us from using std::unique_ptr. template struct is_convertible - : bool_constant::value> { }; - -template struct decay { - using U = remove_reference_t; - using type = conditional_t::value, - remove_extent_t*, - conditional_t::value, add_pointer_t, remove_cv_t>>; -}; -template using decay_t = typename decay::type; + : bool_constant::value> {}; } // namespace skstd @@ -196,12 +152,12 @@ namespace sknonstd { // std::experimental::propagate_const already exists for other purposes in TSv2. // These also follow the pattern used by boost. template struct copy_const { - using type = skstd::conditional_t::value, skstd::add_const_t, D>; + using type = skstd::conditional_t::value, skstd::add_const_t, D>; }; template using copy_const_t = typename copy_const::type; template struct copy_volatile { - using type = skstd::conditional_t::value, skstd::add_volatile_t, D>; + using type = skstd::conditional_t::value, skstd::add_volatile_t, D>; }; template using copy_volatile_t = typename copy_volatile::type; @@ -222,6 +178,8 @@ template using same_cv_t = typename same_cv::type } // namespace sknonstd +#endif /* MOZ_SKIA */ + // Just a pithier wrapper for enable_if_t. #define SK_WHEN(condition, T) skstd::enable_if_t diff --git a/gfx/skia/skia/include/core/SkTSearch.h b/gfx/skia/skia/include/private/SkTSearch.h similarity index 100% rename from gfx/skia/skia/include/core/SkTSearch.h rename to gfx/skia/skia/include/private/SkTSearch.h diff --git a/gfx/skia/skia/include/private/SkTemplates.h b/gfx/skia/skia/include/private/SkTemplates.h index 533cb264d1..81e23906e2 100644 --- a/gfx/skia/skia/include/private/SkTemplates.h +++ b/gfx/skia/skia/include/private/SkTemplates.h @@ -14,7 +14,6 @@ #include "SkTLogic.h" #include "SkTypes.h" #include "SkUniquePtr.h" -#include "SkUtility.h" #include #include @@ -43,7 +42,7 @@ template static D* SkTAfter(S* ptr, size_t count = 1) { template static D* SkTAddOffset(S* ptr, size_t byteOffset) { // The intermediate char* has the same cv-ness as D as this produces better error messages. // This relies on the fact that reinterpret_cast can add constness, but cannot remove it. - return reinterpret_cast(reinterpret_cast*>(ptr) + byteOffset); + return reinterpret_cast((char*)(ptr) + byteOffset); } template struct SkFunctionWrapper { @@ -272,9 +271,10 @@ public: } /** Resize the memory area pointed to by the current ptr without preserving contents. */ - void reset(size_t count) { + T* reset(size_t count) { sk_free(fPtr); fPtr = (T*)sk_malloc_flags(count * sizeof(T), SK_MALLOC_THROW); + return fPtr; } T* get() const { return fPtr; } @@ -295,6 +295,13 @@ public: return fPtr[index]; } + /** + * Releases the block back to the heap + */ + void free() { + this->reset(0); + } + /** * Transfer ownership of the ptr to the caller, setting the internal * pointer to NULL. Note that this differs from get(), which also returns diff --git a/gfx/skia/skia/include/private/SkUniquePtr.h b/gfx/skia/skia/include/private/SkUniquePtr.h index 289dc7d344..474fc9ed42 100644 --- a/gfx/skia/skia/include/private/SkUniquePtr.h +++ b/gfx/skia/skia/include/private/SkUniquePtr.h @@ -9,8 +9,24 @@ #define SkUniquePtr_DEFINED #include "SkTLogic.h" -#include "SkUtility.h" +#include +#include +#ifdef MOZ_SKIA +#include "mozilla/UniquePtr.h" + +namespace std { + using mozilla::DefaultDelete; + using mozilla::UniquePtr; +} + +namespace skstd { + using mozilla::DefaultDelete; + using mozilla::UniquePtr; + #define default_delete DefaultDelete + #define unique_ptr UniquePtr +} +#else namespace skstd { template struct default_delete { @@ -47,11 +63,11 @@ public: using deleter_type = D; private: - template + template ::value /*&& !is_final::value*/> struct compressed_base : private B { /*constexpr*/ compressed_base() : B() {} /*constexpr*/ compressed_base(const B& b) : B(b) {} - /*constexpr*/ compressed_base(const B&& b) : B(move(b)) {} + /*constexpr*/ compressed_base(B&& b) : B(std::move(b)) {} /*constexpr*/ B& get() /*noexcept*/ { return *this; } /*constexpr*/ B const& get() const /*noexcept*/ { return *this; } void swap(compressed_base&) /*noexcept*/ { } @@ -61,33 +77,31 @@ private: B fb; /*constexpr*/ compressed_base() : B() {} /*constexpr*/ compressed_base(const B& b) : fb(b) {} - /*constexpr*/ compressed_base(const B&& b) : fb(move(b)) {} + /*constexpr*/ compressed_base(B&& b) : fb(std::move(b)) {} /*constexpr*/ B& get() /*noexcept*/ { return fb; } /*constexpr*/ B const& get() const /*noexcept*/ { return fb; } void swap(compressed_base& that) /*noexcept*/ { SkTSwap(fb, that.fB); } }; - using compressed_deleter_type = compressed_base::value>; - - struct compressed_data : private compressed_deleter_type { + struct compressed_data : private compressed_base { pointer fPtr; - /*constexpr*/ compressed_data() : compressed_deleter_type(), fPtr() {} + /*constexpr*/ compressed_data() : compressed_base(), fPtr() {} /*constexpr*/ compressed_data(const pointer& ptr, const deleter_type& d) - : compressed_deleter_type(d), fPtr(ptr) {} + : compressed_base(d), fPtr(ptr) {} template ::value && is_convertible::value >> /*constexpr*/ compressed_data(U1&& ptr, U2&& d) - : compressed_deleter_type(skstd::forward(d)), fPtr(skstd::forward(ptr)) {} + : compressed_base(std::forward(d)), fPtr(std::forward(ptr)) {} /*constexpr*/ pointer& getPointer() /*noexcept*/ { return fPtr; } /*constexpr*/ pointer const& getPointer() const /*noexcept*/ { return fPtr; } /*constexpr*/ deleter_type& getDeleter() /*noexcept*/ { - return compressed_deleter_type::get(); + return compressed_base::get(); } /*constexpr*/ deleter_type const& getDeleter() const /*noexcept*/ { - return compressed_deleter_type::get(); + return compressed_base::get(); } void swap(compressed_data& that) /*noexcept*/ { - compressed_deleter_type::swap(static_cast(that)); + compressed_base::swap(static_cast>(that)); SkTSwap(fPtr, that.fPtr); } }; @@ -95,38 +109,41 @@ private: public: /*constexpr*/ unique_ptr() /*noexcept*/ : data() { - static_assert(!is_pointer::value, "Deleter is nullptr function pointer!"); + static_assert(!std::is_pointer::value, "Deleter nullptr function pointer!"); } - /*constexpr*/ unique_ptr(skstd::nullptr_t) /*noexcept*/ : unique_ptr() { } + /*constexpr*/ unique_ptr(std::nullptr_t) /*noexcept*/ : unique_ptr() { } explicit unique_ptr(pointer ptr) /*noexcept*/ : data(ptr, deleter_type()) { - static_assert(!is_pointer::value, "Deleter is nullptr function pointer!"); + static_assert(!std::is_pointer::value, "Deleter nullptr function pointer!"); } unique_ptr(pointer ptr, - conditional_t::value, deleter_type,const deleter_type&> d) + conditional_t::value, + deleter_type, const deleter_type&> d) /*noexcept*/ : data(ptr, d) {} unique_ptr(pointer ptr, remove_reference_t&& d) /*noexcept*/ - : data(move(ptr), move(d)) + : data(std::move(ptr), std::move(d)) { - static_assert(!is_reference::value, + static_assert(!std::is_reference::value, "Binding an rvalue reference deleter as an lvalue reference deleter is not allowed."); } unique_ptr(unique_ptr&& that) /*noexcept*/ - : data(that.release(), forward(that.get_deleter())) + : data(that.release(), std::forward(that.get_deleter())) {} template ::pointer, pointer>::value && - !is_array::value && - conditional_t::value, is_same, is_convertible>::value>> + !std::is_array::value && + conditional_t::value, + std::is_same, + is_convertible>::value>> unique_ptr(unique_ptr&& that) /*noexcept*/ - : data(that.release(), forward(that.get_deleter())) + : data(that.release(), std::forward(that.get_deleter())) {} ~unique_ptr() /*noexcept*/ { @@ -139,20 +156,20 @@ public: unique_ptr& operator=(unique_ptr&& that) /*noexcept*/ { reset(that.release()); - get_deleter() = forward(that.get_deleter()); + get_deleter() = std::forward(that.get_deleter()); return *this; } template enable_if_t< is_convertible::pointer, pointer>::value && - !is_array::value, + !std::is_array::value, unique_ptr&> operator=(unique_ptr&& that) /*noexcept*/ { reset(that.release()); - get_deleter() = forward(that.get_deleter()); + get_deleter() = std::forward(that.get_deleter()); return *this; } - unique_ptr& operator=(skstd::nullptr_t) /*noexcept*/ { + unique_ptr& operator=(std::nullptr_t) /*noexcept*/ { reset(); return *this; } @@ -219,11 +236,11 @@ public: using deleter_type = D; private: - template + template ::value /*&& !is_final::value*/> struct compressed_base : private B { /*constexpr*/ compressed_base() : B() {} /*constexpr*/ compressed_base(const B& b) : B(b) {} - /*constexpr*/ compressed_base(const B&& b) : B(move(b)) {} + /*constexpr*/ compressed_base(B&& b) : B(std::move(b)) {} /*constexpr*/ B& get() /*noexcept*/ { return *this; } /*constexpr*/ B const& get() const /*noexcept*/ { return *this; } void swap(compressed_base&) /*noexcept*/ { } @@ -233,33 +250,31 @@ private: B fb; /*constexpr*/ compressed_base() : B() {} /*constexpr*/ compressed_base(const B& b) : fb(b) {} - /*constexpr*/ compressed_base(const B&& b) : fb(move(b)) {} + /*constexpr*/ compressed_base(B&& b) : fb(std::move(b)) {} /*constexpr*/ B& get() /*noexcept*/ { return fb; } /*constexpr*/ B const& get() const /*noexcept*/ { return fb; } void swap(compressed_base& that) /*noexcept*/ { SkTSwap(fb, that.fB); } }; - using compressed_deleter_type = compressed_base::value>; - - struct compressed_data : private compressed_deleter_type { + struct compressed_data : private compressed_base { pointer fPtr; - /*constexpr*/ compressed_data() : compressed_deleter_type(), fPtr() {} + /*constexpr*/ compressed_data() : compressed_base(), fPtr() {} /*constexpr*/ compressed_data(const pointer& ptr, const deleter_type& d) - : compressed_deleter_type(d), fPtr(ptr) {} + : compressed_base(d), fPtr(ptr) {} template ::value && is_convertible::value >> /*constexpr*/ compressed_data(U1&& ptr, U2&& d) - : compressed_deleter_type(skstd::forward(d)), fPtr(skstd::forward(ptr)) {} + : compressed_base(std::forward(d)), fPtr(std::forward(ptr)) {} /*constexpr*/ pointer& getPointer() /*noexcept*/ { return fPtr; } /*constexpr*/ pointer const& getPointer() const /*noexcept*/ { return fPtr; } /*constexpr*/ deleter_type& getDeleter() /*noexcept*/ { - return compressed_deleter_type::get(); + return compressed_base::get(); } /*constexpr*/ deleter_type const& getDeleter() const /*noexcept*/ { - return compressed_deleter_type::get(); + return compressed_base::get(); } void swap(compressed_data& that) /*noexcept*/ { - compressed_deleter_type::swap(static_cast(that)); + compressed_base::swap(static_cast>(that)); SkTSwap(fPtr, that.fPtr); } }; @@ -267,29 +282,30 @@ private: public: /*constexpr*/ unique_ptr() /*noexcept*/ : data() { - static_assert(!is_pointer::value, "Deleter is nullptr function pointer!"); + static_assert(!std::is_pointer::value, "Deleter nullptr function pointer!"); } - /*constexpr*/ unique_ptr(skstd::nullptr_t) /*noexcept*/ : unique_ptr() { } + /*constexpr*/ unique_ptr(std::nullptr_t) /*noexcept*/ : unique_ptr() { } explicit unique_ptr(pointer ptr) /*noexcept*/ : data(ptr, deleter_type()) { - static_assert(!is_pointer::value, "Deleter is nullptr function pointer!"); + static_assert(!std::is_pointer::value, "Deleter nullptr function pointer!"); } unique_ptr(pointer ptr, - conditional_t::value, deleter_type,const deleter_type&> d) + conditional_t::value, + deleter_type, const deleter_type&> d) /*noexcept*/ : data(ptr, d) {} unique_ptr(pointer ptr, remove_reference_t&& d) /*noexcept*/ - : data(move(ptr), move(d)) + : data(std::move(ptr), std::move(d)) { - static_assert(!is_reference::value, + static_assert(!std::is_reference::value, "Binding an rvalue reference deleter as an lvalue reference deleter is not allowed."); } unique_ptr(unique_ptr&& that) /*noexcept*/ - : data(that.release(), forward(that.get_deleter())) + : data(that.release(), std::forward(that.get_deleter())) {} ~unique_ptr() { @@ -302,11 +318,11 @@ public: unique_ptr& operator=(unique_ptr&& that) /*noexcept*/ { reset(that.release()); - get_deleter() = forward(that.get_deleter()); + get_deleter() = std::forward(that.get_deleter()); return *this; } - unique_ptr& operator=(skstd::nullptr_t) /*noexcept*/ { + unique_ptr& operator=(std::nullptr_t) /*noexcept*/ { reset(); return *this; } @@ -367,13 +383,13 @@ inline bool operator==(const unique_ptr& a, const unique_ptr& b) } template -inline bool operator==(const unique_ptr& a, skstd::nullptr_t) /*noexcept*/ { +inline bool operator==(const unique_ptr& a, std::nullptr_t) /*noexcept*/ { //return !a; return !a.is_attached(); } template -inline bool operator==(skstd::nullptr_t, const unique_ptr& b) /*noexcept*/ { +inline bool operator==(std::nullptr_t, const unique_ptr& b) /*noexcept*/ { //return !b; return !b.is_attached(); } @@ -384,17 +400,18 @@ inline bool operator!=(const unique_ptr& a, const unique_ptr& b) } template -inline bool operator!=(const unique_ptr& a, skstd::nullptr_t) /*noexcept*/ { +inline bool operator!=(const unique_ptr& a, std::nullptr_t) /*noexcept*/ { //return (bool)a; return a.is_attached(); } template -inline bool operator!=(skstd::nullptr_t, const unique_ptr& b) /*noexcept*/ { +inline bool operator!=(std::nullptr_t, const unique_ptr& b) /*noexcept*/ { //return (bool)b; return b.is_attached(); } } // namespace skstd +#endif #endif diff --git a/gfx/skia/skia/include/private/SkUtility.h b/gfx/skia/skia/include/private/SkUtility.h deleted file mode 100644 index a96e8fe292..0000000000 --- a/gfx/skia/skia/include/private/SkUtility.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2015 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#ifndef SkUtility_DEFINED -#define SkUtility_DEFINED - -#include "SkTLogic.h" - -namespace skstd { - -template inline remove_reference_t&& move(T&& t) { - return static_cast&&>(t); -} - -template inline T&& forward(remove_reference_t& t) /*noexcept*/ { - return static_cast(t); -} -template inline T&& forward(remove_reference_t&& t) /*noexcept*/ { - static_assert(!is_lvalue_reference::value, - "Forwarding an rvalue reference as an lvalue reference is not allowed."); - return static_cast(t); -} - -template add_rvalue_reference_t declval(); - -} // namespace skstd - -#endif diff --git a/gfx/skia/skia/include/svg/parser/SkSVGParser.h b/gfx/skia/skia/include/svg/parser/SkSVGParser.h index bb3ab90517..9a5c4157b1 100644 --- a/gfx/skia/skia/include/svg/parser/SkSVGParser.h +++ b/gfx/skia/skia/include/svg/parser/SkSVGParser.h @@ -11,7 +11,7 @@ #define SkSVGParser_DEFINED #include "SkMatrix.h" -#include "SkTDict.h" +#include "../private/SkTDict.h" #include "SkSVGPaintState.h" #include "SkSVGTypes.h" #include "SkStream.h" diff --git a/gfx/skia/skia/include/utils/SkCubicInterval.h b/gfx/skia/skia/include/utils/SkCubicInterval.h deleted file mode 100644 index 64d63cf2cd..0000000000 --- a/gfx/skia/skia/include/utils/SkCubicInterval.h +++ /dev/null @@ -1,22 +0,0 @@ - -/* - * Copyright 2011 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ -#ifndef SkCubicInterval_DEFINED -#define SkCubicInterval_DEFINED - -#include "SkPoint.h" - -SkScalar SkEvalCubicInterval(SkScalar x1, SkScalar y1, - SkScalar x2, SkScalar y2, - SkScalar unitX); - -static inline SkScalar SkEvalCubicInterval(const SkPoint pts[2], SkScalar x) { - return SkEvalCubicInterval(pts[0].fX, pts[0].fY, - pts[1].fX, pts[1].fY, x); -} - -#endif diff --git a/gfx/skia/skia/include/utils/SkCullPoints.h b/gfx/skia/skia/include/utils/SkCullPoints.h deleted file mode 100644 index fafa0fc085..0000000000 --- a/gfx/skia/skia/include/utils/SkCullPoints.h +++ /dev/null @@ -1,71 +0,0 @@ - -/* - * Copyright 2006 The Android Open Source Project - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - - -#ifndef SkCullPoints_DEFINED -#define SkCullPoints_DEFINED - -#include "SkRect.h" - -class SkCullPoints { -public: - SkCullPoints(); - SkCullPoints(const SkIRect& r); - - void reset(const SkIRect& r); - - /** Start a contour at (x,y). Follow this with call(s) to lineTo(...) - */ - void moveTo(int x, int y); - - enum LineToResult { - kNo_Result, //!< line segment was completely clipped out - kLineTo_Result, //!< path.lineTo(pts[1]); - kMoveToLineTo_Result //!< path.moveTo(pts[0]); path.lineTo(pts[1]); - }; - /** Connect a line to the previous call to lineTo (or moveTo). - */ - LineToResult lineTo(int x, int y, SkIPoint pts[2]); - -private: - SkIRect fR; // the caller's rectangle - SkIPoint fAsQuad[4]; // cache of fR as 4 points - SkIPoint fPrevPt; // private state - LineToResult fPrevResult; // private state - - bool sect_test(int x0, int y0, int x1, int y1) const; -}; - -///////////////////////////////////////////////////////////////////////////////// - -class SkPath; - -/** \class SkCullPointsPath - - Similar to SkCullPoints, but this class handles the return values - from lineTo, and automatically builds a SkPath with the result(s). -*/ -class SkCullPointsPath { -public: - SkCullPointsPath(); - SkCullPointsPath(const SkIRect& r, SkPath* dst); - - void reset(const SkIRect& r, SkPath* dst); - - void moveTo(int x, int y); - void lineTo(int x, int y); - -private: - SkCullPoints fCP; - SkPath* fPath; -}; - -bool SkHitTestPath(const SkPath&, SkRect& target, bool hires); -bool SkHitTestPath(const SkPath&, SkScalar x, SkScalar y, bool hires); - -#endif diff --git a/gfx/skia/skia/include/utils/SkDumpCanvas.h b/gfx/skia/skia/include/utils/SkDumpCanvas.h index b853ba686b..d4c6dbf81b 100644 --- a/gfx/skia/skia/include/utils/SkDumpCanvas.h +++ b/gfx/skia/skia/include/utils/SkDumpCanvas.h @@ -73,7 +73,7 @@ public: protected: void willSave() override; - SaveLayerStrategy willSaveLayer(const SkRect*, const SkPaint*, SaveFlags) override; + SaveLayerStrategy getSaveLayerStrategy(const SaveLayerRec&) override; void willRestore() override; void didConcat(const SkMatrix&) override; @@ -108,7 +108,6 @@ protected: const SkPaint*, SrcRectConstraint) override; void onDrawBitmapNine(const SkBitmap&, const SkIRect& center, const SkRect& dst, const SkPaint*) override; - void onDrawSprite(const SkBitmap&, int left, int top, const SkPaint*) override; void onDrawVertices(VertexMode vmode, int vertexCount, const SkPoint vertices[], const SkPoint texs[], const SkColor colors[], SkXfermode* xmode, diff --git a/gfx/skia/skia/include/utils/SkInterpolator.h b/gfx/skia/skia/include/utils/SkInterpolator.h index 18203d05d0..e062b38f65 100644 --- a/gfx/skia/skia/include/utils/SkInterpolator.h +++ b/gfx/skia/skia/include/utils/SkInterpolator.h @@ -59,7 +59,7 @@ public: fFlags = SkToU8((fFlags & ~kReset) | (int)reset); } - Result timeToT(SkMSec time, SkScalar* T, int* index, SkBool* exact) const; + Result timeToT(SkMSec time, SkScalar* T, int* index, bool* exact) const; protected: enum Flags { @@ -121,8 +121,15 @@ private: typedef SkInterpolatorBase INHERITED; }; -/** Given all the parameters are [0...1], apply the cubic specified by (0,0) - (bx,by) (cx,cy) (1,1) to value, returning the answer, also [0...1]. +/** Interpolate a cubic curve, typically to provide an ease-in ease-out transition. + All the parameters are in the range of [0...1]. + The input value is treated as the x-coordinate of the cubic. + The output value is the y-coordinate on the cubic at the x-coordinate. + + @param value The x-coordinate pinned between [0..1]. + @param bx,by,cx,cy The cubic control points where the cubic is specified + as (0,0) (bx,by) (cx,cy) (1,1) + @return the corresponding y-coordinate value, from [0..1]. */ SkScalar SkUnitCubicInterp(SkScalar value, SkScalar bx, SkScalar by, SkScalar cx, SkScalar cy); diff --git a/gfx/skia/skia/include/utils/SkLuaCanvas.h b/gfx/skia/skia/include/utils/SkLuaCanvas.h index f07af0695b..37e82be51d 100644 --- a/gfx/skia/skia/include/utils/SkLuaCanvas.h +++ b/gfx/skia/skia/include/utils/SkLuaCanvas.h @@ -22,7 +22,7 @@ public: protected: void willSave() override; - SaveLayerStrategy willSaveLayer(const SkRect*, const SkPaint*, SaveFlags) override; + SaveLayerStrategy getSaveLayerStrategy(const SaveLayerRec&) override; void willRestore() override; void didConcat(const SkMatrix&) override; @@ -54,7 +54,6 @@ protected: const SkPaint*, SrcRectConstraint) override; void onDrawBitmapNine(const SkBitmap&, const SkIRect& center, const SkRect& dst, const SkPaint*) override; - void onDrawSprite(const SkBitmap&, int left, int top, const SkPaint*) override; void onDrawVertices(VertexMode vmode, int vertexCount, const SkPoint vertices[], const SkPoint texs[], const SkColor colors[], SkXfermode* xmode, diff --git a/gfx/skia/skia/include/utils/SkNWayCanvas.h b/gfx/skia/skia/include/utils/SkNWayCanvas.h index a60836be57..8d824d7e2c 100644 --- a/gfx/skia/skia/include/utils/SkNWayCanvas.h +++ b/gfx/skia/skia/include/utils/SkNWayCanvas.h @@ -30,7 +30,7 @@ protected: SkTDArray fList; void willSave() override; - SaveLayerStrategy willSaveLayer(const SkRect*, const SkPaint*, SaveFlags) override; + SaveLayerStrategy getSaveLayerStrategy(const SaveLayerRec&) override; void willRestore() override; void didConcat(const SkMatrix&) override; @@ -65,7 +65,6 @@ protected: const SkPaint*, SrcRectConstraint) override; void onDrawBitmapNine(const SkBitmap&, const SkIRect& center, const SkRect& dst, const SkPaint*) override; - void onDrawSprite(const SkBitmap&, int left, int top, const SkPaint*) override; void onDrawVertices(VertexMode vmode, int vertexCount, const SkPoint vertices[], const SkPoint texs[], const SkColor colors[], SkXfermode* xmode, diff --git a/gfx/skia/skia/include/utils/SkNoSaveLayerCanvas.h b/gfx/skia/skia/include/utils/SkNoSaveLayerCanvas.h index 0cebefff18..3d786c532c 100644 --- a/gfx/skia/skia/include/utils/SkNoSaveLayerCanvas.h +++ b/gfx/skia/skia/include/utils/SkNoSaveLayerCanvas.h @@ -21,9 +21,8 @@ public: {} protected: - virtual SaveLayerStrategy willSaveLayer(const SkRect* bounds, const SkPaint* paint, - SaveFlags flags) override { - this->INHERITED::willSaveLayer(bounds, paint, flags); + SaveLayerStrategy getSaveLayerStrategy(const SaveLayerRec& rec) override { + (void)this->INHERITED::getSaveLayerStrategy(rec); return kNoLayer_SaveLayerStrategy; } diff --git a/gfx/skia/skia/include/utils/SkPaintFilterCanvas.h b/gfx/skia/skia/include/utils/SkPaintFilterCanvas.h index d693758797..909cf3b987 100644 --- a/gfx/skia/skia/include/utils/SkPaintFilterCanvas.h +++ b/gfx/skia/skia/include/utils/SkPaintFilterCanvas.h @@ -9,10 +9,11 @@ #define SkPaintFilterCanvas_DEFINED #include "SkNWayCanvas.h" +#include "SkTLazy.h" /** \class SkPaintFilterCanvas - A utility proxy base class for implementing paint filters. + A utility proxy base class for implementing draw/paint filters. */ class SK_API SkPaintFilterCanvas : public SkNWayCanvas { public: @@ -48,13 +49,16 @@ public: protected: /** * Called with the paint that will be used to draw the specified type. - * The implementation may modify the paint as they wish. + * The implementation may modify the paint as they wish (using SkTCopyOnFirstWrite::writable). * - * Note: The base implementation calls onFilterPaint() for top-level/explicit paints only. + * The result bool is used to determine whether the draw op is to be + * executed (true) or skipped (false). + * + * Note: The base implementation calls onFilter() for top-level/explicit paints only. * To also filter encapsulated paints (e.g. SkPicture, SkTextBlob), clients may need to * override the relevant methods (i.e. drawPicture, drawTextBlob). */ - virtual void onFilterPaint(SkPaint* paint, Type type) const = 0; + virtual bool onFilter(SkTCopyOnFirstWrite* paint, Type type) const = 0; void onDrawPaint(const SkPaint&) override; void onDrawPoints(PointMode, size_t count, const SkPoint pts[], const SkPaint&) override; @@ -66,12 +70,13 @@ protected: void onDrawBitmap(const SkBitmap&, SkScalar left, SkScalar top, const SkPaint*) override; void onDrawBitmapRect(const SkBitmap&, const SkRect* src, const SkRect& dst, const SkPaint*, SrcRectConstraint) override; + void onDrawBitmapNine(const SkBitmap&, const SkIRect& center, const SkRect& dst, + const SkPaint*) override; void onDrawImage(const SkImage*, SkScalar left, SkScalar top, const SkPaint*) override; void onDrawImageRect(const SkImage*, const SkRect* src, const SkRect& dst, const SkPaint*, SrcRectConstraint) override; - void onDrawBitmapNine(const SkBitmap&, const SkIRect& center, const SkRect& dst, - const SkPaint*) override; - void onDrawSprite(const SkBitmap&, int left, int top, const SkPaint*) override; + void onDrawImageNine(const SkImage*, const SkIRect& center, const SkRect& dst, + const SkPaint*) override; void onDrawVertices(VertexMode vmode, int vertexCount, const SkPoint vertices[], const SkPoint texs[], const SkColor colors[], SkXfermode* xmode, diff --git a/gfx/skia/skia/include/utils/SkRTConf.h b/gfx/skia/skia/include/utils/SkRTConf.h index dc95cdbadb..6c7963c64e 100644 --- a/gfx/skia/skia/include/utils/SkRTConf.h +++ b/gfx/skia/skia/include/utils/SkRTConf.h @@ -9,11 +9,10 @@ #ifndef SkRTConf_DEFINED #define SkRTConf_DEFINED +#include "SkTArray.h" #include "SkString.h" #include "SkStream.h" - -#include "SkTDict.h" -#include "SkTArray.h" +#include "../private/SkTDict.h" /** \class SkRTConfBase Non-templated base class for the runtime configs diff --git a/gfx/skia/skia/include/utils/win/SkHRESULT.h b/gfx/skia/skia/include/utils/win/SkHRESULT.h index a6cc1d21b9..a0ffd5935c 100644 --- a/gfx/skia/skia/include/utils/win/SkHRESULT.h +++ b/gfx/skia/skia/include/utils/win/SkHRESULT.h @@ -17,7 +17,7 @@ void SkTraceHR(const char* file, unsigned long line, #ifdef SK_DEBUG #define SK_TRACEHR(_hr, _msg) SkTraceHR(__FILE__, __LINE__, _hr, _msg) #else -#define SK_TRACEHR(_hr, _msg) _hr +#define SK_TRACEHR(_hr, _msg) sk_ignore_unused_variable(_hr) #endif #define HR_GENERAL(_ex, _msg, _ret) {\ diff --git a/gfx/skia/skia/include/views/SkOSWindow_SDL.h b/gfx/skia/skia/include/views/SkOSWindow_SDL.h index c40ec2e301..e08108add4 100644 --- a/gfx/skia/skia/include/views/SkOSWindow_SDL.h +++ b/gfx/skia/skia/include/views/SkOSWindow_SDL.h @@ -14,16 +14,11 @@ class SkOSWindow : public SkWindow { public: - SkOSWindow(void* screen); + SkOSWindow(void*); virtual ~SkOSWindow(); - static bool PostEvent(SkEvent* evt, SkEventSinkID, SkMSec delay) { - SkFAIL("not implemented\n"); - return false; - } - enum SkBackEndTypes { - kNone_BackEndType, + kNone_BackEndType, // TODO: remove this, it's not a real option. kNativeGL_BackEndType, #if SK_ANGLE kANGLE_BackEndType, @@ -39,25 +34,23 @@ public: bool makeFullscreen(); void setVsync(bool); void closeWindow(); - void loop() { - while (!fQuit) { - this->handleEvents(); - this->update(nullptr); - } - } + static void RunEventLoop(); protected: void onSetTitle(const char title[]) override; - void onHandleInval(const SkIRect&) override; - void onPDFSaved(const char title[], const char desc[], const char path[]) override; private: - void handleEvents(); - bool fQuit; - uint32_t fWindowFlags; + void createWindow(int msaaSampleCount); + void destroyWindow(); + void updateWindowTitle(); + static SkOSWindow* GetInstanceForWindowID(Uint32 windowID); + static bool HasDirtyWindows(); + static void UpdateDirtyWindows(); + static void HandleEvent(const SDL_Event&); + SDL_Window* fWindow; SDL_GLContext fGLContext; - + int fWindowMSAASampleCount; typedef SkWindow INHERITED; }; diff --git a/gfx/skia/skia/include/utils/SkParsePaint.h b/gfx/skia/skia/include/views/SkParsePaint.h similarity index 100% rename from gfx/skia/skia/include/utils/SkParsePaint.h rename to gfx/skia/skia/include/views/SkParsePaint.h diff --git a/gfx/skia/skia/include/views/SkView.h b/gfx/skia/skia/include/views/SkView.h index e099e0d405..17eb3800f4 100644 --- a/gfx/skia/skia/include/views/SkView.h +++ b/gfx/skia/skia/include/views/SkView.h @@ -13,7 +13,7 @@ #include "SkEventSink.h" #include "SkRect.h" #include "SkDOM.h" -#include "SkTDict.h" +#include "../private/SkTDict.h" #include "SkMatrix.h" #include "SkMetaData.h" diff --git a/gfx/skia/skia/include/views/SkViewInflate.h b/gfx/skia/skia/include/views/SkViewInflate.h index db3689a827..4ec36a67df 100644 --- a/gfx/skia/skia/include/views/SkViewInflate.h +++ b/gfx/skia/skia/include/views/SkViewInflate.h @@ -11,7 +11,7 @@ #define SkViewInflate_DEFINED #include "SkDOM.h" -#include "SkTDict.h" +#include "../private/SkTDict.h" #include "SkEvent.h" class SkView; diff --git a/gfx/skia/skia/src/android/SkBitmapRegionCanvas.cpp b/gfx/skia/skia/src/android/SkBitmapRegionCanvas.cpp index bac5dc1ffc..c7c42bd9a2 100644 --- a/gfx/skia/skia/src/android/SkBitmapRegionCanvas.cpp +++ b/gfx/skia/skia/src/android/SkBitmapRegionCanvas.cpp @@ -18,6 +18,7 @@ SkBitmapRegionCanvas::SkBitmapRegionCanvas(SkCodec* decoder) bool SkBitmapRegionCanvas::decodeRegion(SkBitmap* bitmap, SkBRDAllocator* allocator, const SkIRect& desiredSubset, int sampleSize, SkColorType dstColorType, bool requireUnpremul) { + // Reject color types not supported by this method if (kIndex_8_SkColorType == dstColorType || kGray_8_SkColorType == dstColorType) { SkCodecPrintf("Error: Color type not supported.\n"); @@ -34,8 +35,6 @@ bool SkBitmapRegionCanvas::decodeRegion(SkBitmap* bitmap, SkBRDAllocator* alloca dstAlphaType = kPremul_SkAlphaType; } - // FIXME: Can we add checks and support kIndex8 or unpremultiplied alpha in special cases? - // Fix the input sampleSize if necessary. if (sampleSize < 1) { sampleSize = 1; diff --git a/gfx/skia/skia/src/android/SkBitmapRegionCanvas.h b/gfx/skia/skia/src/android/SkBitmapRegionCanvas.h index 2edbf1ff3b..c01f96be3c 100644 --- a/gfx/skia/skia/src/android/SkBitmapRegionCanvas.h +++ b/gfx/skia/skia/src/android/SkBitmapRegionCanvas.h @@ -14,8 +14,16 @@ * an SkCanvas. It uses the scanline decoder to subset the height. It then * will subset the width and scale by drawing to an SkCanvas. */ -// FIXME (msarett): This implementation does not support WEBP, because WEBP -// does not have a scanline decoder. +// FIXME: This class works well as a performance/quality comparison for +// SkBitmapRegionCodec, but it lacks several capabilities that are +// required by BitmapRegionDecoder in Android. +// (1) WEBP decodes - because SkWebpCodec does not have a scanline +// decoder. +// (2) Decodes to kGray8 and kIndex8. +// (3) Decodes to kUnpremul. +// (4) Correcting an invalid dstColorType. For example, if the +// client requests kRGB_565 for a non-opaque image, rather than +// fail, we need to go ahead and decode to kN32. class SkBitmapRegionCanvas : public SkBitmapRegionDecoder { public: diff --git a/gfx/skia/skia/src/android/SkBitmapRegionCodec.cpp b/gfx/skia/skia/src/android/SkBitmapRegionCodec.cpp index 415b60c5ab..be3d5bcce7 100644 --- a/gfx/skia/skia/src/android/SkBitmapRegionCodec.cpp +++ b/gfx/skia/skia/src/android/SkBitmapRegionCodec.cpp @@ -17,7 +17,7 @@ SkBitmapRegionCodec::SkBitmapRegionCodec(SkAndroidCodec* codec) {} bool SkBitmapRegionCodec::decodeRegion(SkBitmap* bitmap, SkBRDAllocator* allocator, - const SkIRect& desiredSubset, int sampleSize, SkColorType dstColorType, + const SkIRect& desiredSubset, int sampleSize, SkColorType prefColorType, bool requireUnpremul) { // Fix the input sampleSize if necessary. @@ -50,10 +50,8 @@ bool SkBitmapRegionCodec::decodeRegion(SkBitmap* bitmap, SkBRDAllocator* allocat SkISize scaledSize = fCodec->getSampledSubsetDimensions(sampleSize, subset); // Create the image info for the decode - SkAlphaType dstAlphaType = fCodec->getInfo().alphaType(); - if (kOpaque_SkAlphaType != dstAlphaType) { - dstAlphaType = requireUnpremul ? kUnpremul_SkAlphaType : kPremul_SkAlphaType; - } + SkColorType dstColorType = fCodec->computeOutputColorType(prefColorType); + SkAlphaType dstAlphaType = fCodec->computeOutputAlphaType(requireUnpremul); SkImageInfo decodeInfo = SkImageInfo::Make(scaledSize.width(), scaledSize.height(), dstColorType, dstAlphaType); @@ -94,6 +92,13 @@ bool SkBitmapRegionCodec::decodeRegion(SkBitmap* bitmap, SkBRDAllocator* allocat scaledOutHeight += scaledOutY + scaledExtraY; } SkImageInfo outInfo = decodeInfo.makeWH(scaledOutWidth, scaledOutHeight); + if (kGray_8_SkColorType == dstColorType) { + // The legacy implementations of BitmapFactory and BitmapRegionDecoder + // used kAlpha8 for grayscale images (before kGray8 existed). While + // the codec recognizes kGray8, we need to decode into a kAlpha8 + // bitmap in order to avoid a behavior change. + outInfo = SkImageInfo::MakeA8(scaledOutWidth, scaledOutHeight); + } bitmap->setInfo(outInfo); if (!bitmap->tryAllocPixels(allocator, colorTable.get())) { SkCodecPrintf("Error: Could not allocate pixels.\n"); diff --git a/gfx/skia/skia/src/animator/SkDisplayType.h b/gfx/skia/skia/src/animator/SkDisplayType.h index 474a65e8e6..8ffcd75fea 100644 --- a/gfx/skia/skia/src/animator/SkDisplayType.h +++ b/gfx/skia/skia/src/animator/SkDisplayType.h @@ -1,4 +1,3 @@ - /* * Copyright 2006 The Android Open Source Project * @@ -6,13 +5,14 @@ * found in the LICENSE file. */ - #ifndef SkDisplayType_DEFINED #define SkDisplayType_DEFINED #include "SkMath.h" #include "SkScalar.h" +typedef int SkBool; + #ifdef SK_DEBUG #define SK_DUMP_ENABLED #ifdef SK_BUILD_FOR_MAC diff --git a/gfx/skia/skia/src/animator/SkDisplayTypes.h b/gfx/skia/skia/src/animator/SkDisplayTypes.h index 07e8448a4c..c24091f398 100644 --- a/gfx/skia/skia/src/animator/SkDisplayTypes.h +++ b/gfx/skia/skia/src/animator/SkDisplayTypes.h @@ -1,4 +1,3 @@ - /* * Copyright 2006 The Android Open Source Project * @@ -6,7 +5,6 @@ * found in the LICENSE file. */ - #ifndef SkDisplayTypes_DEFINED #define SkDisplayTypes_DEFINED diff --git a/gfx/skia/skia/src/animator/SkMemberInfo.h b/gfx/skia/skia/src/animator/SkMemberInfo.h index b62de663d4..709d66ac84 100644 --- a/gfx/skia/skia/src/animator/SkMemberInfo.h +++ b/gfx/skia/skia/src/animator/SkMemberInfo.h @@ -18,6 +18,7 @@ #include "SkScript.h" #include "SkString.h" #include "SkIntArray.h" +#include class SkAnimateMaker; class SkDisplayable; @@ -111,11 +112,11 @@ struct SkMemberInfo { #define SK_MEMBER(_member, _type) \ { #_member, SK_OFFSETOF(BASE_CLASS, _member), SkType_##_type, \ - sizeof(skstd::declval()._member) / sizeof(SkScalar) } + sizeof(std::declval()._member) / sizeof(SkScalar) } #define SK_MEMBER_ALIAS(_member, _alias, _type) \ { #_member, SK_OFFSETOF(BASE_CLASS, _alias), SkType_##_type, \ - sizeof(skstd::declval()._alias) / sizeof(SkScalar) } + sizeof(std::declval()._alias) / sizeof(SkScalar) } #define SK_MEMBER_ARRAY(_member, _type) \ { #_member, SK_OFFSETOF(BASE_CLASS, _member), SkType_Array, \ diff --git a/gfx/skia/skia/src/animator/SkOperandIterpolator.cpp b/gfx/skia/skia/src/animator/SkOperandIterpolator.cpp index e6973e29f7..89ac44dea2 100644 --- a/gfx/skia/skia/src/animator/SkOperandIterpolator.cpp +++ b/gfx/skia/skia/src/animator/SkOperandIterpolator.cpp @@ -1,4 +1,3 @@ - /* * Copyright 2006 The Android Open Source Project * @@ -6,7 +5,6 @@ * found in the LICENSE file. */ - #include "SkOperandInterpolator.h" #include "SkScript.h" @@ -60,7 +58,7 @@ SkInterpolatorBase::Result SkOperandInterpolator::timeToValues(SkMSec time, SkOp { SkScalar T; int index; - SkBool exact; + bool exact; Result result = timeToT(time, &T, &index, &exact); if (values) { diff --git a/gfx/skia/skia/src/animator/SkScript.h b/gfx/skia/skia/src/animator/SkScript.h index aa8d9a30c8..074d10c3cc 100644 --- a/gfx/skia/skia/src/animator/SkScript.h +++ b/gfx/skia/skia/src/animator/SkScript.h @@ -1,4 +1,3 @@ - /* * Copyright 2006 The Android Open Source Project * @@ -6,7 +5,6 @@ * found in the LICENSE file. */ - #ifndef SkScript_DEFINED #define SkScript_DEFINED diff --git a/gfx/skia/skia/src/animator/SkScript2.h b/gfx/skia/skia/src/animator/SkScript2.h index d182e8c7c5..f257adb450 100644 --- a/gfx/skia/skia/src/animator/SkScript2.h +++ b/gfx/skia/skia/src/animator/SkScript2.h @@ -1,13 +1,14 @@ - /* * Copyright 2011 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ + #ifndef SkScript2_DEFINED #define SkScript2_DEFINED +#include "SkDisplayType.h" #include "SkOperand2.h" #include "SkStream.h" #include "SkTDArray.h" diff --git a/gfx/skia/skia/src/animator/SkTime.cpp b/gfx/skia/skia/src/animator/SkTime.cpp index 560ccd2fc8..4ee60bf39d 100644 --- a/gfx/skia/skia/src/animator/SkTime.cpp +++ b/gfx/skia/skia/src/animator/SkTime.cpp @@ -4,49 +4,3 @@ * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ - -#include "SkTime.h" - -#ifdef SK_BUILD_FOR_WIN - -#ifdef SK_DEBUG -SkMSec gForceTickCount = (SkMSec) -1; -#endif - -void SkTime::GetDateTime(DateTime* t) { - if (t) { - SYSTEMTIME syst; - - ::GetLocalTime(&syst); - t->fYear = SkToU16(syst.wYear); - t->fMonth = SkToU8(syst.wMonth); - t->fDayOfWeek = SkToU8(syst.wDayOfWeek); - t->fDay = SkToU8(syst.wDay); - t->fHour = SkToU8(syst.wHour); - t->fMinute = SkToU8(syst.wMinute); - t->fSecond = SkToU8(syst.wSecond); - } -} - -#elif defined(xSK_BUILD_FOR_MAC) - -#include - -void SkTime::GetDateTime(DateTime* t) { - if (t) { - tm syst; - time_t tm; - - time(&tm); - localtime_r(&tm, &syst); - t->fYear = SkToU16(syst.tm_year); - t->fMonth = SkToU8(syst.tm_mon + 1); - t->fDayOfWeek = SkToU8(syst.tm_wday); - t->fDay = SkToU8(syst.tm_mday); - t->fHour = SkToU8(syst.tm_hour); - t->fMinute = SkToU8(syst.tm_min); - t->fSecond = SkToU8(syst.tm_sec); - } -} - -#endif diff --git a/gfx/skia/skia/src/codec/SkAndroidCodec.cpp b/gfx/skia/skia/src/codec/SkAndroidCodec.cpp index cf6e253d97..d309d58501 100644 --- a/gfx/skia/skia/src/codec/SkAndroidCodec.cpp +++ b/gfx/skia/skia/src/codec/SkAndroidCodec.cpp @@ -16,8 +16,9 @@ static bool is_valid_sample_size(int sampleSize) { return sampleSize > 0; } -SkAndroidCodec::SkAndroidCodec(const SkImageInfo& info) - : fInfo(info) +SkAndroidCodec::SkAndroidCodec(SkCodec* codec) + : fInfo(codec->getInfo()) + , fCodec(codec) {} SkAndroidCodec* SkAndroidCodec::NewFromStream(SkStream* stream, SkPngChunkReader* chunkReader) { @@ -34,11 +35,9 @@ SkAndroidCodec* SkAndroidCodec::NewFromStream(SkStream* stream, SkPngChunkReader case kWBMP_SkEncodedFormat: case kBMP_SkEncodedFormat: case kGIF_SkEncodedFormat: + case kICO_SkEncodedFormat: return new SkSampledCodec(codec.detach()); default: - // FIXME: SkSampledCodec is temporarily disabled for other formats - // while focusing on the formats that are supported by - // BitmapRegionDecoder. return nullptr; } } @@ -51,6 +50,59 @@ SkAndroidCodec* SkAndroidCodec::NewFromData(SkData* data, SkPngChunkReader* chun return NewFromStream(new SkMemoryStream(data), chunkReader); } +SkColorType SkAndroidCodec::computeOutputColorType(SkColorType requestedColorType) { + // The legacy GIF and WBMP decoders always decode to kIndex_8_SkColorType. + // We will maintain this behavior. + SkEncodedFormat format = this->getEncodedFormat(); + if (kGIF_SkEncodedFormat == format || kWBMP_SkEncodedFormat == format) { + return kIndex_8_SkColorType; + } + + SkColorType suggestedColorType = this->getInfo().colorType(); + switch (requestedColorType) { + case kARGB_4444_SkColorType: + case kN32_SkColorType: + return kN32_SkColorType; + case kIndex_8_SkColorType: + if (kIndex_8_SkColorType == suggestedColorType) { + return kIndex_8_SkColorType; + } + break; + case kAlpha_8_SkColorType: + // Fall through to kGray_8. Before kGray_8_SkColorType existed, + // we allowed clients to request kAlpha_8 when they wanted a + // grayscale decode. + case kGray_8_SkColorType: + if (kGray_8_SkColorType == suggestedColorType) { + return kGray_8_SkColorType; + } + break; + case kRGB_565_SkColorType: + if (kOpaque_SkAlphaType == this->getInfo().alphaType()) { + return kRGB_565_SkColorType; + } + break; + default: + break; + } + + // Android has limited support for kGray_8 (using kAlpha_8). We will not + // use kGray_8 for Android unless they specifically ask for it. + if (kGray_8_SkColorType == suggestedColorType) { + return kN32_SkColorType; + } + + // This may be kN32_SkColorType or kIndex_8_SkColorType. + return suggestedColorType; +} + +SkAlphaType SkAndroidCodec::computeOutputAlphaType(bool requestedUnpremul) { + if (kOpaque_SkAlphaType == this->getInfo().alphaType()) { + return kOpaque_SkAlphaType; + } + return requestedUnpremul ? kUnpremul_SkAlphaType : kPremul_SkAlphaType; +} + SkISize SkAndroidCodec::getSampledDimensions(int sampleSize) const { if (!is_valid_sample_size(sampleSize)) { return SkISize::Make(0, 0); diff --git a/gfx/skia/skia/src/codec/SkBmpCodec.cpp b/gfx/skia/skia/src/codec/SkBmpCodec.cpp index 191c2ad800..3302e4f752 100644 --- a/gfx/skia/skia/src/codec/SkBmpCodec.cpp +++ b/gfx/skia/skia/src/codec/SkBmpCodec.cpp @@ -56,12 +56,10 @@ enum BmpInputFormat { /* * Checks the start of the stream to see if the image is a bitmap */ -bool SkBmpCodec::IsBmp(SkStream* stream) { +bool SkBmpCodec::IsBmp(const void* buffer, size_t bytesRead) { // TODO: Support "IC", "PT", "CI", "CP", "BA" const char bmpSig[] = { 'B', 'M' }; - char buffer[sizeof(bmpSig)]; - return stream->read(buffer, sizeof(bmpSig)) == sizeof(bmpSig) && - !memcmp(buffer, bmpSig, sizeof(bmpSig)); + return bytesRead >= sizeof(bmpSig) && !memcmp(buffer, bmpSig, sizeof(bmpSig)); } /* @@ -481,6 +479,8 @@ bool SkBmpCodec::ReadHeader(SkStream* stream, bool inIco, SkCodec** codecOut) { // Return the codec switch (inputFormat) { case kStandard_BmpInputFormat: + // We require streams to have a memory base for Bmp-in-Ico decodes. + SkASSERT(!inIco || nullptr != stream->getMemoryBase()); *codecOut = new SkBmpStandardCodec(imageInfo, stream, bitsPerPixel, numColors, bytesPerColor, offset - bytesRead, rowOrder, inIco); return true; @@ -548,19 +548,6 @@ int32_t SkBmpCodec::getDstRow(int32_t y, int32_t height) const { return height - y - 1; } -/* - * Compute the number of colors in the color table - */ -uint32_t SkBmpCodec::computeNumColors(uint32_t numColors) { - // Zero is a default for maxColors - // Also set numColors to maxColors when it is too large - uint32_t maxColors = 1 << fBitsPerPixel; - if (numColors == 0 || numColors >= maxColors) { - return maxColors; - } - return numColors; -} - SkCodec::Result SkBmpCodec::onStartScanlineDecode(const SkImageInfo& dstInfo, const SkCodec::Options& options, SkPMColor inputColorPtr[], int* inputColorCount) { if (!conversion_possible(dstInfo, this->getInfo())) { diff --git a/gfx/skia/skia/src/codec/SkBmpCodec.h b/gfx/skia/skia/src/codec/SkBmpCodec.h index 65662ff1f8..5d77ca3fc8 100644 --- a/gfx/skia/skia/src/codec/SkBmpCodec.h +++ b/gfx/skia/skia/src/codec/SkBmpCodec.h @@ -20,11 +20,7 @@ */ class SkBmpCodec : public SkCodec { public: - - /* - * Checks the start of the stream to see if the image is a bmp - */ - static bool IsBmp(SkStream*); + static bool IsBmp(const void*, size_t); /* * Assumes IsBmp was called and returned true @@ -80,11 +76,6 @@ protected: */ int32_t getDstRow(int32_t y, int32_t height) const; - /* - * Compute the number of colors in the color table - */ - uint32_t computeNumColors(uint32_t numColors); - /* * Accessors used by subclasses */ diff --git a/gfx/skia/skia/src/codec/SkBmpRLECodec.cpp b/gfx/skia/skia/src/codec/SkBmpRLECodec.cpp index 4cee274ffe..b01012644b 100644 --- a/gfx/skia/skia/src/codec/SkBmpRLECodec.cpp +++ b/gfx/skia/skia/src/codec/SkBmpRLECodec.cpp @@ -21,7 +21,7 @@ SkBmpRLECodec::SkBmpRLECodec(const SkImageInfo& info, SkStream* stream, size_t RLEBytes) : INHERITED(info, stream, bitsPerPixel, rowOrder) , fColorTable(nullptr) - , fNumColors(this->computeNumColors(numColors)) + , fNumColors(numColors) , fBytesPerColor(bytesPerColor) , fOffset(offset) , fStreamBuffer(new uint8_t[RLEBytes]) @@ -82,9 +82,12 @@ SkCodec::Result SkBmpRLECodec::onGetPixels(const SkImageInfo& dstInfo, // access memory outside of our color table array. *numColors = maxColors; } + // Don't bother reading more than maxColors. + const uint32_t numColorsToRead = + fNumColors == 0 ? maxColors : SkTMin(fNumColors, maxColors); // Read the color table from the stream - colorBytes = fNumColors * fBytesPerColor; + colorBytes = numColorsToRead * fBytesPerColor; SkAutoTDeleteArray cBuffer(new uint8_t[colorBytes]); if (stream()->read(cBuffer.get(), colorBytes) != colorBytes) { SkCodecPrintf("Error: unable to read color table.\n"); @@ -93,7 +96,7 @@ SkCodec::Result SkBmpRLECodec::onGetPixels(const SkImageInfo& dstInfo, // Fill in the color table uint32_t i = 0; - for (; i < fNumColors; i++) { + for (; i < numColorsToRead; i++) { uint8_t blue = get_byte(cBuffer.get(), i*fBytesPerColor); uint8_t green = get_byte(cBuffer.get(), i*fBytesPerColor + 1); uint8_t red = get_byte(cBuffer.get(), i*fBytesPerColor + 2); diff --git a/gfx/skia/skia/src/codec/SkBmpRLECodec.h b/gfx/skia/skia/src/codec/SkBmpRLECodec.h index fcf910d8e5..df2a97d845 100644 --- a/gfx/skia/skia/src/codec/SkBmpRLECodec.h +++ b/gfx/skia/skia/src/codec/SkBmpRLECodec.h @@ -90,6 +90,7 @@ private: SkSampler* getSampler(bool createIfNecessary) override; SkAutoTUnref fColorTable; // owned + // fNumColors is the number specified in the header, or 0 if not present in the header. const uint32_t fNumColors; const uint32_t fBytesPerColor; const uint32_t fOffset; diff --git a/gfx/skia/skia/src/codec/SkBmpStandardCodec.cpp b/gfx/skia/skia/src/codec/SkBmpStandardCodec.cpp index fd4d6d18bc..85b40778b6 100644 --- a/gfx/skia/skia/src/codec/SkBmpStandardCodec.cpp +++ b/gfx/skia/skia/src/codec/SkBmpStandardCodec.cpp @@ -20,13 +20,14 @@ SkBmpStandardCodec::SkBmpStandardCodec(const SkImageInfo& info, SkStream* stream SkCodec::SkScanlineOrder rowOrder, bool inIco) : INHERITED(info, stream, bitsPerPixel, rowOrder) , fColorTable(nullptr) - , fNumColors(this->computeNumColors(numColors)) + , fNumColors(numColors) , fBytesPerColor(bytesPerColor) , fOffset(offset) , fSwizzler(nullptr) , fSrcRowBytes(SkAlign4(compute_row_bytes(this->getInfo().width(), this->bitsPerPixel()))) , fSrcBuffer(new uint8_t [fSrcRowBytes]) , fInIco(inIco) + , fAndMaskRowBytes(fInIco ? SkAlign4(compute_row_bytes(this->getInfo().width(), 1)) : 0) {} /* @@ -60,9 +61,6 @@ SkCodec::Result SkBmpStandardCodec::onGetPixels(const SkImageInfo& dstInfo, *rowsDecoded = rows; return kIncompleteInput; } - if (fInIco) { - return this->decodeIcoMask(dstInfo, dst, dstRowBytes); - } return kSuccess; } @@ -82,9 +80,12 @@ SkCodec::Result SkBmpStandardCodec::onGetPixels(const SkImageInfo& dstInfo, // access memory outside of our color table array. *numColors = maxColors; } + // Don't bother reading more than maxColors. + const uint32_t numColorsToRead = + fNumColors == 0 ? maxColors : SkTMin(fNumColors, maxColors); // Read the color table from the stream - colorBytes = fNumColors * fBytesPerColor; + colorBytes = numColorsToRead * fBytesPerColor; SkAutoTDeleteArray cBuffer(new uint8_t[colorBytes]); if (stream()->read(cBuffer.get(), colorBytes) != colorBytes) { SkCodecPrintf("Error: unable to read color table.\n"); @@ -112,7 +113,7 @@ SkCodec::Result SkBmpStandardCodec::onGetPixels(const SkImageInfo& dstInfo, // Fill in the color table uint32_t i = 0; - for (; i < fNumColors; i++) { + for (; i < numColorsToRead; i++) { uint8_t blue = get_byte(cBuffer.get(), i*fBytesPerColor); uint8_t green = get_byte(cBuffer.get(), i*fBytesPerColor + 1); uint8_t red = get_byte(cBuffer.get(), i*fBytesPerColor + 2); @@ -227,9 +228,8 @@ SkCodec::Result SkBmpStandardCodec::prepareToDecode(const SkImageInfo& dstInfo, /* * Performs the bitmap decoding for standard input format */ -int SkBmpStandardCodec::decodeRows(const SkImageInfo& dstInfo, - void* dst, size_t dstRowBytes, - const Options& opts) { +int SkBmpStandardCodec::decodeRows(const SkImageInfo& dstInfo, void* dst, size_t dstRowBytes, + const Options& opts) { // Iterate over rows of the image const int height = dstInfo.height(); for (int y = 0; y < height; y++) { @@ -246,29 +246,77 @@ int SkBmpStandardCodec::decodeRows(const SkImageInfo& dstInfo, fSwizzler->swizzle(dstRow, fSrcBuffer.get()); } - // Finished decoding the entire image + if (fInIco) { + const int startScanline = this->currScanline(); + if (startScanline < 0) { + // We are not performing a scanline decode. + // Just decode the entire ICO mask and return. + decodeIcoMask(this->stream(), dstInfo, dst, dstRowBytes); + return height; + } + + // In order to perform a scanline ICO decode, we must be able + // to skip ahead in the stream in order to apply the AND mask + // to the requested scanlines. + // We will do this by taking advantage of the fact that + // SkIcoCodec always uses a SkMemoryStream as its underlying + // representation of the stream. + const void* memoryBase = this->stream()->getMemoryBase(); + SkASSERT(nullptr != memoryBase); + SkASSERT(this->stream()->hasLength()); + SkASSERT(this->stream()->hasPosition()); + + const size_t length = this->stream()->getLength(); + const size_t currPosition = this->stream()->getPosition(); + + // Calculate how many bytes we must skip to reach the AND mask. + const int remainingScanlines = this->getInfo().height() - startScanline - height; + const size_t bytesToSkip = remainingScanlines * fSrcRowBytes + + startScanline * fAndMaskRowBytes; + const size_t subStreamStartPosition = currPosition + bytesToSkip; + if (subStreamStartPosition >= length) { + // FIXME: How can we indicate that this decode was actually incomplete? + return height; + } + + // Create a subStream to pass to decodeIcoMask(). It is useful to encapsulate + // the memory base into a stream in order to safely handle incomplete images + // without reading out of bounds memory. + const void* subStreamMemoryBase = SkTAddOffset(memoryBase, + subStreamStartPosition); + const size_t subStreamLength = length - subStreamStartPosition; + // This call does not transfer ownership of the subStreamMemoryBase. + SkMemoryStream subStream(subStreamMemoryBase, subStreamLength, false); + + // FIXME: If decodeIcoMask does not succeed, is there a way that we can + // indicate the decode was incomplete? + decodeIcoMask(&subStream, dstInfo, dst, dstRowBytes); + } + return height; } -// TODO (msarett): This function will need to be modified in order to perform row by row decodes -// when the Ico scanline decoder is implemented. -SkCodec::Result SkBmpStandardCodec::decodeIcoMask(const SkImageInfo& dstInfo, +void SkBmpStandardCodec::decodeIcoMask(SkStream* stream, const SkImageInfo& dstInfo, void* dst, size_t dstRowBytes) { // BMP in ICO have transparency, so this cannot be 565, and this mask // prevents us from using kIndex8. The below code depends on the output // being an SkPMColor. SkASSERT(dstInfo.colorType() == kN32_SkColorType); - // The AND mask is always 1 bit per pixel - const int width = this->getInfo().width(); - const size_t rowBytes = SkAlign4(compute_row_bytes(width, 1)); + // If we are sampling, make sure that we only mask the sampled pixels. + // We do not need to worry about sampling in the y-dimension because that + // should be handled by SkSampledCodec. + const int sampleX = fSwizzler->sampleX(); + const int sampledWidth = get_scaled_dimension(this->getInfo().width(), sampleX); + const int srcStartX = get_start_coord(sampleX); + SkPMColor* dstPtr = (SkPMColor*) dst; for (int y = 0; y < dstInfo.height(); y++) { // The srcBuffer will at least be large enough - if (stream()->read(fSrcBuffer.get(), rowBytes) != rowBytes) { + if (stream->read(fSrcBuffer.get(), fAndMaskRowBytes) != fAndMaskRowBytes) { SkCodecPrintf("Warning: incomplete AND mask for bmp-in-ico.\n"); - return kIncompleteInput; + return; } int row = this->getDstRow(y, dstInfo.height()); @@ -276,17 +324,17 @@ SkCodec::Result SkBmpStandardCodec::decodeIcoMask(const SkImageInfo& dstInfo, SkPMColor* dstRow = SkTAddOffset(dstPtr, row * dstRowBytes); - for (int x = 0; x < width; x++) { + int srcX = srcStartX; + for (int dstX = 0; dstX < sampledWidth; dstX++) { int quotient; int modulus; - SkTDivMod(x, 8, "ient, &modulus); + SkTDivMod(srcX, 8, "ient, &modulus); uint32_t shift = 7 - modulus; - uint32_t alphaBit = - (fSrcBuffer.get()[quotient] >> shift) & 0x1; - dstRow[x] &= alphaBit - 1; + uint32_t alphaBit = (fSrcBuffer.get()[quotient] >> shift) & 0x1; + dstRow[dstX] &= alphaBit - 1; + srcX += sampleX; } } - return kSuccess; } uint32_t SkBmpStandardCodec::onGetFillValue(SkColorType colorType, SkAlphaType alphaType) const { diff --git a/gfx/skia/skia/src/codec/SkBmpStandardCodec.h b/gfx/skia/skia/src/codec/SkBmpStandardCodec.h index d687eaad28..b7999001e1 100644 --- a/gfx/skia/skia/src/codec/SkBmpStandardCodec.h +++ b/gfx/skia/skia/src/codec/SkBmpStandardCodec.h @@ -75,9 +75,15 @@ private: int decodeRows(const SkImageInfo& dstInfo, void* dst, size_t dstRowBytes, const Options& opts) override; - Result decodeIcoMask(const SkImageInfo& dstInfo, void* dst, size_t dstRowBytes); + /* + * @param stream This may be a pointer to the stream owned by the parent SkCodec + * or a sub-stream of the stream owned by the parent SkCodec. + * Either way, this stream is unowned. + */ + void decodeIcoMask(SkStream* stream, const SkImageInfo& dstInfo, void* dst, size_t dstRowBytes); SkAutoTUnref fColorTable; // owned + // fNumColors is the number specified in the header, or 0 if not present in the header. const uint32_t fNumColors; const uint32_t fBytesPerColor; const uint32_t fOffset; @@ -85,6 +91,7 @@ private: const size_t fSrcRowBytes; SkAutoTDeleteArray fSrcBuffer; const bool fInIco; + const size_t fAndMaskRowBytes; // only used for fInIco decodes typedef SkBmpCodec INHERITED; }; diff --git a/gfx/skia/skia/src/codec/SkCodec.cpp b/gfx/skia/skia/src/codec/SkCodec.cpp index 11eb1f9847..04ee7c6a43 100644 --- a/gfx/skia/skia/src/codec/SkCodec.cpp +++ b/gfx/skia/skia/src/codec/SkCodec.cpp @@ -7,20 +7,20 @@ #include "SkBmpCodec.h" #include "SkCodec.h" -#include "SkData.h" -#include "SkCodec_libgif.h" -#include "SkCodec_libico.h" #include "SkCodec_libpng.h" -#include "SkCodec_wbmp.h" #include "SkCodecPriv.h" +#include "SkData.h" +#include "SkGifCodec.h" +#include "SkIcoCodec.h" #if !defined(GOOGLE3) #include "SkJpegCodec.h" #endif #include "SkStream.h" +#include "SkWbmpCodec.h" #include "SkWebpCodec.h" struct DecoderProc { - bool (*IsFormat)(SkStream*); + bool (*IsFormat)(const void*, size_t); SkCodec* (*NewFromStream)(SkStream*); }; @@ -35,6 +35,10 @@ static const DecoderProc gDecoderProcs[] = { { SkWbmpCodec::IsWbmp, SkWbmpCodec::NewFromStream } }; +size_t SkCodec::MinBufferedBytesNeeded() { + return WEBP_VP8_HEADER_SIZE; +} + SkCodec* SkCodec::NewFromStream(SkStream* stream, SkPngChunkReader* chunkReader) { if (!stream) { @@ -42,39 +46,49 @@ SkCodec* SkCodec::NewFromStream(SkStream* stream, } SkAutoTDelete streamDeleter(stream); - - SkAutoTDelete codec(nullptr); + + // 14 is enough to read all of the supported types. + const size_t bytesToRead = 14; + SkASSERT(bytesToRead <= MinBufferedBytesNeeded()); + + char buffer[bytesToRead]; + size_t bytesRead = stream->peek(buffer, bytesToRead); + + // It is also possible to have a complete image less than bytesToRead bytes + // (e.g. a 1 x 1 wbmp), meaning peek() would return less than bytesToRead. + // Assume that if bytesRead < bytesToRead, but > 0, the stream is shorter + // than bytesToRead, so pass that directly to the decoder. + // It also is possible the stream uses too small a buffer for peeking, but + // we trust the caller to use a large enough buffer. + + if (0 == bytesRead) { + // TODO: After implementing peek in CreateJavaOutputStreamAdaptor.cpp, this + // printf could be useful to notice failures. + // SkCodecPrintf("Encoded image data failed to peek!\n"); + + // It is possible the stream does not support peeking, but does support + // rewinding. + // Attempt to read() and pass the actual amount read to the decoder. + bytesRead = stream->read(buffer, bytesToRead); + if (!stream->rewind()) { + SkCodecPrintf("Encoded image data could not peek or rewind to determine format!\n"); + return nullptr; + } + } + // PNG is special, since we want to be able to supply an SkPngChunkReader. // But this code follows the same pattern as the loop. - const bool isPng = SkPngCodec::IsPng(stream); - if (!stream->rewind()) { - return NULL; - } - if (isPng) { - codec.reset(SkPngCodec::NewFromStream(streamDeleter.detach(), chunkReader)); + if (SkPngCodec::IsPng(buffer, bytesRead)) { + return SkPngCodec::NewFromStream(streamDeleter.detach(), chunkReader); } else { for (DecoderProc proc : gDecoderProcs) { - const bool correctFormat = proc.IsFormat(stream); - if (!stream->rewind()) { - return nullptr; - } - if (correctFormat) { - codec.reset(proc.NewFromStream(streamDeleter.detach())); - break; + if (proc.IsFormat(buffer, bytesRead)) { + return proc.NewFromStream(streamDeleter.detach()); } } } - // Set the max size at 128 megapixels (512 MB for kN32). - // This is about 4x smaller than a test image that takes a few minutes for - // dm to decode and draw. - const int32_t maxSize = 1 << 27; - if (codec && codec->getInfo().width() * codec->getInfo().height() > maxSize) { - SkCodecPrintf("Error: Image size too large, cannot decode.\n"); - return nullptr; - } else { - return codec.detach(); - } + return nullptr; } SkCodec* SkCodec::NewFromData(SkData* data, SkPngChunkReader* reader) { diff --git a/gfx/skia/skia/src/codec/SkCodecImageGenerator.cpp b/gfx/skia/skia/src/codec/SkCodecImageGenerator.cpp new file mode 100644 index 0000000000..2fef381ec1 --- /dev/null +++ b/gfx/skia/skia/src/codec/SkCodecImageGenerator.cpp @@ -0,0 +1,46 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkCodecImageGenerator.h" + +SkImageGenerator* SkCodecImageGenerator::NewFromEncodedCodec(SkData* data) { + SkCodec* codec = SkCodec::NewFromData(data); + if (nullptr == codec) { + return nullptr; + } + + return new SkCodecImageGenerator(codec, data); +} + +SkCodecImageGenerator::SkCodecImageGenerator(SkCodec* codec, SkData* data) + : INHERITED(codec->getInfo()) + , fCodec(codec) + , fData(SkRef(data)) +{} + +SkData* SkCodecImageGenerator::onRefEncodedData(SK_REFENCODEDDATA_CTXPARAM) { + return SkRef(fData.get()); +} + +bool SkCodecImageGenerator::onGetPixels(const SkImageInfo& info, void* pixels, size_t rowBytes, + SkPMColor ctable[], int* ctableCount) { + + SkCodec::Result result = fCodec->getPixels(info, pixels, rowBytes, nullptr, ctable, + ctableCount); + switch (result) { + case SkCodec::kSuccess: + case SkCodec::kIncompleteInput: + return true; + default: + return false; + } +} + +bool SkCodecImageGenerator::onGetYUV8Planes(SkISize sizes[3], void* planes[3], size_t rowBytes[3], + SkYUVColorSpace* colorSpace) { + return false; +} diff --git a/gfx/skia/skia/src/codec/SkCodecImageGenerator.h b/gfx/skia/skia/src/codec/SkCodecImageGenerator.h new file mode 100644 index 0000000000..80eacb19c8 --- /dev/null +++ b/gfx/skia/skia/src/codec/SkCodecImageGenerator.h @@ -0,0 +1,43 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkCodec.h" +#include "SkData.h" +#include "SkImageGenerator.h" + +class SkCodecImageGenerator : public SkImageGenerator { +public: + /* + * If this data represents an encoded image that we know how to decode, + * return an SkCodecImageGenerator. Otherwise return nullptr. + * + * Refs the data if an image generator can be returned. Otherwise does + * not affect the data. + */ + static SkImageGenerator* NewFromEncodedCodec(SkData* data); + +protected: + SkData* onRefEncodedData(SK_REFENCODEDDATA_CTXPARAM) override; + + bool onGetPixels(const SkImageInfo& info, void* pixels, size_t rowBytes, SkPMColor ctable[], + int* ctableCount) override; + + bool onGetYUV8Planes(SkISize sizes[3], void* planes[3], size_t rowBytes[3], + SkYUVColorSpace* colorSpace) override; + +private: + /* + * Takes ownership of codec + * Refs the data + */ + SkCodecImageGenerator(SkCodec* codec, SkData* data); + + SkAutoTDelete fCodec; + SkAutoTUnref fData; + + typedef SkImageGenerator INHERITED; +}; diff --git a/gfx/skia/skia/src/codec/SkCodecPriv.h b/gfx/skia/skia/src/codec/SkCodecPriv.h index 1b6723fd4a..27e2a63722 100644 --- a/gfx/skia/skia/src/codec/SkCodecPriv.h +++ b/gfx/skia/skia/src/codec/SkCodecPriv.h @@ -11,26 +11,9 @@ #include "SkColorPriv.h" #include "SkColorTable.h" #include "SkImageInfo.h" -#include "SkSwizzler.h" #include "SkTypes.h" #include "SkUtils.h" -/* - * - * Helper routine for alpha result codes - * - */ -#define INIT_RESULT_ALPHA \ - uint8_t zeroAlpha = 0; \ - uint8_t maxAlpha = 0xFF; - -#define UPDATE_RESULT_ALPHA(alpha) \ - zeroAlpha |= (alpha); \ - maxAlpha &= (alpha); - -#define COMPUTE_RESULT_ALPHA \ - SkSwizzler::GetResult(zeroAlpha, maxAlpha); - // FIXME: Consider sharing with dm, nanbench, and tools. inline float get_scale_from_sample_size(int sampleSize) { return 1.0f / ((float) sampleSize); diff --git a/gfx/skia/skia/src/codec/SkCodec_libico.h b/gfx/skia/skia/src/codec/SkCodec_libico.h deleted file mode 100644 index 92675f4d74..0000000000 --- a/gfx/skia/skia/src/codec/SkCodec_libico.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2015 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "SkCodec.h" -#include "SkImageInfo.h" -#include "SkStream.h" -#include "SkTypes.h" - -/* - * This class implements the decoding for bmp images - */ -class SkIcoCodec : public SkCodec { -public: - - /* - * Checks the start of the stream to see if the image is a Ico or Cur - */ - static bool IsIco(SkStream*); - - /* - * Assumes IsIco was called and returned true - * Creates an Ico decoder - * Reads enough of the stream to determine the image format - */ - static SkCodec* NewFromStream(SkStream*); - -protected: - - /* - * Chooses the best dimensions given the desired scale - */ - SkISize onGetScaledDimensions(float desiredScale) const override; - - bool onDimensionsSupported(const SkISize&) override; - - /* - * Initiates the Ico decode - */ - Result onGetPixels(const SkImageInfo& dstInfo, void* dst, size_t dstRowBytes, const Options&, - SkPMColor*, int*, int*) override; - - SkEncodedFormat onGetEncodedFormat() const override { - return kICO_SkEncodedFormat; - } - -private: - - /* - * Constructor called by NewFromStream - * @param embeddedCodecs codecs for the embedded images, takes ownership - */ - SkIcoCodec(const SkImageInfo& srcInfo, - SkTArray, true>* embeddedCodecs); - - SkAutoTDelete, true>> - fEmbeddedCodecs; // owned - - typedef SkCodec INHERITED; -}; diff --git a/gfx/skia/skia/src/codec/SkCodec_libpng.cpp b/gfx/skia/skia/src/codec/SkCodec_libpng.cpp index 355d493309..e8fd2a3cdd 100644 --- a/gfx/skia/skia/src/codec/SkCodec_libpng.cpp +++ b/gfx/skia/skia/src/codec/SkCodec_libpng.cpp @@ -14,6 +14,7 @@ #include "SkSize.h" #include "SkStream.h" #include "SkSwizzler.h" +#include "SkTemplates.h" /////////////////////////////////////////////////////////////////////////////// // Helper macros @@ -153,7 +154,6 @@ bool SkPngCodec::decodePalette(bool premultiply, int* ctableCount) { } int index = 0; - int transLessThanFF = 0; // Choose which function to use to create the color table. If the final destination's // colortype is unpremultiplied, the color table will store unpremultiplied colors. @@ -164,16 +164,10 @@ bool SkPngCodec::decodePalette(bool premultiply, int* ctableCount) { proc = &SkPackARGB32NoCheck; } for (; index < numTrans; index++) { - transLessThanFF |= (int)*trans - 0xFF; *colorPtr++ = proc(*trans++, palette->red, palette->green, palette->blue); palette++; } - if (transLessThanFF >= 0) { - // No transparent colors were found. - fAlphaState = kOpaque_AlphaState; - } - for (; index < numPalette; index++) { *colorPtr++ = SkPackARGB32(0xFF, palette->red, palette->green, palette->blue); palette++; @@ -206,17 +200,8 @@ bool SkPngCodec::decodePalette(bool premultiply, int* ctableCount) { // Creation /////////////////////////////////////////////////////////////////////////////// -#define PNG_BYTES_TO_CHECK 4 - -bool SkPngCodec::IsPng(SkStream* stream) { - char buf[PNG_BYTES_TO_CHECK]; - if (stream->read(buf, PNG_BYTES_TO_CHECK) != PNG_BYTES_TO_CHECK) { - return false; - } - if (png_sig_cmp((png_bytep) buf, (png_size_t)0, PNG_BYTES_TO_CHECK)) { - return false; - } - return true; +bool SkPngCodec::IsPng(const char* buf, size_t bytesRead) { + return !png_sig_cmp((png_bytep) buf, (png_size_t)0, bytesRead); } // Reads the header and initializes the output fields, if not NULL. @@ -267,8 +252,9 @@ static bool read_header(SkStream* stream, SkPngChunkReader* chunkReader, png_set_read_fn(png_ptr, static_cast(stream), sk_read_fn); #ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED - // FIXME: Does this need to be installed so early? - // hookup our chunkReader so we can see any user-chunks the caller may be interested in + // Hookup our chunkReader so we can see any user-chunks the caller may be interested in. + // This needs to be installed before we read the png header. Android may store ninepatch + // chunks in the header. if (chunkReader) { png_set_keep_unknown_chunks(png_ptr, PNG_HANDLE_CHUNK_ALWAYS, (png_byte*)"", 0); png_set_read_user_chunk_fn(png_ptr, (png_voidp) chunkReader, sk_read_user_chunk); @@ -392,13 +378,7 @@ SkPngCodec::SkPngCodec(const SkImageInfo& info, SkStream* stream, SkPngChunkRead , fSrcConfig(SkSwizzler::kUnknown) , fNumberPasses(numberPasses) , fBitDepth(bitDepth) -{ - if (info.alphaType() == kOpaque_SkAlphaType) { - fAlphaState = kOpaque_AlphaState; - } else { - fAlphaState = kUnknown_AlphaState; - } -} +{} SkPngCodec::~SkPngCodec() { this->destroyReadStruct(); @@ -512,7 +492,7 @@ SkCodec::Result SkPngCodec::onGetPixels(const SkImageInfo& requestedInfo, void* // error? int row = 0; // This must be declared above the call to setjmp to avoid memory leaks on incomplete images. - SkAutoMalloc storage; + SkAutoTMalloc storage; if (setjmp(png_jmpbuf(fPng_ptr))) { // Assume that any error that occurs while reading rows is caused by an incomplete input. if (fNumberPasses > 1) { @@ -534,7 +514,6 @@ SkCodec::Result SkPngCodec::onGetPixels(const SkImageInfo& requestedInfo, void* return kIncompleteInput; } - bool hasAlpha = false; // FIXME: We could split these out based on subclass. void* dstRow = dst; if (fNumberPasses > 1) { @@ -544,7 +523,7 @@ SkCodec::Result SkPngCodec::onGetPixels(const SkImageInfo& requestedInfo, void* const size_t srcRowBytes = width * bpp; storage.reset(width * height * bpp); - uint8_t* const base = static_cast(storage.get()); + uint8_t* const base = storage.get(); for (int i = 0; i < fNumberPasses; i++) { uint8_t* srcRow = base; @@ -558,27 +537,21 @@ SkCodec::Result SkPngCodec::onGetPixels(const SkImageInfo& requestedInfo, void* // Now swizzle it. uint8_t* srcRow = base; for (int y = 0; y < height; y++) { - hasAlpha |= !SkSwizzler::IsOpaque(fSwizzler->swizzle(dstRow, srcRow)); + fSwizzler->swizzle(dstRow, srcRow); dstRow = SkTAddOffset(dstRow, dstRowBytes); srcRow += srcRowBytes; } } else { storage.reset(requestedInfo.width() * SkSwizzler::BytesPerPixel(fSrcConfig)); - uint8_t* srcRow = static_cast(storage.get()); + uint8_t* srcRow = storage.get(); for (; row < requestedInfo.height(); row++) { png_read_rows(fPng_ptr, &srcRow, png_bytepp_NULL, 1); // FIXME: Only call IsOpaque once, outside the loop. Same for onGetScanlines. - hasAlpha |= !SkSwizzler::IsOpaque(fSwizzler->swizzle(dstRow, srcRow)); + fSwizzler->swizzle(dstRow, srcRow); dstRow = SkTAddOffset(dstRow, dstRowBytes); } } - if (hasAlpha) { - fAlphaState = kHasAlpha_AlphaState; - } else { - fAlphaState = kOpaque_AlphaState; - } - // FIXME: do we need substituteTranspColor? Note that we cannot do it for // scanline decoding, but we could do it here. Alternatively, we could do // it as we go, instead of in post-processing like SkPNGImageDecoder. @@ -602,38 +575,12 @@ uint32_t SkPngCodec::onGetFillValue(SkColorType colorType, SkAlphaType alphaType return INHERITED::onGetFillValue(colorType, alphaType); } -bool SkPngCodec::onReallyHasAlpha() const { - switch (fAlphaState) { - case kOpaque_AlphaState: - return false; - case kUnknown_AlphaState: - // Maybe the subclass knows? - return this->alphaInScanlineDecode() == kHasAlpha_AlphaState; - case kHasAlpha_AlphaState: - switch (this->alphaInScanlineDecode()) { - case kUnknown_AlphaState: - // Scanline decoder must not have been used. Return our knowledge. - return true; - case kOpaque_AlphaState: - // Scanline decoder was used, and did not find alpha in its subset. - return false; - case kHasAlpha_AlphaState: - return true; - } - } - - // All valid AlphaStates have been covered, so this should not be reached. - SkASSERT(false); - return true; -} - // Subclass of SkPngCodec which supports scanline decoding class SkPngScanlineDecoder : public SkPngCodec { public: SkPngScanlineDecoder(const SkImageInfo& srcInfo, SkStream* stream, SkPngChunkReader* chunkReader, png_structp png_ptr, png_infop info_ptr, int bitDepth) : INHERITED(srcInfo, stream, chunkReader, png_ptr, info_ptr, bitDepth, 1) - , fAlphaState(kUnknown_AlphaState) , fSrcRow(nullptr) {} @@ -649,9 +596,8 @@ public: return result; } - fAlphaState = kUnknown_AlphaState; fStorage.reset(this->getInfo().width() * SkSwizzler::BytesPerPixel(this->srcConfig())); - fSrcRow = static_cast(fStorage.get()); + fSrcRow = fStorage.get(); return kSuccess; } @@ -665,22 +611,12 @@ public: } void* dstRow = dst; - bool hasAlpha = false; for (; row < count; row++) { png_read_rows(this->png_ptr(), &fSrcRow, png_bytepp_NULL, 1); - hasAlpha |= !SkSwizzler::IsOpaque(this->swizzler()->swizzle(dstRow, fSrcRow)); + this->swizzler()->swizzle(dstRow, fSrcRow); dstRow = SkTAddOffset(dstRow, rowBytes); } - if (hasAlpha) { - fAlphaState = kHasAlpha_AlphaState; - } else { - if (kUnknown_AlphaState == fAlphaState) { - fAlphaState = kOpaque_AlphaState; - } - // Otherwise, the AlphaState is unchanged. - } - return row; } @@ -699,13 +635,8 @@ public: return true; } - AlphaState alphaInScanlineDecode() const override { - return fAlphaState; - } - private: - AlphaState fAlphaState; - SkAutoMalloc fStorage; + SkAutoTMalloc fStorage; uint8_t* fSrcRow; typedef SkPngCodec INHERITED; @@ -718,7 +649,6 @@ public: SkPngChunkReader* chunkReader, png_structp png_ptr, png_infop info_ptr, int bitDepth, int numberPasses) : INHERITED(srcInfo, stream, chunkReader, png_ptr, info_ptr, bitDepth, numberPasses) - , fAlphaState(kUnknown_AlphaState) , fHeight(-1) , fCanSkipRewind(false) { @@ -737,7 +667,6 @@ public: return result; } - fAlphaState = kUnknown_AlphaState; fHeight = dstInfo.height(); // FIXME: This need not be called on a second call to onStartScanlineDecode. fSrcRowBytes = this->getInfo().width() * SkSwizzler::BytesPerPixel(this->srcConfig()); @@ -768,7 +697,7 @@ public: if (!this->rewindIfNeeded()) { return kCouldNotRewind; } - this->updateNextScanline(currScanline); + this->updateCurrScanline(currScanline); } if (setjmp(png_jmpbuf(this->png_ptr()))) { @@ -778,8 +707,8 @@ public: // fail on the first pass, we can still report than some scanlines are initialized. return 0; } - SkAutoMalloc storage(count * fSrcRowBytes); - uint8_t* storagePtr = static_cast(storage.get()); + SkAutoTMalloc storage(count * fSrcRowBytes); + uint8_t* storagePtr = storage.get(); uint8_t* srcRow; const int startRow = this->nextScanline(); for (int i = 0; i < this->numberPasses(); i++) { @@ -801,22 +730,12 @@ public: //swizzle the rows we care about srcRow = storagePtr; void* dstRow = dst; - bool hasAlpha = false; for (int y = 0; y < count; y++) { - hasAlpha |= !SkSwizzler::IsOpaque(this->swizzler()->swizzle(dstRow, srcRow)); + this->swizzler()->swizzle(dstRow, srcRow); dstRow = SkTAddOffset(dstRow, dstRowBytes); srcRow += fSrcRowBytes; } - if (hasAlpha) { - fAlphaState = kHasAlpha_AlphaState; - } else { - if (kUnknown_AlphaState == fAlphaState) { - fAlphaState = kOpaque_AlphaState; - } - // Otherwise, the AlphaState is unchanged. - } - return count; } @@ -825,16 +744,11 @@ public: return true; } - AlphaState alphaInScanlineDecode() const override { - return fAlphaState; - } - SkScanlineOrder onGetScanlineOrder() const override { return kNone_SkScanlineOrder; } private: - AlphaState fAlphaState; int fHeight; size_t fSrcRowBytes; SkAutoMalloc fGarbageRow; diff --git a/gfx/skia/skia/src/codec/SkCodec_libpng.h b/gfx/skia/skia/src/codec/SkCodec_libpng.h index c2a5f4a707..9a13a12670 100644 --- a/gfx/skia/skia/src/codec/SkCodec_libpng.h +++ b/gfx/skia/skia/src/codec/SkCodec_libpng.h @@ -19,7 +19,7 @@ class SkStream; class SkPngCodec : public SkCodec { public: - static bool IsPng(SkStream*); + static bool IsPng(const char*, size_t); // Assume IsPng was called and returned true. static SkCodec* NewFromStream(SkStream*, SkPngChunkReader* = NULL); @@ -32,7 +32,6 @@ protected: SkEncodedFormat onGetEncodedFormat() const override { return kPNG_SkEncodedFormat; } bool onRewind() override; uint32_t onGetFillValue(SkColorType colorType, SkAlphaType alphaType) const override; - bool onReallyHasAlpha() const final; // Helper to set up swizzler and color table. Also calls png_read_update_info. Result initializeSwizzler(const SkImageInfo& requestedInfo, const Options&, @@ -49,19 +48,6 @@ protected: SkSwizzler::SrcConfig srcConfig() const { return fSrcConfig; } int numberPasses() const { return fNumberPasses; } - enum AlphaState { - // This class has done no decoding, or threw away its knowledge (in - // scanline decodes). - kUnknown_AlphaState, - // This class found the image (possibly partial, in the case of a - // scanline decode) to be opaque. - kOpaque_AlphaState, - // Ths class found the image to have alpha. - kHasAlpha_AlphaState, - }; - - virtual AlphaState alphaInScanlineDecode() const = 0; - private: SkAutoTUnref fPngChunkReader; png_structp fPng_ptr; @@ -74,7 +60,6 @@ private: SkSwizzler::SrcConfig fSrcConfig; const int fNumberPasses; int fBitDepth; - AlphaState fAlphaState; bool decodePalette(bool premultiply, int* ctableCount); void destroyReadStruct(); diff --git a/gfx/skia/skia/src/codec/SkCodec_libgif.cpp b/gfx/skia/skia/src/codec/SkGifCodec.cpp similarity index 99% rename from gfx/skia/skia/src/codec/SkCodec_libgif.cpp rename to gfx/skia/skia/src/codec/SkGifCodec.cpp index 8021af95a2..92470bf3a0 100644 --- a/gfx/skia/skia/src/codec/SkCodec_libgif.cpp +++ b/gfx/skia/skia/src/codec/SkGifCodec.cpp @@ -5,10 +5,10 @@ * found in the LICENSE file. */ -#include "SkCodec_libgif.h" #include "SkCodecPriv.h" #include "SkColorPriv.h" #include "SkColorTable.h" +#include "SkGifCodec.h" #include "SkStream.h" #include "SkSwizzler.h" #include "SkUtils.h" @@ -16,9 +16,8 @@ /* * Checks the start of the stream to see if the image is a gif */ -bool SkGifCodec::IsGif(SkStream* stream) { - char buf[GIF_STAMP_LEN]; - if (stream->read(buf, GIF_STAMP_LEN) == GIF_STAMP_LEN) { +bool SkGifCodec::IsGif(const void* buf, size_t bytesRead) { + if (bytesRead >= GIF_STAMP_LEN) { if (memcmp(GIF_STAMP, buf, GIF_STAMP_LEN) == 0 || memcmp(GIF87_STAMP, buf, GIF_STAMP_LEN) == 0 || memcmp(GIF89_STAMP, buf, GIF_STAMP_LEN) == 0) @@ -508,7 +507,7 @@ SkCodec::Result SkGifCodec::onStartScanlineDecode(const SkImageInfo& dstInfo, void SkGifCodec::handleScanlineFrame(int count, int* rowsBeforeFrame, int* rowsInFrame) { if (fFrameIsSubset) { - const int currRow = this->INHERITED::nextScanline(); + const int currRow = this->currScanline(); // The number of rows that remain to be skipped before reaching rows that we // actually must decode into. diff --git a/gfx/skia/skia/src/codec/SkCodec_libgif.h b/gfx/skia/skia/src/codec/SkGifCodec.h similarity index 98% rename from gfx/skia/skia/src/codec/SkCodec_libgif.h rename to gfx/skia/skia/src/codec/SkGifCodec.h index 200f176e32..ba48989cbb 100644 --- a/gfx/skia/skia/src/codec/SkCodec_libgif.h +++ b/gfx/skia/skia/src/codec/SkGifCodec.h @@ -19,11 +19,7 @@ */ class SkGifCodec : public SkCodec { public: - - /* - * Checks the start of the stream to see if the image is a gif - */ - static bool IsGif(SkStream*); + static bool IsGif(const void*, size_t); /* * Assumes IsGif was called and returned true diff --git a/gfx/skia/skia/src/codec/SkCodec_libico.cpp b/gfx/skia/skia/src/codec/SkIcoCodec.cpp similarity index 65% rename from gfx/skia/skia/src/codec/SkCodec_libico.cpp rename to gfx/skia/skia/src/codec/SkIcoCodec.cpp index 8c5a1b3412..0280be304c 100644 --- a/gfx/skia/skia/src/codec/SkCodec_libico.cpp +++ b/gfx/skia/skia/src/codec/SkIcoCodec.cpp @@ -6,23 +6,59 @@ */ #include "SkBmpCodec.h" -#include "SkCodec_libico.h" #include "SkCodec_libpng.h" #include "SkCodecPriv.h" #include "SkColorPriv.h" #include "SkData.h" +#include "SkIcoCodec.h" #include "SkStream.h" #include "SkTDArray.h" #include "SkTSort.h" +static bool ico_conversion_possible(const SkImageInfo& dstInfo) { + // We only support kN32_SkColorType. + // This makes sense for BMP-in-ICO. The presence of an AND + // mask (which changes colors and adds transparency) means that + // we cannot use k565 or kIndex8. + // FIXME: For PNG-in-ICO, we could technically support whichever + // color types that the png supports. + if (kN32_SkColorType != dstInfo.colorType()) { + return false; + } + + // We only support transparent alpha types. This is necessary for + // BMP-in-ICOs since there will be an AND mask. + // FIXME: For opaque PNG-in-ICOs, we should be able to support kOpaque. + return kPremul_SkAlphaType == dstInfo.alphaType() || + kUnpremul_SkAlphaType == dstInfo.alphaType(); +} + +static SkImageInfo fix_embedded_alpha(const SkImageInfo& dstInfo, SkAlphaType embeddedAlpha) { + // FIXME (msarett): ICO is considered non-opaque, even if the embedded BMP + // incorrectly claims it has no alpha. + switch (embeddedAlpha) { + case kPremul_SkAlphaType: + case kUnpremul_SkAlphaType: + // Use the requested alpha type if the embedded codec supports alpha. + embeddedAlpha = dstInfo.alphaType(); + break; + case kOpaque_SkAlphaType: + // If the embedded codec claims it is opaque, decode as if it is opaque. + break; + default: + SkASSERT(false); + break; + } + return dstInfo.makeAlphaType(embeddedAlpha); +} + /* * Checks the start of the stream to see if the image is an Ico or Cur */ -bool SkIcoCodec::IsIco(SkStream* stream) { +bool SkIcoCodec::IsIco(const void* buffer, size_t bytesRead) { const char icoSig[] = { '\x00', '\x00', '\x01', '\x00' }; const char curSig[] = { '\x00', '\x00', '\x02', '\x00' }; - char buffer[sizeof(icoSig)]; - return stream->read(buffer, sizeof(icoSig)) == sizeof(icoSig) && + return bytesRead >= sizeof(icoSig) && (!memcmp(buffer, icoSig, sizeof(icoSig)) || !memcmp(buffer, curSig, sizeof(curSig))); } @@ -139,10 +175,8 @@ SkCodec* SkIcoCodec::NewFromStream(SkStream* stream) { bytesRead += size; // Check if the embedded codec is bmp or png and create the codec - const bool isPng = SkPngCodec::IsPng(embeddedStream); - SkAssertResult(embeddedStream->rewind()); SkCodec* codec = nullptr; - if (isPng) { + if (SkPngCodec::IsPng((const char*) data->bytes(), data->size())) { codec = SkPngCodec::NewFromStream(embeddedStream.detach()); } else { codec = SkBmpCodec::NewFromIco(embeddedStream.detach()); @@ -197,6 +231,7 @@ SkIcoCodec::SkIcoCodec(const SkImageInfo& info, SkTArray, true>* codecs) : INHERITED(info, nullptr) , fEmbeddedCodecs(codecs) + , fCurrScanlineCodec(nullptr) {} /* @@ -226,15 +261,21 @@ SkISize SkIcoCodec::onGetScaledDimensions(float desiredScale) const { return fEmbeddedCodecs->operator[](minIndex)->getInfo().dimensions(); } -bool SkIcoCodec::onDimensionsSupported(const SkISize& dim) { +int SkIcoCodec::chooseCodec(const SkISize& requestedSize, int startIndex) { + SkASSERT(startIndex >= 0); + // FIXME: Cache the index from onGetScaledDimensions? - for (int32_t i = 0; i < fEmbeddedCodecs->count(); i++) { - if (fEmbeddedCodecs->operator[](i)->getInfo().dimensions() == dim) { - return true; + for (int i = startIndex; i < fEmbeddedCodecs->count(); i++) { + if (fEmbeddedCodecs->operator[](i)->getInfo().dimensions() == requestedSize) { + return i; } } - return false; + return -1; +} + +bool SkIcoCodec::onDimensionsSupported(const SkISize& dim) { + return this->chooseCodec(dim, 0) >= 0; } /* @@ -249,54 +290,89 @@ SkCodec::Result SkIcoCodec::onGetPixels(const SkImageInfo& dstInfo, return kUnimplemented; } - if (!valid_alpha(dstInfo.alphaType(), this->getInfo().alphaType())) { + if (!ico_conversion_possible(dstInfo)) { return kInvalidConversion; } - // We return invalid scale if there is no candidate image with matching - // dimensions. - Result result = kInvalidScale; - for (int32_t i = 0; i < fEmbeddedCodecs->count(); i++) { - SkCodec* embeddedCodec = fEmbeddedCodecs->operator[](i); - // If the dimensions match, try to decode - if (dstInfo.dimensions() == embeddedCodec->getInfo().dimensions()) { - - // Perform the decode - // FIXME (msarett): ICO is considered non-opaque, even if the embedded BMP - // incorrectly claims it has no alpha. - SkAlphaType embeddedAlpha = embeddedCodec->getInfo().alphaType(); - switch (embeddedAlpha) { - case kPremul_SkAlphaType: - case kUnpremul_SkAlphaType: - // Use the requested alpha type if the embedded codec supports alpha. - embeddedAlpha = dstInfo.alphaType(); - break; - case kOpaque_SkAlphaType: - // If the embedded codec claims it is opaque, decode as if it is opaque. - break; - default: - SkASSERT(false); - break; - } - SkImageInfo info = dstInfo.makeAlphaType(embeddedAlpha); - result = embeddedCodec->getPixels(info, dst, dstRowBytes, &opts, colorTable, - colorCount); - // The embedded codec will handle filling incomplete images, so we will indicate - // that all of the rows are initialized. - *rowsDecoded = info.height(); - - // On a fatal error, keep trying to find an image to decode - if (kInvalidConversion == result || kInvalidInput == result || - kInvalidScale == result) { - SkCodecPrintf("Warning: Attempt to decode candidate ico failed.\n"); - continue; - } - - // On success or partial success, return the result - return result; + int index = 0; + SkCodec::Result result = kInvalidScale; + while (true) { + index = this->chooseCodec(dstInfo.dimensions(), index); + if (index < 0) { + break; } + + SkCodec* embeddedCodec = fEmbeddedCodecs->operator[](index); + SkImageInfo decodeInfo = fix_embedded_alpha(dstInfo, embeddedCodec->getInfo().alphaType()); + SkASSERT(decodeInfo.colorType() == kN32_SkColorType); + result = embeddedCodec->getPixels(decodeInfo, dst, dstRowBytes, &opts, colorTable, + colorCount); + + switch (result) { + case kSuccess: + case kIncompleteInput: + // The embedded codec will handle filling incomplete images, so we will indicate + // that all of the rows are initialized. + *rowsDecoded = decodeInfo.height(); + return result; + default: + // Continue trying to find a valid embedded codec on a failed decode. + break; + } + + index++; } SkCodecPrintf("Error: No matching candidate image in ico.\n"); return result; } + +SkCodec::Result SkIcoCodec::onStartScanlineDecode(const SkImageInfo& dstInfo, + const SkCodec::Options& options, SkPMColor colorTable[], int* colorCount) { + if (!ico_conversion_possible(dstInfo)) { + return kInvalidConversion; + } + + int index = 0; + SkCodec::Result result = kInvalidScale; + while (true) { + index = this->chooseCodec(dstInfo.dimensions(), index); + if (index < 0) { + break; + } + + SkCodec* embeddedCodec = fEmbeddedCodecs->operator[](index); + SkImageInfo decodeInfo = fix_embedded_alpha(dstInfo, embeddedCodec->getInfo().alphaType()); + result = embeddedCodec->startScanlineDecode(decodeInfo, &options, colorTable, colorCount); + if (kSuccess == result) { + fCurrScanlineCodec = embeddedCodec; + return result; + } + + index++; + } + + SkCodecPrintf("Error: No matching candidate image in ico.\n"); + return result; +} + +int SkIcoCodec::onGetScanlines(void* dst, int count, size_t rowBytes) { + SkASSERT(fCurrScanlineCodec); + return fCurrScanlineCodec->getScanlines(dst, count, rowBytes); +} + +bool SkIcoCodec::onSkipScanlines(int count) { + SkASSERT(fCurrScanlineCodec); + return fCurrScanlineCodec->skipScanlines(count); +} + +SkCodec::SkScanlineOrder SkIcoCodec::onGetScanlineOrder() const { + // FIXME: This function will possibly return the wrong value if it is called + // before startScanlineDecode(). + return fCurrScanlineCodec ? fCurrScanlineCodec->getScanlineOrder() : + INHERITED::onGetScanlineOrder(); +} + +SkSampler* SkIcoCodec::getSampler(bool createIfNecessary) { + return fCurrScanlineCodec ? fCurrScanlineCodec->getSampler(createIfNecessary) : nullptr; +} diff --git a/gfx/skia/skia/src/codec/SkIcoCodec.h b/gfx/skia/skia/src/codec/SkIcoCodec.h new file mode 100644 index 0000000000..9a3f248af5 --- /dev/null +++ b/gfx/skia/skia/src/codec/SkIcoCodec.h @@ -0,0 +1,87 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkCodec.h" +#include "SkImageInfo.h" +#include "SkStream.h" +#include "SkTypes.h" + +/* + * This class implements the decoding for bmp images + */ +class SkIcoCodec : public SkCodec { +public: + static bool IsIco(const void*, size_t); + + /* + * Assumes IsIco was called and returned true + * Creates an Ico decoder + * Reads enough of the stream to determine the image format + */ + static SkCodec* NewFromStream(SkStream*); + +protected: + + /* + * Chooses the best dimensions given the desired scale + */ + SkISize onGetScaledDimensions(float desiredScale) const override; + + bool onDimensionsSupported(const SkISize&) override; + + /* + * Initiates the Ico decode + */ + Result onGetPixels(const SkImageInfo& dstInfo, void* dst, size_t dstRowBytes, const Options&, + SkPMColor*, int*, int*) override; + + SkEncodedFormat onGetEncodedFormat() const override { + return kICO_SkEncodedFormat; + } + + SkScanlineOrder onGetScanlineOrder() const override; + +private: + + Result onStartScanlineDecode(const SkImageInfo& dstInfo, const SkCodec::Options& options, + SkPMColor inputColorPtr[], int* inputColorCount) override; + + int onGetScanlines(void* dst, int count, size_t rowBytes) override; + + bool onSkipScanlines(int count) override; + + SkSampler* getSampler(bool createIfNecessary) override; + + /* + * Searches fEmbeddedCodecs for a codec that matches requestedSize. + * The search starts at startIndex and ends when an appropriate codec + * is found, or we have reached the end of the array. + * + * @return the index of the matching codec or -1 if there is no + * matching codec between startIndex and the end of + * the array. + */ + int chooseCodec(const SkISize& requestedSize, int startIndex); + + /* + * Constructor called by NewFromStream + * @param embeddedCodecs codecs for the embedded images, takes ownership + */ + SkIcoCodec(const SkImageInfo& srcInfo, SkTArray, true>* embeddedCodecs); + + SkAutoTDelete, true>> fEmbeddedCodecs; // owned + + // Only used by the scanline decoder. onStartScanlineDecode() will set + // fCurrScanlineCodec to one of the fEmbeddedCodecs, if it can find a + // codec of the appropriate size. We will use fCurrScanlineCodec for + // subsequent calls to onGetScanlines() or onSkipScanlines(). + // fCurrScanlineCodec is owned by this class, but should not be an + // SkAutoTDelete. It will be deleted by the destructor of fEmbeddedCodecs. + SkCodec* fCurrScanlineCodec; + + typedef SkCodec INHERITED; +}; diff --git a/gfx/skia/skia/src/codec/SkJpegCodec.cpp b/gfx/skia/skia/src/codec/SkJpegCodec.cpp index 6e2fc84ec8..7db772da63 100644 --- a/gfx/skia/skia/src/codec/SkJpegCodec.cpp +++ b/gfx/skia/skia/src/codec/SkJpegCodec.cpp @@ -23,11 +23,9 @@ extern "C" { #include "jpeglib.h" } -bool SkJpegCodec::IsJpeg(SkStream* stream) { +bool SkJpegCodec::IsJpeg(const void* buffer, size_t bytesRead) { static const uint8_t jpegSig[] = { 0xFF, 0xD8, 0xFF }; - char buffer[sizeof(jpegSig)]; - return stream->read(buffer, sizeof(jpegSig)) == sizeof(jpegSig) && - !memcmp(buffer, jpegSig, sizeof(jpegSig)); + return bytesRead >= 3 && !memcmp(buffer, jpegSig, sizeof(jpegSig)); } bool SkJpegCodec::ReadHeader(SkStream* stream, SkCodec** codecOut, @@ -352,12 +350,12 @@ void SkJpegCodec::initializeSwizzler(const SkImageInfo& dstInfo, const Options& fSwizzler.reset(SkSwizzler::CreateSwizzler(srcConfig, nullptr, dstInfo, options)); fStorage.reset(get_row_bytes(fDecoderMgr->dinfo())); - fSrcRow = static_cast(fStorage.get()); + fSrcRow = fStorage.get(); } SkSampler* SkJpegCodec::getSampler(bool createIfNecessary) { if (!createIfNecessary || fSwizzler) { - SkASSERT(!fSwizzler || (fSrcRow && static_cast(fStorage.get()) == fSrcRow)); + SkASSERT(!fSwizzler || (fSrcRow && fStorage.get() == fSrcRow)); return fSwizzler; } @@ -435,8 +433,8 @@ int SkJpegCodec::onGetScanlines(void* dst, int count, size_t rowBytes) { #ifndef TURBO_HAS_SKIP // TODO (msarett): Avoid reallocating the memory buffer on each call to skip. static uint32_t jpeg_skip_scanlines(jpeg_decompress_struct* dinfo, int count) { - SkAutoMalloc storage(get_row_bytes(dinfo)); - uint8_t* storagePtr = static_cast(storage.get()); + SkAutoTMalloc storage(get_row_bytes(dinfo)); + uint8_t* storagePtr = storage.get(); for (int y = 0; y < count; y++) { if (1 != jpeg_read_scanlines(dinfo, &storagePtr, 1)) { return y; diff --git a/gfx/skia/skia/src/codec/SkJpegCodec.h b/gfx/skia/skia/src/codec/SkJpegCodec.h index 687cf4b4b8..8e2db81b73 100644 --- a/gfx/skia/skia/src/codec/SkJpegCodec.h +++ b/gfx/skia/skia/src/codec/SkJpegCodec.h @@ -13,6 +13,7 @@ #include "SkJpegDecoderMgr.h" #include "SkJpegUtility_codec.h" #include "SkStream.h" +#include "SkTemplates.h" extern "C" { #include "jpeglib.h" @@ -25,12 +26,7 @@ extern "C" { */ class SkJpegCodec : public SkCodec { public: - - /* - * Checks the start of the stream to see if the image is a jpeg - * Does not take ownership of the stream - */ - static bool IsJpeg(SkStream*); + static bool IsJpeg(const void*, size_t); /* * Assumes IsJpeg was called and returned true @@ -116,7 +112,7 @@ private: const int fReadyState; // scanline decoding - SkAutoMalloc fStorage; // Only used if sampling is needed + SkAutoTMalloc fStorage; // Only used if sampling is needed uint8_t* fSrcRow; // Only used if sampling is needed SkAutoTDelete fSwizzler; diff --git a/gfx/skia/skia/src/codec/SkMaskSwizzler.cpp b/gfx/skia/skia/src/codec/SkMaskSwizzler.cpp index e5facc1f0c..01502cbd4a 100644 --- a/gfx/skia/skia/src/codec/SkMaskSwizzler.cpp +++ b/gfx/skia/skia/src/codec/SkMaskSwizzler.cpp @@ -9,7 +9,7 @@ #include "SkColorPriv.h" #include "SkMaskSwizzler.h" -static SkSwizzler::ResultAlpha swizzle_mask16_to_n32_opaque( +static void swizzle_mask16_to_n32_opaque( void* dstRow, const uint8_t* srcRow, int width, SkMasks* masks, uint32_t startX, uint32_t sampleX) { @@ -24,54 +24,47 @@ static SkSwizzler::ResultAlpha swizzle_mask16_to_n32_opaque( dstPtr[i] = SkPackARGB32NoCheck(0xFF, red, green, blue); srcPtr += sampleX; } - return SkSwizzler::kOpaque_ResultAlpha; } -static SkSwizzler::ResultAlpha swizzle_mask16_to_n32_unpremul( +static void swizzle_mask16_to_n32_unpremul( void* dstRow, const uint8_t* srcRow, int width, SkMasks* masks, uint32_t startX, uint32_t sampleX) { // Use the masks to decode to the destination uint16_t* srcPtr = ((uint16_t*) srcRow) + startX; SkPMColor* dstPtr = (SkPMColor*) dstRow; - INIT_RESULT_ALPHA; for (int i = 0; i < width; i++) { uint16_t p = srcPtr[0]; uint8_t red = masks->getRed(p); uint8_t green = masks->getGreen(p); uint8_t blue = masks->getBlue(p); uint8_t alpha = masks->getAlpha(p); - UPDATE_RESULT_ALPHA(alpha); dstPtr[i] = SkPackARGB32NoCheck(alpha, red, green, blue); srcPtr += sampleX; } - return COMPUTE_RESULT_ALPHA; } -static SkSwizzler::ResultAlpha swizzle_mask16_to_n32_premul( +static void swizzle_mask16_to_n32_premul( void* dstRow, const uint8_t* srcRow, int width, SkMasks* masks, uint32_t startX, uint32_t sampleX) { // Use the masks to decode to the destination uint16_t* srcPtr = ((uint16_t*) srcRow) + startX; SkPMColor* dstPtr = (SkPMColor*) dstRow; - INIT_RESULT_ALPHA; for (int i = 0; i < width; i++) { uint16_t p = srcPtr[0]; uint8_t red = masks->getRed(p); uint8_t green = masks->getGreen(p); uint8_t blue = masks->getBlue(p); uint8_t alpha = masks->getAlpha(p); - UPDATE_RESULT_ALPHA(alpha); dstPtr[i] = SkPreMultiplyARGB(alpha, red, green, blue); srcPtr += sampleX; } - return COMPUTE_RESULT_ALPHA; } // TODO (msarett): We have promoted a two byte per pixel image to 8888, only to // convert it back to 565. Instead, we should swizzle to 565 directly. -static SkSwizzler::ResultAlpha swizzle_mask16_to_565( +static void swizzle_mask16_to_565( void* dstRow, const uint8_t* srcRow, int width, SkMasks* masks, uint32_t startX, uint32_t sampleX) { @@ -86,10 +79,9 @@ static SkSwizzler::ResultAlpha swizzle_mask16_to_565( dstPtr[i] = SkPack888ToRGB16(red, green, blue); srcPtr += sampleX; } - return SkSwizzler::kOpaque_ResultAlpha; } -static SkSwizzler::ResultAlpha swizzle_mask24_to_n32_opaque( +static void swizzle_mask24_to_n32_opaque( void* dstRow, const uint8_t* srcRow, int width, SkMasks* masks, uint32_t startX, uint32_t sampleX) { @@ -104,52 +96,45 @@ static SkSwizzler::ResultAlpha swizzle_mask24_to_n32_opaque( dstPtr[i] = SkPackARGB32NoCheck(0xFF, red, green, blue); srcRow += 3 * sampleX; } - return SkSwizzler::kOpaque_ResultAlpha; } -static SkSwizzler::ResultAlpha swizzle_mask24_to_n32_unpremul( +static void swizzle_mask24_to_n32_unpremul( void* dstRow, const uint8_t* srcRow, int width, SkMasks* masks, uint32_t startX, uint32_t sampleX) { // Use the masks to decode to the destination srcRow += 3 * startX; SkPMColor* dstPtr = (SkPMColor*) dstRow; - INIT_RESULT_ALPHA; for (int i = 0; i < width; i++) { uint32_t p = srcRow[0] | (srcRow[1] << 8) | srcRow[2] << 16; uint8_t red = masks->getRed(p); uint8_t green = masks->getGreen(p); uint8_t blue = masks->getBlue(p); uint8_t alpha = masks->getAlpha(p); - UPDATE_RESULT_ALPHA(alpha); dstPtr[i] = SkPackARGB32NoCheck(alpha, red, green, blue); srcRow += 3 * sampleX; } - return COMPUTE_RESULT_ALPHA; } -static SkSwizzler::ResultAlpha swizzle_mask24_to_n32_premul( +static void swizzle_mask24_to_n32_premul( void* dstRow, const uint8_t* srcRow, int width, SkMasks* masks, uint32_t startX, uint32_t sampleX) { // Use the masks to decode to the destination srcRow += 3 * startX; SkPMColor* dstPtr = (SkPMColor*) dstRow; - INIT_RESULT_ALPHA; for (int i = 0; i < width; i++) { uint32_t p = srcRow[0] | (srcRow[1] << 8) | srcRow[2] << 16; uint8_t red = masks->getRed(p); uint8_t green = masks->getGreen(p); uint8_t blue = masks->getBlue(p); uint8_t alpha = masks->getAlpha(p); - UPDATE_RESULT_ALPHA(alpha); dstPtr[i] = SkPreMultiplyARGB(alpha, red, green, blue); srcRow += 3 * sampleX; } - return COMPUTE_RESULT_ALPHA; } -static SkSwizzler::ResultAlpha swizzle_mask24_to_565( +static void swizzle_mask24_to_565( void* dstRow, const uint8_t* srcRow, int width, SkMasks* masks, uint32_t startX, uint32_t sampleX) { @@ -164,10 +149,9 @@ static SkSwizzler::ResultAlpha swizzle_mask24_to_565( dstPtr[i] = SkPack888ToRGB16(red, green, blue); srcRow += 3 * sampleX; } - return SkSwizzler::kOpaque_ResultAlpha; } -static SkSwizzler::ResultAlpha swizzle_mask32_to_n32_opaque( +static void swizzle_mask32_to_n32_opaque( void* dstRow, const uint8_t* srcRow, int width, SkMasks* masks, uint32_t startX, uint32_t sampleX) { @@ -182,52 +166,45 @@ static SkSwizzler::ResultAlpha swizzle_mask32_to_n32_opaque( dstPtr[i] = SkPackARGB32NoCheck(0xFF, red, green, blue); srcPtr += sampleX; } - return SkSwizzler::kOpaque_ResultAlpha; } -static SkSwizzler::ResultAlpha swizzle_mask32_to_n32_unpremul( +static void swizzle_mask32_to_n32_unpremul( void* dstRow, const uint8_t* srcRow, int width, SkMasks* masks, uint32_t startX, uint32_t sampleX) { // Use the masks to decode to the destination uint32_t* srcPtr = ((uint32_t*) srcRow) + startX; SkPMColor* dstPtr = (SkPMColor*) dstRow; - INIT_RESULT_ALPHA; for (int i = 0; i < width; i++) { uint32_t p = srcPtr[0]; uint8_t red = masks->getRed(p); uint8_t green = masks->getGreen(p); uint8_t blue = masks->getBlue(p); uint8_t alpha = masks->getAlpha(p); - UPDATE_RESULT_ALPHA(alpha); dstPtr[i] = SkPackARGB32NoCheck(alpha, red, green, blue); srcPtr += sampleX; } - return COMPUTE_RESULT_ALPHA; } -static SkSwizzler::ResultAlpha swizzle_mask32_to_n32_premul( +static void swizzle_mask32_to_n32_premul( void* dstRow, const uint8_t* srcRow, int width, SkMasks* masks, uint32_t startX, uint32_t sampleX) { // Use the masks to decode to the destination uint32_t* srcPtr = ((uint32_t*) srcRow) + startX; SkPMColor* dstPtr = (SkPMColor*) dstRow; - INIT_RESULT_ALPHA; for (int i = 0; i < width; i++) { uint32_t p = srcPtr[0]; uint8_t red = masks->getRed(p); uint8_t green = masks->getGreen(p); uint8_t blue = masks->getBlue(p); uint8_t alpha = masks->getAlpha(p); - UPDATE_RESULT_ALPHA(alpha); dstPtr[i] = SkPreMultiplyARGB(alpha, red, green, blue); srcPtr += sampleX; } - return COMPUTE_RESULT_ALPHA; } -static SkSwizzler::ResultAlpha swizzle_mask32_to_565( +static void swizzle_mask32_to_565( void* dstRow, const uint8_t* srcRow, int width, SkMasks* masks, uint32_t startX, uint32_t sampleX) { // Use the masks to decode to the destination @@ -241,7 +218,6 @@ static SkSwizzler::ResultAlpha swizzle_mask32_to_565( dstPtr[i] = SkPack888ToRGB16(red, green, blue); srcPtr += sampleX; } - return SkSwizzler::kOpaque_ResultAlpha; } /* @@ -394,7 +370,7 @@ int SkMaskSwizzler::onSetSampleX(int sampleX) { * Swizzle the specified row * */ -SkSwizzler::ResultAlpha SkMaskSwizzler::swizzle(void* dst, const uint8_t* SK_RESTRICT src) { +void SkMaskSwizzler::swizzle(void* dst, const uint8_t* SK_RESTRICT src) { SkASSERT(nullptr != dst && nullptr != src); - return fRowProc(dst, src, fDstWidth, fMasks, fX0, fSampleX); + fRowProc(dst, src, fDstWidth, fMasks, fX0, fSampleX); } diff --git a/gfx/skia/skia/src/codec/SkMaskSwizzler.h b/gfx/skia/skia/src/codec/SkMaskSwizzler.h index e5da723ca5..1dc1918f49 100644 --- a/gfx/skia/skia/src/codec/SkMaskSwizzler.h +++ b/gfx/skia/skia/src/codec/SkMaskSwizzler.h @@ -34,7 +34,7 @@ public: /* * Swizzle a row */ - SkSwizzler::ResultAlpha swizzle(void* dst, const uint8_t* SK_RESTRICT src); + void swizzle(void* dst, const uint8_t* SK_RESTRICT src); /** * Implement fill using a custom width. @@ -50,7 +50,7 @@ private: /* * Row procedure used for swizzle */ - typedef SkSwizzler::ResultAlpha (*RowProc)(void* dstRow, const uint8_t* srcRow, int width, + typedef void (*RowProc)(void* dstRow, const uint8_t* srcRow, int width, SkMasks* masks, uint32_t startX, uint32_t sampleX); SkMaskSwizzler(SkMasks* masks, RowProc proc, int subsetWidth, int srcOffset); diff --git a/gfx/skia/skia/src/codec/SkSampledCodec.cpp b/gfx/skia/skia/src/codec/SkSampledCodec.cpp index 52e5648742..e52470505d 100644 --- a/gfx/skia/skia/src/codec/SkSampledCodec.cpp +++ b/gfx/skia/skia/src/codec/SkSampledCodec.cpp @@ -9,14 +9,15 @@ #include "SkCodecPriv.h" #include "SkMath.h" #include "SkSampledCodec.h" +#include "SkSampler.h" +#include "SkTemplates.h" SkSampledCodec::SkSampledCodec(SkCodec* codec) - : INHERITED(codec->getInfo()) - , fCodec(codec) + : INHERITED(codec) {} SkISize SkSampledCodec::accountForNativeScaling(int* sampleSizePtr, int* nativeSampleSize) const { - SkISize preSampledSize = fCodec->getInfo().dimensions(); + SkISize preSampledSize = this->codec()->getInfo().dimensions(); int sampleSize = *sampleSizePtr; SkASSERT(sampleSize > 1); @@ -25,7 +26,7 @@ SkISize SkSampledCodec::accountForNativeScaling(int* sampleSizePtr, int* nativeS } // Only JPEG supports native downsampling. - if (fCodec->getEncodedFormat() == kJPEG_SkEncodedFormat) { + if (this->codec()->getEncodedFormat() == kJPEG_SkEncodedFormat) { // See if libjpeg supports this scale directly switch (sampleSize) { case 2: @@ -33,7 +34,7 @@ SkISize SkSampledCodec::accountForNativeScaling(int* sampleSizePtr, int* nativeS case 8: // This class does not need to do any sampling. *sampleSizePtr = 1; - return fCodec->getScaledDimensions(get_scale_from_sample_size(sampleSize)); + return this->codec()->getScaledDimensions(get_scale_from_sample_size(sampleSize)); default: break; } @@ -47,8 +48,8 @@ SkISize SkSampledCodec::accountForNativeScaling(int* sampleSizePtr, int* nativeS if (0 == remainder) { float scale = get_scale_from_sample_size(supportedSampleSize); - // fCodec will scale to this size. - preSampledSize = fCodec->getScaledDimensions(scale); + // this->codec() will scale to this size. + preSampledSize = this->codec()->getScaledDimensions(scale); // And then this class will sample it. *sampleSizePtr = actualSampleSize; @@ -76,10 +77,10 @@ SkCodec::Result SkSampledCodec::onGetAndroidPixels(const SkImageInfo& info, void codecOptions.fZeroInitialized = options.fZeroInitialized; SkIRect* subset = options.fSubset; - if (!subset || subset->size() == fCodec->getInfo().dimensions()) { - if (fCodec->dimensionsSupported(info.dimensions())) { - return fCodec->getPixels(info, pixels, rowBytes, &codecOptions, options.fColorPtr, - options.fColorCount); + if (!subset || subset->size() == this->codec()->getInfo().dimensions()) { + if (this->codec()->dimensionsSupported(info.dimensions())) { + return this->codec()->getPixels(info, pixels, rowBytes, &codecOptions, + options.fColorPtr, options.fColorCount); } // If the native codec does not support the requested scale, scale by sampling. @@ -89,7 +90,7 @@ SkCodec::Result SkSampledCodec::onGetAndroidPixels(const SkImageInfo& info, void // We are performing a subset decode. int sampleSize = options.fSampleSize; SkISize scaledSize = this->getSampledDimensions(sampleSize); - if (!fCodec->dimensionsSupported(scaledSize)) { + if (!this->codec()->dimensionsSupported(scaledSize)) { // If the native codec does not support the requested scale, scale by sampling. return this->sampledDecode(info, pixels, rowBytes, options); } @@ -104,24 +105,24 @@ SkCodec::Result SkSampledCodec::onGetAndroidPixels(const SkImageInfo& info, void SkIRect scanlineSubset = SkIRect::MakeXYWH(scaledSubsetX, 0, scaledSubsetWidth, scaledSize.height()); codecOptions.fSubset = &scanlineSubset; - SkCodec::Result result = fCodec->startScanlineDecode(info.makeWH(scaledSize.width(), + SkCodec::Result result = this->codec()->startScanlineDecode(info.makeWH(scaledSize.width(), scaledSize.height()), &codecOptions, options.fColorPtr, options.fColorCount); if (SkCodec::kSuccess != result) { return result; } // At this point, we are only concerned with subsetting. Either no scale was - // requested, or the fCodec is handling the scale. - switch (fCodec->getScanlineOrder()) { + // requested, or the this->codec() is handling the scale. + switch (this->codec()->getScanlineOrder()) { case SkCodec::kTopDown_SkScanlineOrder: case SkCodec::kNone_SkScanlineOrder: { - if (!fCodec->skipScanlines(scaledSubsetY)) { - fCodec->fillIncompleteImage(info, pixels, rowBytes, options.fZeroInitialized, + if (!this->codec()->skipScanlines(scaledSubsetY)) { + this->codec()->fillIncompleteImage(info, pixels, rowBytes, options.fZeroInitialized, scaledSubsetHeight, 0); return SkCodec::kIncompleteInput; } - int decodedLines = fCodec->getScanlines(pixels, scaledSubsetHeight, rowBytes); + int decodedLines = this->codec()->getScanlines(pixels, scaledSubsetHeight, rowBytes); if (decodedLines != scaledSubsetHeight) { return SkCodec::kIncompleteInput; } @@ -156,7 +157,7 @@ SkCodec::Result SkSampledCodec::sampledDecode(const SkImageInfo& info, void* pix if (options.fSubset) { // We will need to know about subsetting in the y-dimension in order to use the // scanline decoder. - // Update the subset to account for scaling done by fCodec. + // Update the subset to account for scaling done by this->codec(). SkIRect* subsetPtr = options.fSubset; // Do the divide ourselves, instead of calling get_scaled_dimension. If @@ -174,14 +175,14 @@ SkCodec::Result SkSampledCodec::sampledDecode(const SkImageInfo& info, void* pix } // Start the scanline decode. - SkCodec::Result result = fCodec->startScanlineDecode( + SkCodec::Result result = this->codec()->startScanlineDecode( info.makeWH(nativeSize.width(), nativeSize.height()), &sampledOptions, options.fColorPtr, options.fColorCount); if (SkCodec::kSuccess != result) { return result; } - SkSampler* sampler = fCodec->getSampler(true); + SkSampler* sampler = this->codec()->getSampler(true); if (!sampler) { return SkCodec::kUnimplemented; } @@ -201,23 +202,23 @@ SkCodec::Result SkSampledCodec::sampledDecode(const SkImageInfo& info, void* pix const int samplingOffsetY = get_start_coord(sampleY); const int startY = samplingOffsetY + subsetY; int dstHeight = info.height(); - switch(fCodec->getScanlineOrder()) { + switch(this->codec()->getScanlineOrder()) { case SkCodec::kTopDown_SkScanlineOrder: { - if (!fCodec->skipScanlines(startY)) { - fCodec->fillIncompleteImage(info, pixels, rowBytes, options.fZeroInitialized, + if (!this->codec()->skipScanlines(startY)) { + this->codec()->fillIncompleteImage(info, pixels, rowBytes, options.fZeroInitialized, dstHeight, 0); return SkCodec::kIncompleteInput; } void* pixelPtr = pixels; for (int y = 0; y < dstHeight; y++) { - if (1 != fCodec->getScanlines(pixelPtr, 1, rowBytes)) { - fCodec->fillIncompleteImage(info, pixels, rowBytes, options.fZeroInitialized, - dstHeight, y + 1); + if (1 != this->codec()->getScanlines(pixelPtr, 1, rowBytes)) { + this->codec()->fillIncompleteImage(info, pixels, rowBytes, + options.fZeroInitialized, dstHeight, y + 1); return SkCodec::kIncompleteInput; } if (y < dstHeight - 1) { - if (!fCodec->skipScanlines(sampleY - 1)) { - fCodec->fillIncompleteImage(info, pixels, rowBytes, + if (!this->codec()->skipScanlines(sampleY - 1)) { + this->codec()->fillIncompleteImage(info, pixels, rowBytes, options.fZeroInitialized, dstHeight, y + 1); return SkCodec::kIncompleteInput; } @@ -232,15 +233,15 @@ SkCodec::Result SkSampledCodec::sampledDecode(const SkImageInfo& info, void* pix SkASSERT(0 == subsetY && nativeSize.height() == subsetHeight); int y; for (y = 0; y < nativeSize.height(); y++) { - int srcY = fCodec->nextScanline(); + int srcY = this->codec()->nextScanline(); if (is_coord_necessary(srcY, sampleY, dstHeight)) { void* pixelPtr = SkTAddOffset(pixels, rowBytes * get_dst_coord(srcY, sampleY)); - if (1 != fCodec->getScanlines(pixelPtr, 1, rowBytes)) { + if (1 != this->codec()->getScanlines(pixelPtr, 1, rowBytes)) { break; } } else { - if (!fCodec->skipScanlines(1)) { + if (!this->codec()->skipScanlines(1)) { break; } } @@ -250,12 +251,13 @@ SkCodec::Result SkSampledCodec::sampledDecode(const SkImageInfo& info, void* pix return SkCodec::kSuccess; } - // We handle filling uninitialized memory here instead of using fCodec. - // fCodec does not know that we are sampling. - const uint32_t fillValue = fCodec->getFillValue(info.colorType(), info.alphaType()); + // We handle filling uninitialized memory here instead of using this->codec(). + // this->codec() does not know that we are sampling. + const uint32_t fillValue = this->codec()->getFillValue(info.colorType(), + info.alphaType()); const SkImageInfo fillInfo = info.makeWH(info.width(), 1); for (; y < nativeSize.height(); y++) { - int srcY = fCodec->outputScanline(y); + int srcY = this->codec()->outputScanline(y); if (!is_coord_necessary(srcY, sampleY, dstHeight)) { continue; } @@ -267,15 +269,15 @@ SkCodec::Result SkSampledCodec::sampledDecode(const SkImageInfo& info, void* pix } case SkCodec::kNone_SkScanlineOrder: { const int linesNeeded = subsetHeight - samplingOffsetY; - SkAutoMalloc storage(linesNeeded * rowBytes); - uint8_t* storagePtr = static_cast(storage.get()); + SkAutoTMalloc storage(linesNeeded * rowBytes); + uint8_t* storagePtr = storage.get(); - if (!fCodec->skipScanlines(startY)) { - fCodec->fillIncompleteImage(info, pixels, rowBytes, options.fZeroInitialized, + if (!this->codec()->skipScanlines(startY)) { + this->codec()->fillIncompleteImage(info, pixels, rowBytes, options.fZeroInitialized, dstHeight, 0); return SkCodec::kIncompleteInput; } - int scanlines = fCodec->getScanlines(storagePtr, linesNeeded, rowBytes); + int scanlines = this->codec()->getScanlines(storagePtr, linesNeeded, rowBytes); for (int y = 0; y < dstHeight; y++) { memcpy(pixels, storagePtr, info.minRowBytes()); @@ -284,7 +286,7 @@ SkCodec::Result SkSampledCodec::sampledDecode(const SkImageInfo& info, void* pix } if (scanlines < dstHeight) { - // fCodec has already handled filling uninitialized memory. + // this->codec() has already handled filling uninitialized memory. return SkCodec::kIncompleteInput; } return SkCodec::kSuccess; diff --git a/gfx/skia/skia/src/codec/SkSampledCodec.h b/gfx/skia/skia/src/codec/SkSampledCodec.h index b3a59126d2..35e4f571d0 100644 --- a/gfx/skia/skia/src/codec/SkSampledCodec.h +++ b/gfx/skia/skia/src/codec/SkSampledCodec.h @@ -23,8 +23,6 @@ public: protected: - SkEncodedFormat onGetEncodedFormat() const override { return fCodec->getEncodedFormat(); }; - SkISize onGetSampledDimensions(int sampleSize) const override; bool onGetSupportedSubset(SkIRect* desiredSubset) const override { return true; } @@ -57,8 +55,6 @@ private: SkCodec::Result sampledDecode(const SkImageInfo& info, void* pixels, size_t rowBytes, const AndroidOptions& options); - SkAutoTDelete fCodec; - typedef SkAndroidCodec INHERITED; }; #endif // SkSampledCodec_DEFINED diff --git a/gfx/skia/skia/src/codec/SkSampler.h b/gfx/skia/skia/src/codec/SkSampler.h index afabc1f9eb..73e11c986e 100644 --- a/gfx/skia/skia/src/codec/SkSampler.h +++ b/gfx/skia/skia/src/codec/SkSampler.h @@ -7,6 +7,7 @@ #ifndef SkSampler_DEFINED #define SkSampler_DEFINED +#include "SkCodec.h" #include "SkTypes.h" class SkSampler : public SkNoncopyable { diff --git a/gfx/skia/skia/src/codec/SkSwizzler.cpp b/gfx/skia/skia/src/codec/SkSwizzler.cpp index d783380294..74d6c7f87f 100644 --- a/gfx/skia/skia/src/codec/SkSwizzler.cpp +++ b/gfx/skia/skia/src/codec/SkSwizzler.cpp @@ -7,19 +7,12 @@ #include "SkCodecPriv.h" #include "SkColorPriv.h" +#include "SkOpts.h" #include "SkSwizzler.h" #include "SkTemplates.h" -SkSwizzler::ResultAlpha SkSwizzler::GetResult(uint8_t zeroAlpha, - uint8_t maxAlpha) { - // In the transparent case, this returns 0x0000 - // In the opaque case, this returns 0xFFFF - // If the row is neither transparent nor opaque, returns something else - return (((uint16_t) maxAlpha) << 8) | zeroAlpha; -} - // samples the row. Does not do anything else but sampling -static SkSwizzler::ResultAlpha sample565(void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, +static void sample565(void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int width, int bpp, int deltaSrc, int offset, const SkPMColor ctable[]){ src += offset; @@ -28,8 +21,6 @@ static SkSwizzler::ResultAlpha sample565(void* SK_RESTRICT dstRow, const uint8_t dst[x] = src[1] << 8 | src[0]; src += deltaSrc; } - // 565 is always opaque - return SkSwizzler::kOpaque_ResultAlpha; } // TODO (msarett): Investigate SIMD optimizations for swizzle routines. @@ -42,7 +33,7 @@ static SkSwizzler::ResultAlpha sample565(void* SK_RESTRICT dstRow, const uint8_t // same as swizzle_bit_to_index and swizzle_bit_to_n32 except for value assigned to dst[x] -static SkSwizzler::ResultAlpha swizzle_bit_to_grayscale( +static void swizzle_bit_to_grayscale( void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int dstWidth, int bpp, int deltaSrc, int offset, const SkPMColor* /*ctable*/) { @@ -61,15 +52,13 @@ static SkSwizzler::ResultAlpha swizzle_bit_to_grayscale( currByte = *(src += bitOffset / 8); dst[x] = ((currByte >> (7-bitIndex)) & 1) ? GRAYSCALE_WHITE : GRAYSCALE_BLACK; } - - return SkSwizzler::kOpaque_ResultAlpha; } #undef GRAYSCALE_BLACK #undef GRAYSCALE_WHITE // same as swizzle_bit_to_grayscale and swizzle_bit_to_n32 except for value assigned to dst[x] -static SkSwizzler::ResultAlpha swizzle_bit_to_index( +static void swizzle_bit_to_index( void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int dstWidth, int bpp, int deltaSrc, int offset, const SkPMColor* /*ctable*/) { uint8_t* SK_RESTRICT dst = (uint8_t*) dstRow; @@ -87,12 +76,10 @@ static SkSwizzler::ResultAlpha swizzle_bit_to_index( currByte = *(src += bitOffset / 8); dst[x] = ((currByte >> (7-bitIndex)) & 1); } - - return SkSwizzler::kOpaque_ResultAlpha; } // same as swizzle_bit_to_grayscale and swizzle_bit_to_index except for value assigned to dst[x] -static SkSwizzler::ResultAlpha swizzle_bit_to_n32( +static void swizzle_bit_to_n32( void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int dstWidth, int bpp, int deltaSrc, int offset, const SkPMColor* /*ctable*/) { SkPMColor* SK_RESTRICT dst = (SkPMColor*) dstRow; @@ -110,14 +97,12 @@ static SkSwizzler::ResultAlpha swizzle_bit_to_n32( currByte = *(src += bitOffset / 8); dst[x] = ((currByte >> (7 - bitIndex)) & 1) ? SK_ColorWHITE : SK_ColorBLACK; } - - return SkSwizzler::kOpaque_ResultAlpha; } #define RGB565_BLACK 0 #define RGB565_WHITE 0xFFFF -static SkSwizzler::ResultAlpha swizzle_bit_to_565( +static void swizzle_bit_to_565( void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int dstWidth, int bpp, int deltaSrc, int offset, const SkPMColor* /*ctable*/) { uint16_t* SK_RESTRICT dst = (uint16_t*) dstRow; @@ -135,8 +120,6 @@ static SkSwizzler::ResultAlpha swizzle_bit_to_565( currByte = *(src += bitOffset / 8); dst[x] = ((currByte >> (7 - bitIndex)) & 1) ? RGB565_WHITE : RGB565_BLACK; } - - return SkSwizzler::kOpaque_ResultAlpha; } #undef RGB565_BLACK @@ -144,19 +127,17 @@ static SkSwizzler::ResultAlpha swizzle_bit_to_565( // kIndex1, kIndex2, kIndex4 -static SkSwizzler::ResultAlpha swizzle_small_index_to_index( +static void swizzle_small_index_to_index( void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int dstWidth, int bpp, int deltaSrc, int offset, const SkPMColor ctable[]) { uint8_t* dst = (uint8_t*) dstRow; - INIT_RESULT_ALPHA; src += offset / 8; int bitIndex = offset % 8; uint8_t currByte = *src; const uint8_t mask = (1 << bpp) - 1; uint8_t index = (currByte >> (8 - bpp - bitIndex)) & mask; dst[0] = index; - UPDATE_RESULT_ALPHA(ctable[index] >> SK_A32_SHIFT); for (int x = 1; x < dstWidth; x++) { int bitOffset = bitIndex + deltaSrc; @@ -164,12 +145,10 @@ static SkSwizzler::ResultAlpha swizzle_small_index_to_index( currByte = *(src += bitOffset / 8); index = (currByte >> (8 - bpp - bitIndex)) & mask; dst[x] = index; - UPDATE_RESULT_ALPHA(ctable[index] >> SK_A32_SHIFT); } - return COMPUTE_RESULT_ALPHA; } -static SkSwizzler::ResultAlpha swizzle_small_index_to_565( +static void swizzle_small_index_to_565( void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int dstWidth, int bpp, int deltaSrc, int offset, const SkPMColor ctable[]) { @@ -188,22 +167,19 @@ static SkSwizzler::ResultAlpha swizzle_small_index_to_565( index = (currByte >> (8 - bpp - bitIndex)) & mask; dst[x] = SkPixel32ToPixel16(ctable[index]); } - return SkAlphaType::kOpaque_SkAlphaType; } -static SkSwizzler::ResultAlpha swizzle_small_index_to_n32( +static void swizzle_small_index_to_n32( void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int dstWidth, int bpp, int deltaSrc, int offset, const SkPMColor ctable[]) { SkPMColor* dst = (SkPMColor*) dstRow; - INIT_RESULT_ALPHA; src += offset / 8; int bitIndex = offset % 8; uint8_t currByte = *src; const uint8_t mask = (1 << bpp) - 1; uint8_t index = (currByte >> (8 - bpp - bitIndex)) & mask; dst[0] = ctable[index]; - UPDATE_RESULT_ALPHA(ctable[index] >> SK_A32_SHIFT); for (int x = 1; x < dstWidth; x++) { int bitOffset = bitIndex + deltaSrc; @@ -211,92 +187,69 @@ static SkSwizzler::ResultAlpha swizzle_small_index_to_n32( currByte = *(src += bitOffset / 8); index = (currByte >> (8 - bpp - bitIndex)) & mask; dst[x] = ctable[index]; - UPDATE_RESULT_ALPHA(ctable[index] >> SK_A32_SHIFT); } - return COMPUTE_RESULT_ALPHA; } // kIndex -static SkSwizzler::ResultAlpha swizzle_index_to_index( +static void swizzle_index_to_index( void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int dstWidth, int bpp, int deltaSrc, int offset, const SkPMColor ctable[]) { src += offset; uint8_t* SK_RESTRICT dst = (uint8_t*) dstRow; - INIT_RESULT_ALPHA; - // TODO (msarett): Should we skip the loop here and guess that the row is opaque/not opaque? - // SkScaledBitmap sampler just guesses that it is opaque. This is dangerous - // and probably wrong since gif and bmp (rarely) may have alpha. if (1 == deltaSrc) { memcpy(dst, src, dstWidth); - for (int x = 0; x < dstWidth; x++) { - UPDATE_RESULT_ALPHA(ctable[src[x]] >> SK_A32_SHIFT); - } } else { for (int x = 0; x < dstWidth; x++) { dst[x] = *src; - UPDATE_RESULT_ALPHA(ctable[*src] >> SK_A32_SHIFT); src += deltaSrc; } } - return COMPUTE_RESULT_ALPHA; } -static SkSwizzler::ResultAlpha swizzle_index_to_n32( +static void swizzle_index_to_n32( void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int dstWidth, int bpp, int deltaSrc, int offset, const SkPMColor ctable[]) { src += offset; SkPMColor* SK_RESTRICT dst = (SkPMColor*)dstRow; - INIT_RESULT_ALPHA; for (int x = 0; x < dstWidth; x++) { SkPMColor c = ctable[*src]; - UPDATE_RESULT_ALPHA(c >> SK_A32_SHIFT); dst[x] = c; src += deltaSrc; } - return COMPUTE_RESULT_ALPHA; } -static SkSwizzler::ResultAlpha swizzle_index_to_n32_skipZ( +static void swizzle_index_to_n32_skipZ( void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int dstWidth, int bpp, int deltaSrc, int offset, const SkPMColor ctable[]) { src += offset; SkPMColor* SK_RESTRICT dst = (SkPMColor*)dstRow; - INIT_RESULT_ALPHA; for (int x = 0; x < dstWidth; x++) { SkPMColor c = ctable[*src]; - UPDATE_RESULT_ALPHA(c >> SK_A32_SHIFT); if (c != 0) { dst[x] = c; } src += deltaSrc; } - return COMPUTE_RESULT_ALPHA; } -static SkSwizzler::ResultAlpha swizzle_index_to_565( +static void swizzle_index_to_565( void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int dstWidth, int bytesPerPixel, int deltaSrc, int offset, const SkPMColor ctable[]) { - // FIXME: Support dithering? Requires knowing y, which I think is a bigger - // change. src += offset; uint16_t* SK_RESTRICT dst = (uint16_t*)dstRow; for (int x = 0; x < dstWidth; x++) { dst[x] = SkPixel32ToPixel16(ctable[*src]); src += deltaSrc; } - return SkSwizzler::kOpaque_ResultAlpha; } - -#undef A32_MASK_IN_PLACE - // kGray -static SkSwizzler::ResultAlpha swizzle_gray_to_n32( +static void swizzle_gray_to_n32( void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int dstWidth, int bpp, int deltaSrc, int offset, const SkPMColor ctable[]) { @@ -306,10 +259,9 @@ static SkSwizzler::ResultAlpha swizzle_gray_to_n32( dst[x] = SkPackARGB32NoCheck(0xFF, *src, *src, *src); src += deltaSrc; } - return SkSwizzler::kOpaque_ResultAlpha; } -static SkSwizzler::ResultAlpha swizzle_gray_to_gray( +static void swizzle_gray_to_gray( void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int dstWidth, int bpp, int deltaSrc, int offset, const SkPMColor ctable[]) { @@ -323,25 +275,23 @@ static SkSwizzler::ResultAlpha swizzle_gray_to_gray( src += deltaSrc; } } - return SkSwizzler::kOpaque_ResultAlpha; } -static SkSwizzler::ResultAlpha swizzle_gray_to_565( +static void swizzle_gray_to_565( void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int dstWidth, int bytesPerPixel, int deltaSrc, int offset, const SkPMColor ctable[]) { - // FIXME: Support dithering? + src += offset; uint16_t* SK_RESTRICT dst = (uint16_t*)dstRow; for (int x = 0; x < dstWidth; x++) { dst[x] = SkPack888ToRGB16(src[0], src[0], src[0]); src += deltaSrc; } - return SkSwizzler::kOpaque_ResultAlpha; } // kBGRX -static SkSwizzler::ResultAlpha swizzle_bgrx_to_n32( +static void swizzle_bgrx_to_n32( void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int dstWidth, int bpp, int deltaSrc, int offset, const SkPMColor ctable[]) { @@ -351,58 +301,50 @@ static SkSwizzler::ResultAlpha swizzle_bgrx_to_n32( dst[x] = SkPackARGB32NoCheck(0xFF, src[2], src[1], src[0]); src += deltaSrc; } - return SkSwizzler::kOpaque_ResultAlpha; } -static SkSwizzler::ResultAlpha swizzle_bgrx_to_565( +static void swizzle_bgrx_to_565( void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int dstWidth, int bpp, int deltaSrc, int offset, const SkPMColor ctable[]) { - // FIXME: Support dithering? + src += offset; uint16_t* SK_RESTRICT dst = (uint16_t*)dstRow; for (int x = 0; x < dstWidth; x++) { dst[x] = SkPack888ToRGB16(src[2], src[1], src[0]); src += deltaSrc; } - return SkSwizzler::kOpaque_ResultAlpha; } // kBGRA -static SkSwizzler::ResultAlpha swizzle_bgra_to_n32_unpremul( +static void swizzle_bgra_to_n32_unpremul( void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int dstWidth, int bpp, int deltaSrc, int offset, const SkPMColor ctable[]) { src += offset; SkPMColor* SK_RESTRICT dst = (SkPMColor*)dstRow; - INIT_RESULT_ALPHA; for (int x = 0; x < dstWidth; x++) { uint8_t alpha = src[3]; - UPDATE_RESULT_ALPHA(alpha); dst[x] = SkPackARGB32NoCheck(alpha, src[2], src[1], src[0]); src += deltaSrc; } - return COMPUTE_RESULT_ALPHA; } -static SkSwizzler::ResultAlpha swizzle_bgra_to_n32_premul( +static void swizzle_bgra_to_n32_premul( void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int dstWidth, int bpp, int deltaSrc, int offset, const SkPMColor ctable[]) { src += offset; SkPMColor* SK_RESTRICT dst = (SkPMColor*)dstRow; - INIT_RESULT_ALPHA; for (int x = 0; x < dstWidth; x++) { uint8_t alpha = src[3]; - UPDATE_RESULT_ALPHA(alpha); - dst[x] = SkPreMultiplyARGB(alpha, src[2], src[1], src[0]); + dst[x] = SkPremultiplyARGBInline(alpha, src[2], src[1], src[0]); src += deltaSrc; } - return COMPUTE_RESULT_ALPHA; } // kRGBX -static SkSwizzler::ResultAlpha swizzle_rgbx_to_n32( +static void swizzle_rgbx_to_n32( void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int dstWidth, int bpp, int deltaSrc, int offset, const SkPMColor ctable[]) { @@ -412,72 +354,60 @@ static SkSwizzler::ResultAlpha swizzle_rgbx_to_n32( dst[x] = SkPackARGB32(0xFF, src[0], src[1], src[2]); src += deltaSrc; } - return SkSwizzler::kOpaque_ResultAlpha; } -static SkSwizzler::ResultAlpha swizzle_rgbx_to_565( +static void swizzle_rgbx_to_565( void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int dstWidth, int bytesPerPixel, int deltaSrc, int offset, const SkPMColor ctable[]) { - // FIXME: Support dithering? + src += offset; uint16_t* SK_RESTRICT dst = (uint16_t*)dstRow; for (int x = 0; x < dstWidth; x++) { dst[x] = SkPack888ToRGB16(src[0], src[1], src[2]); src += deltaSrc; } - return SkSwizzler::kOpaque_ResultAlpha; } - // kRGBA -static SkSwizzler::ResultAlpha swizzle_rgba_to_n32_premul( +static void swizzle_rgba_to_n32_premul( void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int dstWidth, int bpp, int deltaSrc, int offset, const SkPMColor ctable[]) { src += offset; SkPMColor* SK_RESTRICT dst = (SkPMColor*)dstRow; - INIT_RESULT_ALPHA; for (int x = 0; x < dstWidth; x++) { unsigned alpha = src[3]; - UPDATE_RESULT_ALPHA(alpha); - dst[x] = SkPreMultiplyARGB(alpha, src[0], src[1], src[2]); + dst[x] = SkPremultiplyARGBInline(alpha, src[0], src[1], src[2]); src += deltaSrc; } - return COMPUTE_RESULT_ALPHA; } -static SkSwizzler::ResultAlpha swizzle_rgba_to_n32_unpremul( +static void fast_swizzle_rgba_to_n32_premul( + void* dst, const uint8_t* src, int width, int bpp, int deltaSrc, + int offset, const SkPMColor ctable[]) { + + // This function must not be called if we are sampling. If we are not + // sampling, deltaSrc should equal bpp. + SkASSERT(deltaSrc == bpp); + +#ifdef SK_PMCOLOR_IS_RGBA + SkOpts::premul_xxxa((uint32_t*) dst, (const uint32_t*) (src + offset), width); +#else + SkOpts::premul_swaprb_xxxa((uint32_t*) dst, (const uint32_t*) (src + offset), width); +#endif +} + +static void swizzle_rgba_to_n32_unpremul( void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int dstWidth, int bpp, int deltaSrc, int offset, const SkPMColor ctable[]) { src += offset; uint32_t* SK_RESTRICT dst = reinterpret_cast(dstRow); - INIT_RESULT_ALPHA; for (int x = 0; x < dstWidth; x++) { unsigned alpha = src[3]; - UPDATE_RESULT_ALPHA(alpha); dst[x] = SkPackARGB32NoCheck(alpha, src[0], src[1], src[2]); src += deltaSrc; } - return COMPUTE_RESULT_ALPHA; -} - -static SkSwizzler::ResultAlpha swizzle_rgba_to_n32_premul_skipZ( - void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int dstWidth, - int bpp, int deltaSrc, int offset, const SkPMColor ctable[]) { - - src += offset; - SkPMColor* SK_RESTRICT dst = (SkPMColor*)dstRow; - INIT_RESULT_ALPHA; - for (int x = 0; x < dstWidth; x++) { - unsigned alpha = src[3]; - UPDATE_RESULT_ALPHA(alpha); - if (0 != alpha) { - dst[x] = SkPreMultiplyARGB(alpha, src[0], src[1], src[2]); - } - src += deltaSrc; - } - return COMPUTE_RESULT_ALPHA; } // kCMYK @@ -525,7 +455,7 @@ static SkSwizzler::ResultAlpha swizzle_rgba_to_n32_premul_skipZ( // R = C * K / 255 // G = M * K / 255 // B = Y * K / 255 -static SkSwizzler::ResultAlpha swizzle_cmyk_to_n32( +static void swizzle_cmyk_to_n32( void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int dstWidth, int bpp, int deltaSrc, int offset, const SkPMColor ctable[]) { @@ -539,12 +469,9 @@ static SkSwizzler::ResultAlpha swizzle_cmyk_to_n32( dst[x] = SkPackARGB32NoCheck(0xFF, r, g, b); src += deltaSrc; } - - // CMYK is always opaque - return SkSwizzler::kOpaque_ResultAlpha; } -static SkSwizzler::ResultAlpha swizzle_cmyk_to_565( +static void swizzle_cmyk_to_565( void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int dstWidth, int bpp, int deltaSrc, int offset, const SkPMColor ctable[]) { @@ -558,38 +485,26 @@ static SkSwizzler::ResultAlpha swizzle_cmyk_to_565( dst[x] = SkPack888ToRGB16(r, g, b); src += deltaSrc; } - - // CMYK is always opaque - return SkSwizzler::kOpaque_ResultAlpha; } -/** - FIXME: This was my idea to cheat in order to continue taking advantage of skipping zeroes. - This would be fine for drawing normally, but not for drawing with transfer modes. Being - honest means we can draw correctly with transfer modes, with the cost of not being able - to take advantage of Android's free unwritten pages. Something to keep in mind when we - decide whether to switch to unpremul default. -static bool swizzle_rgba_to_n32_unpremul_skipZ(void* SK_RESTRICT dstRow, - const uint8_t* SK_RESTRICT src, - int dstWidth, int bitsPerPixel, int offset, - const SkPMColor[]) { - src += offset; - SkPMColor* SK_RESTRICT dst = (SkPMColor*)dstRow; - unsigned alphaMask = 0xFF; - for (int x = 0; x < dstWidth; x++) { - unsigned alpha = src[3]; - // NOTE: We cheat here. The caller requested unpremul and skip zeroes. It's possible - // the color components are not zero, but we skip them anyway, meaning they'll remain - // zero (implied by the request to skip zeroes). - if (0 != alpha) { - dst[x] = SkPackARGB32NoCheck(alpha, src[0], src[1], src[2]); - } - src += deltaSrc; - alphaMask &= alpha; +template +void SkSwizzler::SkipLeading8888ZerosThen( + void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int dstWidth, + int bpp, int deltaSrc, int offset, const SkPMColor ctable[]) { + SkASSERT(!ctable); + + auto src32 = (const uint32_t*)(src+offset); + auto dst32 = (uint32_t*)dstRow; + + // This may miss opportunities to skip when the output is premultiplied, + // e.g. for a src pixel 0x00FFFFFF which is not zero but becomes zero after premultiplication. + while (dstWidth > 0 && *src32 == 0x00000000) { + dstWidth--; + dst32++; + src32 += deltaSrc/4; } - return alphaMask != 0xFF; + proc(dst32, (const uint8_t*)src32, dstWidth, bpp, deltaSrc, 0, ctable); } -*/ SkSwizzler* SkSwizzler::CreateSwizzler(SkSwizzler::SrcConfig sc, const SkPMColor* ctable, @@ -603,6 +518,7 @@ SkSwizzler* SkSwizzler::CreateSwizzler(SkSwizzler::SrcConfig sc, && nullptr == ctable) { return nullptr; } + RowProc fastProc = nullptr; RowProc proc = nullptr; SkCodec::ZeroInitialized zeroInit = options.fZeroInitialized; switch (sc) { @@ -710,7 +626,6 @@ SkSwizzler* SkSwizzler::CreateSwizzler(SkSwizzler::SrcConfig sc, } break; case kRGBX: - // TODO: Support other swizzles. switch (dstInfo.colorType()) { case kN32_SkColorType: proc = &swizzle_rgbx_to_n32; @@ -725,13 +640,18 @@ SkSwizzler* SkSwizzler::CreateSwizzler(SkSwizzler::SrcConfig sc, switch (dstInfo.colorType()) { case kN32_SkColorType: if (dstInfo.alphaType() == kUnpremul_SkAlphaType) { - // Respect zeroInit? - proc = &swizzle_rgba_to_n32_unpremul; + if (SkCodec::kYes_ZeroInitialized == zeroInit) { + proc = &SkipLeading8888ZerosThen; + } else { + proc = &swizzle_rgba_to_n32_unpremul; + } } else { if (SkCodec::kYes_ZeroInitialized == zeroInit) { - proc = &swizzle_rgba_to_n32_premul_skipZ; + proc = &SkipLeading8888ZerosThen; + fastProc = &SkipLeading8888ZerosThen; } else { proc = &swizzle_rgba_to_n32_premul; + fastProc = &fast_swizzle_rgba_to_n32_premul; } } break; @@ -772,14 +692,11 @@ SkSwizzler* SkSwizzler::CreateSwizzler(SkSwizzler::SrcConfig sc, default: break; } - if (nullptr == proc) { - return nullptr; - } // Store bpp in bytes if it is an even multiple, otherwise use bits int srcBPP = SkIsAlign8(BitsPerPixel(sc)) ? BytesPerPixel(sc) : BitsPerPixel(sc); int dstBPP = SkColorTypeBytesPerPixel(dstInfo.colorType()); - + int srcOffset = 0; int srcWidth = dstInfo.width(); int dstOffset = 0; @@ -796,12 +713,14 @@ SkSwizzler* SkSwizzler::CreateSwizzler(SkSwizzler::SrcConfig sc, srcWidth = frame->width(); } - return new SkSwizzler(proc, ctable, srcOffset, srcWidth, dstOffset, dstWidth, srcBPP, dstBPP); + return new SkSwizzler(fastProc, proc, ctable, srcOffset, srcWidth, dstOffset, dstWidth, + srcBPP, dstBPP); } -SkSwizzler::SkSwizzler(RowProc proc, const SkPMColor* ctable, int srcOffset, int srcWidth, - int dstOffset, int dstWidth, int srcBPP, int dstBPP) - : fRowProc(proc) +SkSwizzler::SkSwizzler(RowProc fastProc, RowProc proc, const SkPMColor* ctable, int srcOffset, + int srcWidth, int dstOffset, int dstWidth, int srcBPP, int dstBPP) + : fFastProc(fastProc) + , fProc(proc) , fColorTable(ctable) , fSrcOffset(srcOffset) , fDstOffset(dstOffset) @@ -825,11 +744,15 @@ int SkSwizzler::onSetSampleX(int sampleX) { fSwizzleWidth = get_scaled_dimension(fSrcWidth, sampleX); fAllocatedWidth = get_scaled_dimension(fDstWidth, sampleX); + // The optimized swizzler routines do not (yet) support sampling. + fFastProc = nullptr; + return fAllocatedWidth; } -SkSwizzler::ResultAlpha SkSwizzler::swizzle(void* dst, const uint8_t* SK_RESTRICT src) { +void SkSwizzler::swizzle(void* dst, const uint8_t* SK_RESTRICT src) { SkASSERT(nullptr != dst && nullptr != src); - return fRowProc(SkTAddOffset(dst, fDstOffsetBytes), src, fSwizzleWidth, fSrcBPP, + RowProc proc = fFastProc ? fFastProc : fProc; + proc(SkTAddOffset(dst, fDstOffsetBytes), src, fSwizzleWidth, fSrcBPP, fSampleX * fSrcBPP, fSrcOffsetUnits, fColorTable); } diff --git a/gfx/skia/skia/src/codec/SkSwizzler.h b/gfx/skia/skia/src/codec/SkSwizzler.h index 7f5bbc6209..e75ab43cfe 100644 --- a/gfx/skia/skia/src/codec/SkSwizzler.h +++ b/gfx/skia/skia/src/codec/SkSwizzler.h @@ -36,42 +36,6 @@ public: kCMYK, }; - /* - * - * Result code for the alpha components of a row. - * - */ - typedef uint16_t ResultAlpha; - static const ResultAlpha kOpaque_ResultAlpha = 0xFFFF; - static const ResultAlpha kTransparent_ResultAlpha = 0x0000; - - /* - * - * Checks if the result of decoding a row indicates that the row was - * transparent. - * - */ - static bool IsTransparent(ResultAlpha r) { - return kTransparent_ResultAlpha == r; - } - - /* - * - * Checks if the result of decoding a row indicates that the row was - * opaque. - * - */ - static bool IsOpaque(ResultAlpha r) { - return kOpaque_ResultAlpha == r; - } - - /* - * - * Constructs the proper result code based on accumulated alpha masks - * - */ - static ResultAlpha GetResult(uint8_t zeroAlpha, uint8_t maxAlpha); - /* * * Returns bits per pixel for source config @@ -149,10 +113,8 @@ public: * subset decodes. * @param dst Where we write the output. * @param src The next row of the source data. - * @return A result code describing if the row was fully opaque, fully - * transparent, or neither */ - ResultAlpha swizzle(void* dst, const uint8_t* SK_RESTRICT src); + void swizzle(void* dst, const uint8_t* SK_RESTRICT src); /** * Implement fill using a custom width. @@ -163,6 +125,16 @@ public: SkSampler::Fill(fillInfo, dst, rowBytes, colorOrIndex, zeroInit); } + /** + * If fSampleX > 1, the swizzler is sampling every fSampleX'th pixel and + * discarding the rest. + * + * This getter is currently used by SkBmpStandardCodec for Bmp-in-Ico decodes. + * Ideally, the subclasses of SkCodec would have no knowledge of sampling, but + * this allows us to apply a transparency mask to pixels after swizzling. + */ + int sampleX() const { return fSampleX; } + private: /** @@ -177,12 +149,21 @@ private: * @param offset The offset before the first pixel to sample. Is in bytes or bits based on what deltaSrc is in. */ - typedef ResultAlpha (*RowProc)(void* SK_RESTRICT dstRow, - const uint8_t* SK_RESTRICT src, - int dstWidth, int bpp, int deltaSrc, int offset, - const SkPMColor ctable[]); + typedef void (*RowProc)(void* SK_RESTRICT dstRow, + const uint8_t* SK_RESTRICT src, + int dstWidth, int bpp, int deltaSrc, int offset, + const SkPMColor ctable[]); - const RowProc fRowProc; + template + static void SkipLeading8888ZerosThen(void* SK_RESTRICT dstRow, + const uint8_t* SK_RESTRICT src, + int dstWidth, int bpp, int deltaSrc, int offset, + const SkPMColor ctable[]); + + // May be NULL. We will not always be able to used an optimized function. + RowProc fFastProc; + // Always non-NULL. We use this if fFastProc is NULL. + const RowProc fProc; const SkPMColor* fColorTable; // Unowned pointer // Subset Swizzles @@ -269,8 +250,8 @@ private: // fBPP is bitsPerPixel const int fDstBPP; // Bytes per pixel for the destination color type - SkSwizzler(RowProc proc, const SkPMColor* ctable, int srcOffset, int srcWidth, int dstOffset, - int dstWidth, int srcBPP, int dstBPP); + SkSwizzler(RowProc fastProc, RowProc proc, const SkPMColor* ctable, int srcOffset, + int srcWidth, int dstOffset, int dstWidth, int srcBPP, int dstBPP); int onSetSampleX(int) override; diff --git a/gfx/skia/skia/src/codec/SkCodec_wbmp.cpp b/gfx/skia/skia/src/codec/SkWbmpCodec.cpp similarity index 96% rename from gfx/skia/skia/src/codec/SkCodec_wbmp.cpp rename to gfx/skia/skia/src/codec/SkWbmpCodec.cpp index 8f8763fb90..4dd0f2544d 100644 --- a/gfx/skia/skia/src/codec/SkCodec_wbmp.cpp +++ b/gfx/skia/skia/src/codec/SkWbmpCodec.cpp @@ -9,8 +9,9 @@ #include "SkCodecPriv.h" #include "SkColorPriv.h" #include "SkColorTable.h" +#include "SkData.h" #include "SkStream.h" -#include "SkCodec_wbmp.h" +#include "SkWbmpCodec.h" // Each bit represents a pixel, so width is actually a number of bits. // A row will always be stored in bytes, so we round width up to the @@ -151,8 +152,10 @@ SkCodec::Result SkWbmpCodec::onGetPixels(const SkImageInfo& info, return kSuccess; } -bool SkWbmpCodec::IsWbmp(SkStream* stream) { - return read_header(stream, nullptr); +bool SkWbmpCodec::IsWbmp(const void* buffer, size_t bytesRead) { + SkAutoTUnref data(SkData::NewWithoutCopy(buffer, bytesRead)); + SkMemoryStream stream(data); + return read_header(&stream, nullptr); } SkCodec* SkWbmpCodec::NewFromStream(SkStream* stream) { diff --git a/gfx/skia/skia/src/codec/SkCodec_wbmp.h b/gfx/skia/skia/src/codec/SkWbmpCodec.h similarity index 97% rename from gfx/skia/skia/src/codec/SkCodec_wbmp.h rename to gfx/skia/skia/src/codec/SkWbmpCodec.h index f54dd0f166..fb062c94d5 100644 --- a/gfx/skia/skia/src/codec/SkCodec_wbmp.h +++ b/gfx/skia/skia/src/codec/SkWbmpCodec.h @@ -13,7 +13,7 @@ class SkWbmpCodec final : public SkCodec { public: - static bool IsWbmp(SkStream*); + static bool IsWbmp(const void*, size_t); /* * Assumes IsWbmp was called and returned true diff --git a/gfx/skia/skia/src/codec/SkWebpAdapterCodec.cpp b/gfx/skia/skia/src/codec/SkWebpAdapterCodec.cpp index fb1eacee77..5aefe5d805 100644 --- a/gfx/skia/skia/src/codec/SkWebpAdapterCodec.cpp +++ b/gfx/skia/skia/src/codec/SkWebpAdapterCodec.cpp @@ -10,17 +10,16 @@ #include "SkWebpAdapterCodec.h" SkWebpAdapterCodec::SkWebpAdapterCodec(SkWebpCodec* codec) - : INHERITED(codec->getInfo()) - , fCodec(codec) + : INHERITED(codec) {} SkISize SkWebpAdapterCodec::onGetSampledDimensions(int sampleSize) const { float scale = get_scale_from_sample_size(sampleSize); - return fCodec->getScaledDimensions(scale); + return this->codec()->getScaledDimensions(scale); } bool SkWebpAdapterCodec::onGetSupportedSubset(SkIRect* desiredSubset) const { - return fCodec->getValidSubset(desiredSubset); + return this->codec()->getValidSubset(desiredSubset); } SkCodec::Result SkWebpAdapterCodec::onGetAndroidPixels(const SkImageInfo& info, void* pixels, @@ -41,6 +40,6 @@ SkCodec::Result SkWebpAdapterCodec::onGetAndroidPixels(const SkImageInfo& info, SkCodec::Options codecOptions; codecOptions.fZeroInitialized = options.fZeroInitialized; codecOptions.fSubset = options.fSubset; - return fCodec->getPixels(info, pixels, rowBytes, &codecOptions, options.fColorPtr, + return this->codec()->getPixels(info, pixels, rowBytes, &codecOptions, options.fColorPtr, options.fColorCount); } diff --git a/gfx/skia/skia/src/codec/SkWebpAdapterCodec.h b/gfx/skia/skia/src/codec/SkWebpAdapterCodec.h index ed0292a02e..ece46a668a 100644 --- a/gfx/skia/skia/src/codec/SkWebpAdapterCodec.h +++ b/gfx/skia/skia/src/codec/SkWebpAdapterCodec.h @@ -23,8 +23,6 @@ public: protected: - SkEncodedFormat onGetEncodedFormat() const override { return kWEBP_SkEncodedFormat; }; - SkISize onGetSampledDimensions(int sampleSize) const override; bool onGetSupportedSubset(SkIRect* desiredSubset) const override; @@ -34,8 +32,6 @@ protected: private: - SkAutoTDelete fCodec; - typedef SkAndroidCodec INHERITED; }; #endif // SkWebpAdapterCodec_DEFINED diff --git a/gfx/skia/skia/src/codec/SkWebpCodec.cpp b/gfx/skia/skia/src/codec/SkWebpCodec.cpp index 3c61b93762..6cfb385294 100644 --- a/gfx/skia/skia/src/codec/SkWebpCodec.cpp +++ b/gfx/skia/skia/src/codec/SkWebpCodec.cpp @@ -20,26 +20,26 @@ #include "webp/decode.h" #include "webp/encode.h" -bool SkWebpCodec::IsWebp(SkStream* stream) { +bool SkWebpCodec::IsWebp(const void* buf, size_t bytesRead) { // WEBP starts with the following: // RIFFXXXXWEBPVP // Where XXXX is unspecified. - const char LENGTH = 14; - char bytes[LENGTH]; - if (stream->read(&bytes, LENGTH) != LENGTH) { - return false; - } - return !memcmp(bytes, "RIFF", 4) && !memcmp(&bytes[8], "WEBPVP", 6); + const char* bytes = static_cast(buf); + return bytesRead >= 14 && !memcmp(bytes, "RIFF", 4) && !memcmp(&bytes[8], "WEBPVP", 6); } -static const size_t WEBP_VP8_HEADER_SIZE = 30; - // Parse headers of RIFF container, and check for valid Webp (VP8) content. // NOTE: This calls peek instead of read, since onGetPixels will need these // bytes again. static bool webp_parse_header(SkStream* stream, SkImageInfo* info) { unsigned char buffer[WEBP_VP8_HEADER_SIZE]; - if (!stream->peek(buffer, WEBP_VP8_HEADER_SIZE)) { + SkASSERT(WEBP_VP8_HEADER_SIZE <= SkCodec::MinBufferedBytesNeeded()); + + const size_t bytesPeeked = stream->peek(buffer, WEBP_VP8_HEADER_SIZE); + if (bytesPeeked != WEBP_VP8_HEADER_SIZE) { + // Use read + rewind as a backup + if (stream->read(buffer, WEBP_VP8_HEADER_SIZE) != WEBP_VP8_HEADER_SIZE + || !stream->rewind()) return false; } @@ -230,8 +230,8 @@ SkCodec::Result SkWebpCodec::onGetPixels(const SkImageInfo& dstInfo, void* dst, return kInvalidInput; } - SkAutoMalloc storage(BUFFER_SIZE); - uint8_t* buffer = static_cast(storage.get()); + SkAutoTMalloc storage(BUFFER_SIZE); + uint8_t* buffer = storage.get(); while (true) { const size_t bytesRead = stream()->read(buffer, BUFFER_SIZE); if (0 == bytesRead) { diff --git a/gfx/skia/skia/src/codec/SkWebpCodec.h b/gfx/skia/skia/src/codec/SkWebpCodec.h index 97b3920ce9..dfc9f12ee9 100644 --- a/gfx/skia/skia/src/codec/SkWebpCodec.h +++ b/gfx/skia/skia/src/codec/SkWebpCodec.h @@ -15,20 +15,18 @@ class SkStream; +static const size_t WEBP_VP8_HEADER_SIZE = 30; + class SkWebpCodec final : public SkCodec { public: // Assumes IsWebp was called and returned true. static SkCodec* NewFromStream(SkStream*); - static bool IsWebp(SkStream*); + static bool IsWebp(const void*, size_t); protected: Result onGetPixels(const SkImageInfo&, void*, size_t, const Options&, SkPMColor*, int*, int*) override; SkEncodedFormat onGetEncodedFormat() const override { return kWEBP_SkEncodedFormat; } - bool onReallyHasAlpha() const override { - return this->getInfo().alphaType() != kOpaque_SkAlphaType; - } - SkISize onGetScaledDimensions(float desiredScale) const override; bool onDimensionsSupported(const SkISize&) override; diff --git a/gfx/skia/skia/src/core/Sk4px.h b/gfx/skia/skia/src/core/Sk4px.h index 3755488a4a..05378a8bc8 100644 --- a/gfx/skia/skia/src/core/Sk4px.h +++ b/gfx/skia/skia/src/core/Sk4px.h @@ -48,14 +48,6 @@ public: void store2(SkPMColor[2]) const; void store1(SkPMColor[1]) const; - // Same as above for 565. - static Sk4px Load4(const SkPMColor16 src[4]); - static Sk4px Load2(const SkPMColor16 src[2]); - static Sk4px Load1(const SkPMColor16 src[1]); - void store4(SkPMColor16 dst[4]) const; - void store2(SkPMColor16 dst[2]) const; - void store1(SkPMColor16 dst[1]) const; - // 1, 2, or 4 SkPMColors with 16-bit components. // This is most useful as the result of a multiply, e.g. from mulWiden(). class Wide : public Sk16h { @@ -105,8 +97,8 @@ public: // A generic driver that maps fn over a src array into a dst array. // fn should take an Sk4px (4 src pixels) and return an Sk4px (4 dst pixels). - template - static void MapSrc(int n, Dst* dst, const SkPMColor* src, const Fn& fn) { + template + static void MapSrc(int n, SkPMColor* dst, const SkPMColor* src, const Fn& fn) { SkASSERT(dst); SkASSERT(src); // This looks a bit odd, but it helps loop-invariant hoisting across different calls to fn. @@ -137,8 +129,8 @@ public: } // As above, but with dst4' = fn(dst4, src4). - template - static void MapDstSrc(int n, Dst* dst, const SkPMColor* src, const Fn& fn) { + template + static void MapDstSrc(int n, SkPMColor* dst, const SkPMColor* src, const Fn& fn) { SkASSERT(dst); SkASSERT(src); while (n > 0) { @@ -167,8 +159,8 @@ public: } // As above, but with dst4' = fn(dst4, alpha4). - template - static void MapDstAlpha(int n, Dst* dst, const SkAlpha* a, const Fn& fn) { + template + static void MapDstAlpha(int n, SkPMColor* dst, const SkAlpha* a, const Fn& fn) { SkASSERT(dst); SkASSERT(a); while (n > 0) { @@ -197,8 +189,8 @@ public: } // As above, but with dst4' = fn(dst4, src4, alpha4). - template - static void MapDstSrcAlpha(int n, Dst* dst, const SkPMColor* src, const SkAlpha* a, + template + static void MapDstSrcAlpha(int n, SkPMColor* dst, const SkPMColor* src, const SkAlpha* a, const Fn& fn) { SkASSERT(dst); SkASSERT(src); diff --git a/gfx/skia/skia/src/core/SkAAClip.cpp b/gfx/skia/skia/src/core/SkAAClip.cpp index bbd1f555f0..71d212a292 100644 --- a/gfx/skia/skia/src/core/SkAAClip.cpp +++ b/gfx/skia/skia/src/core/SkAAClip.cpp @@ -2056,9 +2056,10 @@ static inline uint16_t mergeOne(uint16_t value, unsigned alpha) { SkMulDiv255Round(b, alpha)); } -template void mergeT(const T* SK_RESTRICT src, int srcN, - const uint8_t* SK_RESTRICT row, int rowN, - T* SK_RESTRICT dst) { +template +void mergeT(const void* inSrc, int srcN, const uint8_t* SK_RESTRICT row, int rowN, void* inDst) { + const T* SK_RESTRICT src = static_cast(inSrc); + T* SK_RESTRICT dst = static_cast(inDst); for (;;) { SkASSERT(rowN > 0); SkASSERT(srcN > 0); @@ -2094,14 +2095,10 @@ static MergeAAProc find_merge_aa_proc(SkMask::Format format) { SkDEBUGFAIL("unsupported"); return nullptr; case SkMask::kA8_Format: - case SkMask::k3D_Format: { - void (*proc8)(const uint8_t*, int, const uint8_t*, int, uint8_t*) = mergeT; - return (MergeAAProc)proc8; - } - case SkMask::kLCD16_Format: { - void (*proc16)(const uint16_t*, int, const uint8_t*, int, uint16_t*) = mergeT; - return (MergeAAProc)proc16; - } + case SkMask::k3D_Format: + return mergeT ; + case SkMask::kLCD16_Format: + return mergeT; default: SkDEBUGFAIL("unsupported"); return nullptr; @@ -2168,7 +2165,6 @@ void SkAAClipBlitter::blitMask(const SkMask& origMask, const SkIRect& clip) { // if we're BW, we need to upscale to A8 (ugh) SkMask grayMask; - grayMask.fImage = nullptr; if (SkMask::kBW_Format == origMask.fFormat) { grayMask.fFormat = SkMask::kA8_Format; grayMask.fBounds = origMask.fBounds; diff --git a/gfx/skia/skia/src/core/SkAntiRun.h b/gfx/skia/skia/src/core/SkAntiRun.h index 9a0003935f..d49a723b20 100644 --- a/gfx/skia/skia/src/core/SkAntiRun.h +++ b/gfx/skia/skia/src/core/SkAntiRun.h @@ -71,7 +71,6 @@ public: runs += x + 1; alpha += x + 1; x = 0; - lastAlpha += x; // we don't want the +1 SkDEBUGCODE(this->validate();) } diff --git a/gfx/skia/skia/src/core/SkBitmap.cpp b/gfx/skia/skia/src/core/SkBitmap.cpp index 6fd97dc241..bdf1daafcc 100644 --- a/gfx/skia/skia/src/core/SkBitmap.cpp +++ b/gfx/skia/skia/src/core/SkBitmap.cpp @@ -17,6 +17,7 @@ #include "SkReadBuffer.h" #include "SkRect.h" #include "SkScalar.h" +#include "SkTemplates.h" #include "SkUnPreMultiply.h" #include "SkWriteBuffer.h" @@ -38,6 +39,8 @@ SkBitmap::SkBitmap(const SkBitmap& src) { SkDEBUGCODE(this->validate();) } +SkBitmap::SkBitmap(SkBitmap&& other) : SkBitmap() { this->swap(other); } + SkBitmap::~SkBitmap() { SkDEBUGCODE(this->validate();) this->freePixels(); @@ -71,6 +74,14 @@ SkBitmap& SkBitmap::operator=(const SkBitmap& src) { return *this; } +SkBitmap& SkBitmap::operator=(SkBitmap&& other) { + if (this != &other) { + this->swap(other); + other.reset(); + } + return *this; +} + void SkBitmap::swap(SkBitmap& other) { SkTSwap(fColorTable, other.fColorTable); SkTSwap(fPixelRef, other.fPixelRef); @@ -372,6 +383,12 @@ bool SkBitmap::installPixels(const SkImageInfo& requestedInfo, void* pixels, siz return true; } +bool SkBitmap::installPixels(const SkPixmap& pixmap) { + return this->installPixels(pixmap.info(), pixmap.writable_addr(), + pixmap.rowBytes(), pixmap.ctable(), + nullptr, nullptr); +} + bool SkBitmap::installMaskPixels(const SkMask& mask) { if (SkMask::kA8_Format != mask.fFormat) { this->reset(); @@ -572,10 +589,15 @@ SkColor SkBitmap::getColor(int x, int y) const { SkPMColor c = SkPixel4444ToPixel32(addr[0]); return SkUnPreMultiply::PMColorToColor(c); } - case kBGRA_8888_SkColorType: + case kBGRA_8888_SkColorType: { + uint32_t* addr = this->getAddr32(x, y); + SkPMColor c = SkSwizzle_BGRA_to_PMColor(addr[0]); + return SkUnPreMultiply::PMColorToColor(c); + } case kRGBA_8888_SkColorType: { uint32_t* addr = this->getAddr32(x, y); - return SkUnPreMultiply::PMColorToColor(addr[0]); + SkPMColor c = SkSwizzle_RGBA_to_PMColor(addr[0]); + return SkUnPreMultiply::PMColorToColor(c); } default: SkASSERT(false); @@ -806,7 +828,7 @@ bool SkBitmap::copyTo(SkBitmap* dst, SkColorType dstColorType, Allocator* alloc) SkIRect subset; subset.setXYWH(fPixelRefOrigin.fX, fPixelRefOrigin.fY, fInfo.width(), fInfo.height()); - if (fPixelRef->readPixels(&tmpSrc, &subset)) { + if (fPixelRef->readPixels(&tmpSrc, dstColorType, &subset)) { if (fPixelRef->info().alphaType() == kUnpremul_SkAlphaType) { // FIXME: The only meaningful implementation of readPixels // (GrPixelRef) assumes premultiplied pixels. @@ -1011,7 +1033,6 @@ bool SkBitmap::extractAlpha(SkBitmap* dst, const SkPaint* paint, // compute our (larger?) dst bounds if we have a filter if (filter) { identity.reset(); - srcM.fImage = nullptr; if (!filter->filterMask(&dstM, srcM, identity, nullptr)) { goto NO_FILTER_CASE; } @@ -1071,8 +1092,8 @@ static void write_raw_pixels(SkWriteBuffer* buffer, const SkPixmap& pmap) { info.flatten(*buffer); const size_t size = snugRB * info.height(); - SkAutoMalloc storage(size); - char* dst = (char*)storage.get(); + SkAutoTMalloc storage(size); + char* dst = storage.get(); for (int y = 0; y < info.height(); ++y) { memcpy(dst, src, snugRB); dst += snugRB; diff --git a/gfx/skia/skia/src/core/SkBitmapController.cpp b/gfx/skia/skia/src/core/SkBitmapController.cpp index 233e81146f..7182ea7382 100644 --- a/gfx/skia/skia/src/core/SkBitmapController.cpp +++ b/gfx/skia/skia/src/core/SkBitmapController.cpp @@ -100,6 +100,12 @@ bool SkDefaultBitmapControllerState::processHQRequest(const SkBitmapProvider& pr if (SkScalarNearlyEqual(invScaleX, 1) && SkScalarNearlyEqual(invScaleY, 1)) { return false; // no need for HQ } + +#ifndef SK_SUPPORT_LEGACY_HQ_DOWNSAMPLING + if (invScaleX > 1 || invScaleY > 1) { + return false; // only use HQ when upsampling + } +#endif const int dstW = SkScalarRoundToScalar(provider.width() / invScaleX); const int dstH = SkScalarRoundToScalar(provider.height() / invScaleY); diff --git a/gfx/skia/skia/src/core/SkBitmapDevice.cpp b/gfx/skia/skia/src/core/SkBitmapDevice.cpp index ac3b9df082..a7ca7b7181 100644 --- a/gfx/skia/skia/src/core/SkBitmapDevice.cpp +++ b/gfx/skia/skia/src/core/SkBitmapDevice.cpp @@ -95,7 +95,7 @@ SkBitmapDevice* SkBitmapDevice::Create(const SkImageInfo& origInfo, if (!bitmap.setInfo(info)) { return nullptr; } - } else if (bitmap.info().isOpaque()) { + } else if (info.isOpaque()) { // If this bitmap is opaque, we don't have any sensible default color, // so we just return uninitialized pixels. if (!bitmap.tryAllocPixels(info)) { @@ -286,7 +286,7 @@ void SkBitmapDevice::drawBitmapRect(const SkDraw& draw, const SkBitmap& bitmap, if(bitmap.pixelRef()->getTexture()) { // Accelerated source canvas, don't use extractSubset but readPixels to get the subset. // This way, the pixels are copied in CPU memory instead of GPU memory. - bitmap.pixelRef()->readPixels(&tmpBitmap, &srcIR); + bitmap.pixelRef()->readPixels(&tmpBitmap, kN32_SkColorType, &srcIR); } else { if (!bitmap.extractSubset(&tmpBitmap, srcIR)) { return; diff --git a/gfx/skia/skia/src/core/SkBitmapFilter.cpp b/gfx/skia/skia/src/core/SkBitmapFilter.cpp deleted file mode 100644 index 55a7092ba7..0000000000 --- a/gfx/skia/skia/src/core/SkBitmapFilter.cpp +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2013 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "SkBitmapFilter.h" -#include "SkRTConf.h" -#include "SkTypes.h" - -#include - -// These are the per-scanline callbacks that are used when we must resort to -// resampling an image as it is blitted. Typically these are used only when -// the image is rotated or has some other complex transformation applied. -// Scaled images will usually be rescaled directly before rasterization. - -SK_CONF_DECLARE(const char *, c_bitmapFilter, "bitmap.filter", "mitchell", "Which scanline bitmap filter to use [mitchell, lanczos, hamming, gaussian, triangle, box]"); - -SkBitmapFilter *SkBitmapFilter::Allocate() { - if (!strcmp(c_bitmapFilter, "mitchell")) { - return new SkMitchellFilter(1.f / 3.f, 1.f / 3.f); - } else if (!strcmp(c_bitmapFilter, "lanczos")) { - return new SkLanczosFilter; - } else if (!strcmp(c_bitmapFilter, "hamming")) { - return new SkHammingFilter; - } else if (!strcmp(c_bitmapFilter, "gaussian")) { - return new SkGaussianFilter(2); - } else if (!strcmp(c_bitmapFilter, "triangle")) { - return new SkTriangleFilter; - } else if (!strcmp(c_bitmapFilter, "box")) { - return new SkBoxFilter; - } else { - SkDEBUGFAIL("Unknown filter type"); - } - - return nullptr; -} - diff --git a/gfx/skia/skia/src/core/SkBitmapFilter.h b/gfx/skia/skia/src/core/SkBitmapFilter.h index eb327d716c..dc3ea693b8 100644 --- a/gfx/skia/skia/src/core/SkBitmapFilter.h +++ b/gfx/skia/skia/src/core/SkBitmapFilter.h @@ -12,6 +12,10 @@ #include "SkMath.h" #include "SkScalar.h" +#ifndef SK_SUPPORT_LEGACY_BITMAP_FILTER +#include "SkNx.h" +#endif + // size of the precomputed bitmap filter tables for high quality filtering. // Used to precompute the shape of the filter kernel. // Table size chosen from experiments to see where I could start to see a difference. @@ -19,125 +23,200 @@ #define SKBITMAP_FILTER_TABLE_SIZE 128 class SkBitmapFilter { - public: - SkBitmapFilter(float width) - : fWidth(width), fInvWidth(1.f/width) { - fPrecomputed = false; - fLookupMultiplier = this->invWidth() * (SKBITMAP_FILTER_TABLE_SIZE-1); - } - - SkFixed lookup(float x) const { - if (!fPrecomputed) { - precomputeTable(); - } - int filter_idx = int(sk_float_abs(x * fLookupMultiplier)); - SkASSERT(filter_idx < SKBITMAP_FILTER_TABLE_SIZE); - return fFilterTable[filter_idx]; - } - - SkScalar lookupScalar(float x) const { - if (!fPrecomputed) { - precomputeTable(); - } - int filter_idx = int(sk_float_abs(x * fLookupMultiplier)); - SkASSERT(filter_idx < SKBITMAP_FILTER_TABLE_SIZE); - return fFilterTableScalar[filter_idx]; - } - - float width() const { return fWidth; } - float invWidth() const { return fInvWidth; } - virtual float evaluate(float x) const = 0; - virtual ~SkBitmapFilter() {} - - static SkBitmapFilter* Allocate(); - protected: - float fWidth; - float fInvWidth; - - float fLookupMultiplier; - - mutable bool fPrecomputed; - mutable SkFixed fFilterTable[SKBITMAP_FILTER_TABLE_SIZE]; - mutable SkScalar fFilterTableScalar[SKBITMAP_FILTER_TABLE_SIZE]; - private: - void precomputeTable() const { - fPrecomputed = true; - SkFixed *ftp = fFilterTable; - SkScalar *ftpScalar = fFilterTableScalar; - for (int x = 0; x < SKBITMAP_FILTER_TABLE_SIZE; ++x) { - float fx = ((float)x + .5f) * this->width() / SKBITMAP_FILTER_TABLE_SIZE; - float filter_value = evaluate(fx); - *ftpScalar++ = filter_value; - *ftp++ = SkFloatToFixed(filter_value); - } - } -}; - -class SkMitchellFilter: public SkBitmapFilter { - public: - SkMitchellFilter(float b, float c, float width=2.0f) - : SkBitmapFilter(width), B(b), C(c) { - } - - float evaluate(float x) const override { - x = fabsf(x); - if (x > 2.f) { - return 0; - } else if (x > 1.f) { - return ((-B - 6*C) * x*x*x + (6*B + 30*C) * x*x + - (-12*B - 48*C) * x + (8*B + 24*C)) * (1.f/6.f); - } else { - return ((12 - 9*B - 6*C) * x*x*x + - (-18 + 12*B + 6*C) * x*x + - (6 - 2*B)) * (1.f/6.f); - } - } - protected: - float B, C; -}; - -class SkGaussianFilter: public SkBitmapFilter { - public: - SkGaussianFilter(float a, float width=2.0f) - : SkBitmapFilter(width), alpha(a), expWidth(expf(-alpha * width * width)) { - } - - float evaluate(float x) const override { - return SkTMax(0.f, float(expf(-alpha*x*x) - expWidth)); - } - protected: - float alpha, expWidth; -}; - -class SkTriangleFilter: public SkBitmapFilter { - public: - SkTriangleFilter(float width=1) - : SkBitmapFilter(width) { - } - - float evaluate(float x) const override { - return SkTMax(0.f, fWidth - fabsf(x)); - } - protected: -}; - -class SkBoxFilter: public SkBitmapFilter { - public: - SkBoxFilter(float width=0.5f) - : SkBitmapFilter(width) { - } - - float evaluate(float x) const override { - return (x >= -fWidth && x < fWidth) ? 1.0f : 0.0f; - } - protected: -}; - -class SkHammingFilter: public SkBitmapFilter { public: - SkHammingFilter(float width=1.f) - : SkBitmapFilter(width) { + SkBitmapFilter(float width) : fWidth(width), fInvWidth(1.f/width) { + fPrecomputed = false; + fLookupMultiplier = this->invWidth() * (SKBITMAP_FILTER_TABLE_SIZE-1); } + virtual ~SkBitmapFilter() {} + + SkFixed lookup(float x) const { + if (!fPrecomputed) { + precomputeTable(); + } + int filter_idx = int(sk_float_abs(x * fLookupMultiplier)); + SkASSERT(filter_idx < SKBITMAP_FILTER_TABLE_SIZE); + return fFilterTable[filter_idx]; + } + + SkScalar lookupScalar(float x) const { + if (!fPrecomputed) { + precomputeTable(); + } + int filter_idx = int(sk_float_abs(x * fLookupMultiplier)); + SkASSERT(filter_idx < SKBITMAP_FILTER_TABLE_SIZE); + return fFilterTableScalar[filter_idx]; + } + + float width() const { return fWidth; } + float invWidth() const { return fInvWidth; } + virtual float evaluate(float x) const = 0; + +#ifndef SK_SUPPORT_LEGACY_BITMAP_FILTER + virtual float evaluate_n(float val, float diff, int count, float* output) const { + float sum = 0; + for (int index = 0; index < count; index++) { + float filterValue = evaluate(val); + *output++ = filterValue; + sum += filterValue; + val += diff; + } + return sum; + } +#endif + +protected: + float fWidth; + float fInvWidth; + float fLookupMultiplier; + + mutable bool fPrecomputed; + mutable SkFixed fFilterTable[SKBITMAP_FILTER_TABLE_SIZE]; + mutable SkScalar fFilterTableScalar[SKBITMAP_FILTER_TABLE_SIZE]; + +private: + void precomputeTable() const { + fPrecomputed = true; + SkFixed *ftp = fFilterTable; + SkScalar *ftpScalar = fFilterTableScalar; + for (int x = 0; x < SKBITMAP_FILTER_TABLE_SIZE; ++x) { + float fx = ((float)x + .5f) * this->width() / SKBITMAP_FILTER_TABLE_SIZE; + float filter_value = evaluate(fx); + *ftpScalar++ = filter_value; + *ftp++ = SkFloatToFixed(filter_value); + } + } +}; + +class SkMitchellFilter final : public SkBitmapFilter { +public: +#ifdef SK_SUPPORT_LEGACY_BITMAP_FILTER + SkMitchellFilter() : INHERITED(2), B(1.f / 3), C(1.f / 3) {} +#else + SkMitchellFilter() + : INHERITED(2) + , fB(1.f / 3.f) + , fC(1.f / 3.f) + , fA1(-fB - 6*fC) + , fB1(6*fB + 30*fC) + , fC1(-12*fB - 48*fC) + , fD1(8*fB + 24*fC) + , fA2(12 - 9*fB - 6*fC) + , fB2(-18 + 12*fB + 6*fC) + , fD2(6 - 2*fB) + {} +#endif + + float evaluate(float x) const override { + x = fabsf(x); + if (x > 2.f) { + return 0; + } else if (x > 1.f) { +#ifdef SK_SUPPORT_LEGACY_BITMAP_FILTER + return ((-B - 6*C) * x*x*x + (6*B + 30*C) * x*x + + (-12*B - 48*C) * x + (8*B + 24*C)) * (1.f/6.f); +#else + return (((fA1 * x + fB1) * x + fC1) * x + fD1) * (1.f/6.f); +#endif + } else { +#ifdef SK_SUPPORT_LEGACY_BITMAP_FILTER + return ((12 - 9*B - 6*C) * x*x*x + + (-18 + 12*B + 6*C) * x*x + + (6 - 2*B)) * (1.f/6.f); +#else + return ((fA2 * x + fB2) * x*x + fD2) * (1.f/6.f); +#endif + } + } + +#ifndef SK_SUPPORT_LEGACY_BITMAP_FILTER + // TODO : native Sk4f abs + static Sk4f abs(const Sk4f& x) { + Sk4f neg = x < Sk4f(0); + return neg.thenElse(Sk4f(0) - x, x); + } + + Sk4f evalcore_n(const Sk4f& val) const { + Sk4f x = abs(val); + Sk4f over2 = x > Sk4f(2); + Sk4f over1 = x > Sk4f(1); + Sk4f poly1 = (((Sk4f(fA1) * x + Sk4f(fB1)) * x + Sk4f(fC1)) * x + Sk4f(fD1)) + * Sk4f(1.f/6.f); + Sk4f poly0 = ((Sk4f(fA2) * x + Sk4f(fB2)) * x*x + Sk4f(fD2)) * Sk4f(1.f/6.f); + return over2.thenElse(Sk4f(0), over1.thenElse(poly1, poly0)); + } + + float evaluate_n(float val, float diff, int count, float* output) const override { + Sk4f sum(0); + while (count >= 4) { + float v0 = val; + float v1 = val += diff; + float v2 = val += diff; + float v3 = val += diff; + val += diff; + Sk4f filterValue = evalcore_n(Sk4f(v0, v1, v2, v3)); + filterValue.store(output); + output += 4; + sum = sum + filterValue; + count -= 4; + } + float sums[4]; + sum.store(sums); + float result = sums[0] + sums[1] + sums[2] + sums[3]; + result += INHERITED::evaluate_n(val, diff, count, output); + return result; + } +#endif + + protected: +#ifdef SK_SUPPORT_LEGACY_BITMAP_FILTER + float B, C; +#else + float fB, fC; + float fA1, fB1, fC1, fD1; + float fA2, fB2, fD2; +#endif +private: + typedef SkBitmapFilter INHERITED; +}; + +class SkGaussianFilter final : public SkBitmapFilter { + float fAlpha, fExpWidth; + +public: + SkGaussianFilter(float a, float width = 2) + : SkBitmapFilter(width) + , fAlpha(a) + , fExpWidth(expf(-a * width * width)) + {} + + float evaluate(float x) const override { + return SkTMax(0.f, float(expf(-fAlpha*x*x) - fExpWidth)); + } +}; + +class SkTriangleFilter final : public SkBitmapFilter { +public: + SkTriangleFilter(float width = 1) : SkBitmapFilter(width) {} + + float evaluate(float x) const override { + return SkTMax(0.f, fWidth - fabsf(x)); + } +}; + +class SkBoxFilter final : public SkBitmapFilter { +public: + SkBoxFilter(float width = 0.5f) : SkBitmapFilter(width) {} + + float evaluate(float x) const override { + return (x >= -fWidth && x < fWidth) ? 1.0f : 0.0f; + } +}; + +class SkHammingFilter final : public SkBitmapFilter { +public: + SkHammingFilter(float width = 1) : SkBitmapFilter(width) {} + float evaluate(float x) const override { if (x <= -fWidth || x >= fWidth) { return 0.0f; // Outside of the window. @@ -152,23 +231,21 @@ public: } }; -class SkLanczosFilter: public SkBitmapFilter { - public: - SkLanczosFilter(float width=3.f) - : SkBitmapFilter(width) { - } +class SkLanczosFilter final : public SkBitmapFilter { +public: + SkLanczosFilter(float width = 3.f) : SkBitmapFilter(width) {} - float evaluate(float x) const override { - if (x <= -fWidth || x >= fWidth) { - return 0.0f; // Outside of the window. - } - if (x > -FLT_EPSILON && x < FLT_EPSILON) { - return 1.0f; // Special case the discontinuity at the origin. - } - float xpi = x * static_cast(SK_ScalarPI); - return (sk_float_sin(xpi) / xpi) * // sinc(x) - sk_float_sin(xpi / fWidth) / (xpi / fWidth); // sinc(x/fWidth) - } + float evaluate(float x) const override { + if (x <= -fWidth || x >= fWidth) { + return 0.0f; // Outside of the window. + } + if (x > -FLT_EPSILON && x < FLT_EPSILON) { + return 1.0f; // Special case the discontinuity at the origin. + } + float xpi = x * static_cast(SK_ScalarPI); + return (sk_float_sin(xpi) / xpi) * // sinc(x) + sk_float_sin(xpi / fWidth) / (xpi / fWidth); // sinc(x/fWidth) + } }; diff --git a/gfx/skia/skia/src/core/SkBitmapProcShader.cpp b/gfx/skia/skia/src/core/SkBitmapProcShader.cpp index dd97380bbd..804c688320 100644 --- a/gfx/skia/skia/src/core/SkBitmapProcShader.cpp +++ b/gfx/skia/skia/src/core/SkBitmapProcShader.cpp @@ -68,11 +68,6 @@ void SkBitmapProcShader::flatten(SkWriteBuffer& buffer) const { buffer.writeUInt(fTileModeY); } -static bool only_scale_and_translate(const SkMatrix& matrix) { - unsigned mask = SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask; - return (matrix.getType() & ~mask) == 0; -} - bool SkBitmapProcShader::isOpaque() const { return fRawBitmap.isOpaque(); } @@ -104,53 +99,25 @@ SkShader::Context* SkBitmapProcShader::onCreateContext(const ContextRec& rec, vo SkBitmapProvider(fRawBitmap), rec, storage); } +static bool only_scale_and_translate(const SkMatrix& matrix) { + unsigned mask = SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask; + return (matrix.getType() & ~mask) == 0; +} + SkBitmapProcShader::BitmapProcShaderContext::BitmapProcShaderContext(const SkShader& shader, const ContextRec& rec, SkBitmapProcState* state) : INHERITED(shader, rec) , fState(state) { - const SkPixmap& pixmap = fState->fPixmap; - bool isOpaque = pixmap.isOpaque(); - - // update fFlags - uint32_t flags = 0; - if (isOpaque && (255 == this->getPaintAlpha())) { - flags |= kOpaqueAlpha_Flag; + fFlags = 0; + if (fState->fPixmap.isOpaque() && (255 == this->getPaintAlpha())) { + fFlags |= kOpaqueAlpha_Flag; } - switch (pixmap.colorType()) { - case kRGB_565_SkColorType: - flags |= (kHasSpan16_Flag | kIntrinsicly16_Flag); - break; - case kIndex_8_SkColorType: - case kN32_SkColorType: - if (isOpaque) { - flags |= kHasSpan16_Flag; - } - break; - case kAlpha_8_SkColorType: - break; // never set kHasSpan16_Flag - default: - break; + if (1 == fState->fPixmap.height() && only_scale_and_translate(this->getTotalInverse())) { + fFlags |= kConstInY32_Flag; } - - if (rec.fPaint->isDither() && pixmap.colorType() != kRGB_565_SkColorType) { - // gradients can auto-dither in their 16bit sampler, but we don't so - // we clear the flag here. - flags &= ~kHasSpan16_Flag; - } - - // if we're only 1-pixel high, and we don't rotate, then we can claim this - if (1 == pixmap.height() && - only_scale_and_translate(this->getTotalInverse())) { - flags |= kConstInY32_Flag; - if (flags & kHasSpan16_Flag) { - flags |= kConstInY16_Flag; - } - } - - fFlags = flags; } SkBitmapProcShader::BitmapProcShaderContext::~BitmapProcShaderContext() { @@ -174,7 +141,7 @@ void SkBitmapProcShader::BitmapProcShaderContext::shadeSpan(int x, int y, SkPMCo int count) { const SkBitmapProcState& state = *fState; if (state.getShaderProc32()) { - state.getShaderProc32()(state, x, y, dstC, count); + state.getShaderProc32()(&state, x, y, dstC, count); return; } @@ -221,37 +188,6 @@ SkShader::Context::ShadeProc SkBitmapProcShader::BitmapProcShaderContext::asASha return nullptr; } -void SkBitmapProcShader::BitmapProcShaderContext::shadeSpan16(int x, int y, uint16_t dstC[], - int count) { - const SkBitmapProcState& state = *fState; - if (state.getShaderProc16()) { - state.getShaderProc16()(state, x, y, dstC, count); - return; - } - - uint32_t buffer[BUF_MAX]; - SkBitmapProcState::MatrixProc mproc = state.getMatrixProc(); - SkBitmapProcState::SampleProc16 sproc = state.getSampleProc16(); - int max = state.maxCountForBufferSize(sizeof(buffer)); - - SkASSERT(state.fPixmap.addr()); - - for (;;) { - int n = count; - if (n > max) { - n = max; - } - mproc(state, buffer, n, x, y); - sproc(state, buffer, n, dstC); - - if ((count -= n) == 0) { - break; - } - x += n; - dstC += n; - } -} - /////////////////////////////////////////////////////////////////////////////// #include "SkUnPreMultiply.h" diff --git a/gfx/skia/skia/src/core/SkBitmapProcShader.h b/gfx/skia/skia/src/core/SkBitmapProcShader.h index b69b028616..0346eff0e2 100644 --- a/gfx/skia/skia/src/core/SkBitmapProcShader.h +++ b/gfx/skia/skia/src/core/SkBitmapProcShader.h @@ -43,7 +43,6 @@ protected: void shadeSpan(int x, int y, SkPMColor dstC[], int count) override; ShadeProc asAShadeProc(void** ctx) override; - void shadeSpan16(int x, int y, uint16_t dstC[], int count) override; uint32_t getFlags() const override { return fFlags; } diff --git a/gfx/skia/skia/src/core/SkBitmapProcState.cpp b/gfx/skia/skia/src/core/SkBitmapProcState.cpp index 5ec6721436..b1438cb8a0 100644 --- a/gfx/skia/skia/src/core/SkBitmapProcState.cpp +++ b/gfx/skia/skia/src/core/SkBitmapProcState.cpp @@ -21,17 +21,16 @@ #if !SK_ARM_NEON_IS_NONE // These are defined in src/opts/SkBitmapProcState_arm_neon.cpp -extern const SkBitmapProcState::SampleProc16 gSkBitmapProcStateSample16_neon[]; extern const SkBitmapProcState::SampleProc32 gSkBitmapProcStateSample32_neon[]; extern void S16_D16_filter_DX_neon(const SkBitmapProcState&, const uint32_t*, int, uint16_t*); -extern void Clamp_S16_D16_filter_DX_shaderproc_neon(const SkBitmapProcState&, int, int, uint16_t*, int); -extern void Repeat_S16_D16_filter_DX_shaderproc_neon(const SkBitmapProcState&, int, int, uint16_t*, int); +extern void Clamp_S16_D16_filter_DX_shaderproc_neon(const void *, int, int, uint16_t*, int); +extern void Repeat_S16_D16_filter_DX_shaderproc_neon(const void *, int, int, uint16_t*, int); extern void SI8_opaque_D32_filter_DX_neon(const SkBitmapProcState&, const uint32_t*, int, SkPMColor*); -extern void SI8_opaque_D32_filter_DX_shaderproc_neon(const SkBitmapProcState&, int, int, uint32_t*, int); -extern void Clamp_SI8_opaque_D32_filter_DX_shaderproc_neon(const SkBitmapProcState&, int, int, uint32_t*, int); +extern void SI8_opaque_D32_filter_DX_shaderproc_neon(const void *, int, int, uint32_t*, int); +extern void Clamp_SI8_opaque_D32_filter_DX_shaderproc_neon(const void*, int, int, uint32_t*, int); #endif -extern void Clamp_S32_opaque_D32_nofilter_DX_shaderproc(const SkBitmapProcState&, int, int, uint32_t*, int); +extern void Clamp_S32_opaque_D32_nofilter_DX_shaderproc(const void*, int, int, uint32_t*, int); #define NAME_WRAP(x) x #include "SkBitmapProcState_filter.h" @@ -193,7 +192,6 @@ bool SkBitmapProcState::chooseProcs(const SkMatrix& inv, const SkPaint& paint) { fShaderProc32 = nullptr; fShaderProc16 = nullptr; fSampleProc32 = nullptr; - fSampleProc16 = nullptr; // recompute the triviality of the matrix here because we may have // changed it! @@ -336,45 +334,13 @@ bool SkBitmapProcState::chooseScanlineProcs(bool trivialMatrix, bool clampClamp, SG8_alpha_D32_filter_DX, SG8_alpha_D32_filter_DX }; - - static const SampleProc16 gSkBitmapProcStateSample16[] = { - S32_D16_nofilter_DXDY, - S32_D16_nofilter_DX, - S32_D16_filter_DXDY, - S32_D16_filter_DX, - - S16_D16_nofilter_DXDY, - S16_D16_nofilter_DX, - S16_D16_filter_DXDY, - S16_D16_filter_DX, - - SI8_D16_nofilter_DXDY, - SI8_D16_nofilter_DX, - SI8_D16_filter_DXDY, - SI8_D16_filter_DX, - - // Don't support 4444 -> 565 - nullptr, nullptr, nullptr, nullptr, - // Don't support A8 -> 565 - nullptr, nullptr, nullptr, nullptr, - // Don't support G8 -> 565 (but we could) - nullptr, nullptr, nullptr, nullptr - }; #endif fSampleProc32 = SK_ARM_NEON_WRAP(gSkBitmapProcStateSample32)[index]; index >>= 1; // shift away any opaque/alpha distinction - fSampleProc16 = SK_ARM_NEON_WRAP(gSkBitmapProcStateSample16)[index]; // our special-case shaderprocs - if (SK_ARM_NEON_WRAP(S16_D16_filter_DX) == fSampleProc16) { - if (clampClamp) { - fShaderProc16 = SK_ARM_NEON_WRAP(Clamp_S16_D16_filter_DX_shaderproc); - } else if (SkShader::kRepeat_TileMode == fTileModeX && - SkShader::kRepeat_TileMode == fTileModeY) { - fShaderProc16 = SK_ARM_NEON_WRAP(Repeat_S16_D16_filter_DX_shaderproc); - } - } else if (SK_ARM_NEON_WRAP(SI8_opaque_D32_filter_DX) == fSampleProc32 && clampClamp) { + if (SK_ARM_NEON_WRAP(SI8_opaque_D32_filter_DX) == fSampleProc32 && clampClamp) { fShaderProc32 = SK_ARM_NEON_WRAP(Clamp_SI8_opaque_D32_filter_DX_shaderproc); } else if (S32_opaque_D32_nofilter_DX == fSampleProc32 && clampClamp) { fShaderProc32 = Clamp_S32_opaque_D32_nofilter_DX_shaderproc; @@ -391,10 +357,11 @@ bool SkBitmapProcState::chooseScanlineProcs(bool trivialMatrix, bool clampClamp, return true; } -static void Clamp_S32_D32_nofilter_trans_shaderproc(const SkBitmapProcState& s, +static void Clamp_S32_D32_nofilter_trans_shaderproc(const void* sIn, int x, int y, SkPMColor* SK_RESTRICT colors, int count) { + const SkBitmapProcState& s = *static_cast(sIn); SkASSERT(((s.fInvType & ~SkMatrix::kTranslate_Mask)) == 0); SkASSERT(s.fInvKy == 0); SkASSERT(count > 0 && colors != nullptr); @@ -465,10 +432,11 @@ static inline int sk_int_mirror(int x, int n) { return x; } -static void Repeat_S32_D32_nofilter_trans_shaderproc(const SkBitmapProcState& s, +static void Repeat_S32_D32_nofilter_trans_shaderproc(const void* sIn, int x, int y, SkPMColor* SK_RESTRICT colors, int count) { + const SkBitmapProcState& s = *static_cast(sIn); SkASSERT(((s.fInvType & ~SkMatrix::kTranslate_Mask)) == 0); SkASSERT(s.fInvKy == 0); SkASSERT(count > 0 && colors != nullptr); @@ -505,10 +473,11 @@ static void Repeat_S32_D32_nofilter_trans_shaderproc(const SkBitmapProcState& s, } } -static void S32_D32_constX_shaderproc(const SkBitmapProcState& s, +static void S32_D32_constX_shaderproc(const void* sIn, int x, int y, SkPMColor* SK_RESTRICT colors, int count) { + const SkBitmapProcState& s = *static_cast(sIn); SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)) == 0); SkASSERT(s.fInvKy == 0); SkASSERT(count > 0 && colors != nullptr); @@ -618,7 +587,7 @@ static void S32_D32_constX_shaderproc(const SkBitmapProcState& s, sk_memset32(colors, color, count); } -static void DoNothing_shaderproc(const SkBitmapProcState&, int x, int y, +static void DoNothing_shaderproc(const void*, int x, int y, SkPMColor* SK_RESTRICT colors, int count) { // if we get called, the matrix is too tricky, so we just draw nothing sk_memset32(colors, 0, count); @@ -811,8 +780,9 @@ int SkBitmapProcState::maxCountForBufferSize(size_t bufferSize) const { /////////////////////// -void Clamp_S32_opaque_D32_nofilter_DX_shaderproc(const SkBitmapProcState& s, int x, int y, +void Clamp_S32_opaque_D32_nofilter_DX_shaderproc(const void* sIn, int x, int y, SkPMColor* SK_RESTRICT dst, int count) { + const SkBitmapProcState& s = *static_cast(sIn); SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)) == 0); @@ -820,13 +790,10 @@ void Clamp_S32_opaque_D32_nofilter_DX_shaderproc(const SkBitmapProcState& s, in SkFractionalInt fx; int dstY; { - SkPoint pt; - s.fInvProc(s.fInvMatrix, SkIntToScalar(x) + SK_ScalarHalf, SkIntToScalar(y) + SK_ScalarHalf, - &pt); - fx = SkScalarToFractionalInt(pt.fY); + const SkBitmapProcStateAutoMapper mapper(s, x, y); const unsigned maxY = s.fPixmap.height() - 1; - dstY = SkClampMax(SkFractionalIntToInt(fx), maxY); - fx = SkScalarToFractionalInt(pt.fX); + dstY = SkClampMax(SkFractionalIntToInt(mapper.y()), maxY); + fx = mapper.x(); } const SkPMColor* SK_RESTRICT src = s.fPixmap.addr32(0, dstY); diff --git a/gfx/skia/skia/src/core/SkBitmapProcState.h b/gfx/skia/skia/src/core/SkBitmapProcState.h index 2ce76bc83e..3f1d699cf2 100644 --- a/gfx/skia/skia/src/core/SkBitmapProcState.h +++ b/gfx/skia/skia/src/core/SkBitmapProcState.h @@ -12,6 +12,7 @@ #include "SkBitmapController.h" #include "SkBitmapFilter.h" #include "SkBitmapProvider.h" +#include "SkFloatBits.h" #include "SkMatrix.h" #include "SkMipMap.h" #include "SkPaint.h" @@ -31,11 +32,9 @@ struct SkBitmapProcState { SkBitmapProcState(const SkBitmap&, SkShader::TileMode tmx, SkShader::TileMode tmy); ~SkBitmapProcState(); - typedef void (*ShaderProc32)(const SkBitmapProcState&, int x, int y, - SkPMColor[], int count); + typedef void (*ShaderProc32)(const void* ctx, int x, int y, SkPMColor[], int count); - typedef void (*ShaderProc16)(const SkBitmapProcState&, int x, int y, - uint16_t[], int count); + typedef void (*ShaderProc16)(const void* ctx, int x, int y, uint16_t[], int count); typedef void (*MatrixProc)(const SkBitmapProcState&, uint32_t bitmapXY[], @@ -47,11 +46,6 @@ struct SkBitmapProcState { int count, SkPMColor colors[]); - typedef void (*SampleProc16)(const SkBitmapProcState&, - const uint32_t[], - int count, - uint16_t colors[]); - typedef U16CPU (*FixedTileProc)(SkFixed); // returns 0..0xFFFF typedef U16CPU (*FixedTileLowBitsProc)(SkFixed, int); // returns 0..0xF typedef U16CPU (*IntTileProc)(int value, int count); // returns 0..count-1 @@ -118,7 +112,6 @@ struct SkBitmapProcState { MatrixProc getMatrixProc() const { return fMatrixProc; } #endif SampleProc32 getSampleProc32() const { return fSampleProc32; } - SampleProc16 getSampleProc16() const { return fSampleProc16; } private: friend class SkBitmapProcShader; @@ -129,7 +122,6 @@ private: // These are used if the shaderproc is nullptr MatrixProc fMatrixProc; // chooseProcs SampleProc32 fSampleProc32; // chooseProcs - SampleProc16 fSampleProc16; // chooseProcs const SkBitmapProvider fProvider; @@ -195,9 +187,32 @@ void ClampX_ClampY_filter_affine(const SkBitmapProcState& s, uint32_t xy[], int count, int x, int y); void ClampX_ClampY_nofilter_affine(const SkBitmapProcState& s, uint32_t xy[], int count, int x, int y); -void S32_D16_filter_DX(const SkBitmapProcState& s, - const uint32_t* xy, int count, uint16_t* colors); -void S32_D16_filter_DXDY(const SkBitmapProcState& s, - const uint32_t* xy, int count, uint16_t* colors); + +// Helper class for mapping the middle of pixel (x, y) into SkFractionalInt bitmap space. +// TODO: filtered version which applies a fFilterOne{X,Y}/2 bias instead of epsilon? +class SkBitmapProcStateAutoMapper { +public: + SkBitmapProcStateAutoMapper(const SkBitmapProcState& s, int x, int y) { + SkPoint pt; + s.fInvProc(s.fInvMatrix, + SkIntToScalar(x) + SK_ScalarHalf, + SkIntToScalar(y) + SK_ScalarHalf, &pt); + + // SkFixed epsilon bias to ensure inverse-mapped bitmap coordinates are rounded + // consistently WRT geometry. Note that we only need the bias for positive scales: + // for negative scales, the rounding is intrinsically correct. + // We scale it to persist SkFractionalInt -> SkFixed conversions. + const SkFixed biasX = (s.fInvMatrix.getScaleX() > 0); + const SkFixed biasY = (s.fInvMatrix.getScaleY() > 0); + fX = SkScalarToFractionalInt(pt.x()) - SkFixedToFractionalInt(biasX); + fY = SkScalarToFractionalInt(pt.y()) - SkFixedToFractionalInt(biasY); + } + + SkFractionalInt x() const { return fX; } + SkFractionalInt y() const { return fY; } + +private: + SkFractionalInt fX, fY; +}; #endif diff --git a/gfx/skia/skia/src/core/SkBitmapProcState_matrixProcs.cpp b/gfx/skia/skia/src/core/SkBitmapProcState_matrixProcs.cpp index 1870a0e50e..1c4b7b6bcf 100644 --- a/gfx/skia/skia/src/core/SkBitmapProcState_matrixProcs.cpp +++ b/gfx/skia/skia/src/core/SkBitmapProcState_matrixProcs.cpp @@ -95,18 +95,20 @@ static SkBitmapProcState::MatrixProc ClampX_ClampY_Procs[] = { }; #define MAKENAME(suffix) RepeatX_RepeatY ## suffix -#define TILEX_PROCF(fx, max) SK_USHIFT16(((fx) & 0xFFFF) * ((max) + 1)) -#define TILEY_PROCF(fy, max) SK_USHIFT16(((fy) & 0xFFFF) * ((max) + 1)) -#define TILEX_LOW_BITS(fx, max) ((((fx) & 0xFFFF) * ((max) + 1) >> 12) & 0xF) -#define TILEY_LOW_BITS(fy, max) ((((fy) & 0xFFFF) * ((max) + 1) >> 12) & 0xF) +#define TILEX_PROCF(fx, max) SK_USHIFT16((unsigned)((fx) & 0xFFFF) * ((max) + 1)) +#define TILEY_PROCF(fy, max) SK_USHIFT16((unsigned)((fy) & 0xFFFF) * ((max) + 1)) +#define TILEX_LOW_BITS(fx, max) (((unsigned)((fx) & 0xFFFF) * ((max) + 1) >> 12) & 0xF) +#define TILEY_LOW_BITS(fy, max) (((unsigned)((fy) & 0xFFFF) * ((max) + 1) >> 12) & 0xF) #include "SkBitmapProcState_matrix.h" struct RepeatTileProcs { static unsigned X(const SkBitmapProcState&, SkFixed fx, int max) { - return SK_USHIFT16(((fx) & 0xFFFF) * ((max) + 1)); + SkASSERT(max < 65535); + return SK_USHIFT16((unsigned)((fx) & 0xFFFF) * ((max) + 1)); } static unsigned Y(const SkBitmapProcState&, SkFixed fy, int max) { - return SK_USHIFT16(((fy) & 0xFFFF) * ((max) + 1)); + SkASSERT(max < 65535); + return SK_USHIFT16((unsigned)((fy) & 0xFFFF) * ((max) + 1)); } }; @@ -176,7 +178,7 @@ static inline U16CPU fixed_repeat(SkFixed x) { #endif static inline U16CPU fixed_mirror(SkFixed x) { - SkFixed s = x << 15 >> 31; + SkFixed s = SkLeftShift(x, 15) >> 31; // s is FFFFFFFF if we're on an odd interval, or 0 if an even interval return (x ^ s) & 0xFFFF; } diff --git a/gfx/skia/skia/src/core/SkBitmapProcState_matrix_template.h b/gfx/skia/skia/src/core/SkBitmapProcState_matrix_template.h index 468013c30d..82fdddbece 100644 --- a/gfx/skia/skia/src/core/SkBitmapProcState_matrix_template.h +++ b/gfx/skia/skia/src/core/SkBitmapProcState_matrix_template.h @@ -22,13 +22,10 @@ void NoFilterProc_Scale(const SkBitmapProcState& s, uint32_t xy[], const unsigned maxX = s.fPixmap.width() - 1; SkFractionalInt fx; { - SkPoint pt; - s.fInvProc(s.fInvMatrix, SkIntToScalar(x) + SK_ScalarHalf, - SkIntToScalar(y) + SK_ScalarHalf, &pt); - fx = SkScalarToFractionalInt(pt.fY); + const SkBitmapProcStateAutoMapper mapper(s, x, y); const unsigned maxY = s.fPixmap.height() - 1; - *xy++ = TileProc::Y(s, SkFractionalIntToFixed(fx), maxY); - fx = SkScalarToFractionalInt(pt.fX); + *xy++ = TileProc::Y(s, SkFractionalIntToFixed(mapper.y()), maxY); + fx = mapper.x(); } if (0 == maxX) { @@ -80,13 +77,10 @@ void NoFilterProc_Affine(const SkBitmapProcState& s, uint32_t xy[], SkMatrix::kScale_Mask | SkMatrix::kAffine_Mask)) == 0); - SkPoint srcPt; - s.fInvProc(s.fInvMatrix, - SkIntToScalar(x) + SK_ScalarHalf, - SkIntToScalar(y) + SK_ScalarHalf, &srcPt); + const SkBitmapProcStateAutoMapper mapper(s, x, y); - SkFractionalInt fx = SkScalarToFractionalInt(srcPt.fX); - SkFractionalInt fy = SkScalarToFractionalInt(srcPt.fY); + SkFractionalInt fx = mapper.x(); + SkFractionalInt fy = mapper.y(); SkFractionalInt dx = s.fInvSxFractionalInt; SkFractionalInt dy = s.fInvKyFractionalInt; int maxX = s.fPixmap.width() - 1; diff --git a/gfx/skia/skia/src/core/SkBitmapProcState_procs.h b/gfx/skia/skia/src/core/SkBitmapProcState_procs.h index 03e192719c..cec079eae5 100644 --- a/gfx/skia/skia/src/core/SkBitmapProcState_procs.h +++ b/gfx/skia/skia/src/core/SkBitmapProcState_procs.h @@ -1,4 +1,3 @@ - /* * Copyright 2011 Google Inc. * @@ -88,7 +87,6 @@ static inline U8CPU Filter_8(unsigned x, unsigned y, #define FILTER_PROC(x, y, a, b, c, d, dst) NAME_WRAP(Filter_32_opaque)(x, y, a, b, c, d, dst) #define MAKENAME(suffix) NAME_WRAP(S32_opaque_D32 ## suffix) -#define DSTSIZE 32 #define SRCTYPE SkPMColor #define CHECKSTATE(state) SkASSERT(4 == state.fPixmap.info().bytesPerPixel()); \ SkASSERT(state.fAlphaScale == 256) @@ -100,7 +98,6 @@ static inline U8CPU Filter_8(unsigned x, unsigned y, #define FILTER_PROC(x, y, a, b, c, d, dst) NAME_WRAP(Filter_32_alpha)(x, y, a, b, c, d, dst, alphaScale) #define MAKENAME(suffix) NAME_WRAP(S32_alpha_D32 ## suffix) -#define DSTSIZE 32 #define SRCTYPE SkPMColor #define CHECKSTATE(state) SkASSERT(4 == state.fPixmap.info().bytesPerPixel()); \ SkASSERT(state.fAlphaScale < 256) @@ -119,7 +116,6 @@ static inline U8CPU Filter_8(unsigned x, unsigned y, } while (0) #define MAKENAME(suffix) NAME_WRAP(S16_opaque_D32 ## suffix) -#define DSTSIZE 32 #define SRCTYPE uint16_t #define CHECKSTATE(state) SkASSERT(kRGB_565_SkColorType == state.fPixmap.colorType()); \ SkASSERT(state.fAlphaScale == 256) @@ -135,7 +131,6 @@ static inline U8CPU Filter_8(unsigned x, unsigned y, } while (0) #define MAKENAME(suffix) NAME_WRAP(S16_alpha_D32 ## suffix) -#define DSTSIZE 32 #define SRCTYPE uint16_t #define CHECKSTATE(state) SkASSERT(kRGB_565_SkColorType == state.fPixmap.colorType()); \ SkASSERT(state.fAlphaScale < 256) @@ -150,7 +145,6 @@ static inline U8CPU Filter_8(unsigned x, unsigned y, #define FILTER_PROC(x, y, a, b, c, d, dst) NAME_WRAP(Filter_32_opaque)(x, y, a, b, c, d, dst) #define MAKENAME(suffix) NAME_WRAP(SI8_opaque_D32 ## suffix) -#define DSTSIZE 32 #define SRCTYPE uint8_t #define CHECKSTATE(state) SkASSERT(kIndex_8_SkColorType == state.fPixmap.colorType()); \ SkASSERT(state.fAlphaScale == 256) @@ -164,7 +158,6 @@ static inline U8CPU Filter_8(unsigned x, unsigned y, #define FILTER_PROC(x, y, a, b, c, d, dst) NAME_WRAP(Filter_32_alpha)(x, y, a, b, c, d, dst, alphaScale) #define MAKENAME(suffix) NAME_WRAP(SI8_alpha_D32 ## suffix) -#define DSTSIZE 32 #define SRCTYPE uint8_t #define CHECKSTATE(state) SkASSERT(kIndex_8_SkColorType == state.fPixmap.colorType()); \ SkASSERT(state.fAlphaScale < 256) @@ -181,7 +174,6 @@ static inline U8CPU Filter_8(unsigned x, unsigned y, #define FILTER_PROC(x, y, a, b, c, d, dst) *(dst) = Filter_4444_D32(x, y, a, b, c, d) #define MAKENAME(suffix) NAME_WRAP(S4444_opaque_D32 ## suffix) -#define DSTSIZE 32 #define SRCTYPE SkPMColor16 #define CHECKSTATE(state) SkASSERT(kARGB_4444_SkColorType == state.fPixmap.colorType()); \ SkASSERT(state.fAlphaScale == 256) @@ -197,7 +189,6 @@ static inline U8CPU Filter_8(unsigned x, unsigned y, } while (0) #define MAKENAME(suffix) NAME_WRAP(S4444_alpha_D32 ## suffix) -#define DSTSIZE 32 #define SRCTYPE SkPMColor16 #define CHECKSTATE(state) SkASSERT(kARGB_4444_SkColorType == state.fPixmap.colorType()); \ SkASSERT(state.fAlphaScale < 256) @@ -216,7 +207,6 @@ static inline U8CPU Filter_8(unsigned x, unsigned y, } while (0) #define MAKENAME(suffix) NAME_WRAP(SA8_alpha_D32 ## suffix) -#define DSTSIZE 32 #define SRCTYPE uint8_t #define CHECKSTATE(state) SkASSERT(kAlpha_8_SkColorType == state.fPixmap.colorType()); #define PREAMBLE(state) const SkPMColor pmColor = state.fPaintPMColor; @@ -235,7 +225,6 @@ static inline U8CPU Filter_8(unsigned x, unsigned y, } while (0) #define MAKENAME(suffix) NAME_WRAP(SG8_alpha_D32 ## suffix) -#define DSTSIZE 32 #define SRCTYPE uint8_t #define CHECKSTATE(state) SkASSERT(kGray_8_SkColorType == state.fPixmap.colorType()); #define PREAMBLE(state) unsigned alphaScale = state.fAlphaScale @@ -243,105 +232,6 @@ static inline U8CPU Filter_8(unsigned x, unsigned y, #define SRC_TO_FILTER(src) src #include "SkBitmapProcState_sample.h" -/***************************************************************************** - * - * D16 functions - * - */ - -// SRC == 8888 - -#undef FILTER_PROC -#define FILTER_PROC(x, y, a, b, c, d, dst) \ - do { \ - SkPMColor dstColor; \ - NAME_WRAP(Filter_32_opaque)(x, y, a, b, c, d, &dstColor); \ - (*dst) = SkPixel32ToPixel16(dstColor); \ - } while (0) - -#define MAKENAME(suffix) NAME_WRAP(S32_D16 ## suffix) -#define DSTSIZE 16 -#define SRCTYPE SkPMColor -#define CHECKSTATE(state) SkASSERT(4 == state.fPixmap.info().bytesPerPixel()); \ - SkASSERT(state.fPixmap.isOpaque()) -#define RETURNDST(src) SkPixel32ToPixel16(src) -#define SRC_TO_FILTER(src) src -#include "SkBitmapProcState_sample.h" - -// SRC == 565 - -#undef FILTER_PROC -#define FILTER_PROC(x, y, a, b, c, d, dst) \ - do { \ - uint32_t tmp = Filter_565_Expanded(x, y, a, b, c, d); \ - *(dst) = SkCompact_rgb_16((tmp) >> 5); \ - } while (0) - -#define MAKENAME(suffix) NAME_WRAP(S16_D16 ## suffix) -#define DSTSIZE 16 -#define SRCTYPE uint16_t -#define CHECKSTATE(state) SkASSERT(kRGB_565_SkColorType == state.fPixmap.colorType()) -#define RETURNDST(src) src -#define SRC_TO_FILTER(src) src -#include "SkBitmapProcState_sample.h" - -// SRC == Index8 - -#undef FILTER_PROC -#define FILTER_PROC(x, y, a, b, c, d, dst) \ - do { \ - uint32_t tmp = Filter_565_Expanded(x, y, a, b, c, d); \ - *(dst) = SkCompact_rgb_16((tmp) >> 5); \ - } while (0) - -#define MAKENAME(suffix) NAME_WRAP(SI8_D16 ## suffix) -#define DSTSIZE 16 -#define SRCTYPE uint8_t -#define CHECKSTATE(state) SkASSERT(kIndex_8_SkColorType == state.fPixmap.colorType()); \ - SkASSERT(state.fPixmap.isOpaque()) -#define PREAMBLE(state) const uint16_t* SK_RESTRICT table = state.fPixmap.ctable()->read16BitCache() -#define RETURNDST(src) table[src] -#define SRC_TO_FILTER(src) table[src] -#define POSTAMBLE(state) -#include "SkBitmapProcState_sample.h" - -/////////////////////////////////////////////////////////////////////////////// - -#undef FILTER_PROC -#define FILTER_PROC(x, y, a, b, c, d, dst) \ - do { \ - uint32_t tmp = Filter_565_Expanded(x, y, a, b, c, d); \ - *(dst) = SkCompact_rgb_16((tmp) >> 5); \ - } while (0) - - -// clamp - -#define TILEX_PROCF(fx, max) SkClampMax((fx) >> 16, max) -#define TILEY_PROCF(fy, max) SkClampMax((fy) >> 16, max) -#define TILEX_LOW_BITS(fx, max) (((fx) >> 12) & 0xF) -#define TILEY_LOW_BITS(fy, max) (((fy) >> 12) & 0xF) - -#define MAKENAME(suffix) NAME_WRAP(Clamp_S16_D16 ## suffix) -#define SRCTYPE uint16_t -#define DSTTYPE uint16_t -#define CHECKSTATE(state) SkASSERT(kRGB_565_SkColorType == state.fPixmap.colorType()) -#define SRC_TO_FILTER(src) src -#include "SkBitmapProcState_shaderproc.h" - - -#define TILEX_PROCF(fx, max) (((fx) & 0xFFFF) * ((max) + 1) >> 16) -#define TILEY_PROCF(fy, max) (((fy) & 0xFFFF) * ((max) + 1) >> 16) -#define TILEX_LOW_BITS(fx, max) ((((fx) & 0xFFFF) * ((max) + 1) >> 12) & 0xF) -#define TILEY_LOW_BITS(fy, max) ((((fy) & 0xFFFF) * ((max) + 1) >> 12) & 0xF) - -#define MAKENAME(suffix) NAME_WRAP(Repeat_S16_D16 ## suffix) -#define SRCTYPE uint16_t -#define DSTTYPE uint16_t -#define CHECKSTATE(state) SkASSERT(kRGB_565_SkColorType == state.fPixmap.colorType()) -#define SRC_TO_FILTER(src) src -#include "SkBitmapProcState_shaderproc.h" - #define TILEX_PROCF(fx, max) SkClampMax((fx) >> 16, max) #define TILEY_PROCF(fy, max) SkClampMax((fy) >> 16, max) @@ -352,7 +242,6 @@ static inline U8CPU Filter_8(unsigned x, unsigned y, #define FILTER_PROC(x, y, a, b, c, d, dst) NAME_WRAP(Filter_32_opaque)(x, y, a, b, c, d, dst) #define MAKENAME(suffix) NAME_WRAP(Clamp_SI8_opaque_D32 ## suffix) #define SRCTYPE uint8_t -#define DSTTYPE uint32_t #define CHECKSTATE(state) SkASSERT(kIndex_8_SkColorType == state.fPixmap.colorType()) #define PREAMBLE(state) const SkPMColor* SK_RESTRICT table = state.fPixmap.ctable()->readColors() #define SRC_TO_FILTER(src) table[src] diff --git a/gfx/skia/skia/src/core/SkBitmapProcState_sample.h b/gfx/skia/skia/src/core/SkBitmapProcState_sample.h index f70b758ac2..4a022884d5 100644 --- a/gfx/skia/skia/src/core/SkBitmapProcState_sample.h +++ b/gfx/skia/skia/src/core/SkBitmapProcState_sample.h @@ -7,40 +7,23 @@ #include "SkUtils.h" -#if DSTSIZE==32 - #define DSTTYPE SkPMColor -#elif DSTSIZE==16 - #define DSTTYPE uint16_t -#else - #error "need DSTSIZE to be 32 or 16" -#endif - -#if (DSTSIZE == 32) - #define BITMAPPROC_MEMSET(ptr, value, n) sk_memset32(ptr, value, n) -#elif (DSTSIZE == 16) - #define BITMAPPROC_MEMSET(ptr, value, n) sk_memset16(ptr, value, n) -#else - #error "unsupported DSTSIZE" -#endif - - // declare functions externally to suppress warnings. void MAKENAME(_nofilter_DXDY)(const SkBitmapProcState& s, const uint32_t* SK_RESTRICT xy, - int count, DSTTYPE* SK_RESTRICT colors); + int count, SkPMColor* SK_RESTRICT colors); void MAKENAME(_nofilter_DX)(const SkBitmapProcState& s, const uint32_t* SK_RESTRICT xy, - int count, DSTTYPE* SK_RESTRICT colors); + int count, SkPMColor* SK_RESTRICT colors); void MAKENAME(_filter_DX)(const SkBitmapProcState& s, const uint32_t* SK_RESTRICT xy, - int count, DSTTYPE* SK_RESTRICT colors); + int count, SkPMColor* SK_RESTRICT colors); void MAKENAME(_filter_DXDY)(const SkBitmapProcState& s, const uint32_t* SK_RESTRICT xy, - int count, DSTTYPE* SK_RESTRICT colors); + int count, SkPMColor* SK_RESTRICT colors); void MAKENAME(_nofilter_DXDY)(const SkBitmapProcState& s, const uint32_t* SK_RESTRICT xy, - int count, DSTTYPE* SK_RESTRICT colors) { + int count, SkPMColor* SK_RESTRICT colors) { SkASSERT(count > 0 && colors != nullptr); SkASSERT(kNone_SkFilterQuality == s.fFilterLevel); SkDEBUGCODE(CHECKSTATE(s);) @@ -82,7 +65,7 @@ void MAKENAME(_nofilter_DXDY)(const SkBitmapProcState& s, void MAKENAME(_nofilter_DX)(const SkBitmapProcState& s, const uint32_t* SK_RESTRICT xy, - int count, DSTTYPE* SK_RESTRICT colors) { + int count, SkPMColor* SK_RESTRICT colors) { SkASSERT(count > 0 && colors != nullptr); SkASSERT(s.fInvType <= (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)); SkASSERT(kNone_SkFilterQuality == s.fFilterLevel); @@ -104,8 +87,8 @@ void MAKENAME(_nofilter_DX)(const SkBitmapProcState& s, if (1 == s.fPixmap.width()) { src = srcAddr[0]; - DSTTYPE dstValue = RETURNDST(src); - BITMAPPROC_MEMSET(colors, dstValue, count); + SkPMColor dstValue = RETURNDST(src); + sk_memset32(colors, dstValue, count); } else { int i; for (i = (count >> 2); i > 0; --i) { @@ -137,7 +120,7 @@ void MAKENAME(_nofilter_DX)(const SkBitmapProcState& s, void MAKENAME(_filter_DX)(const SkBitmapProcState& s, const uint32_t* SK_RESTRICT xy, - int count, DSTTYPE* SK_RESTRICT colors) { + int count, SkPMColor* SK_RESTRICT colors) { SkASSERT(count > 0 && colors != nullptr); SkASSERT(s.fFilterLevel != kNone_SkFilterQuality); SkDEBUGCODE(CHECKSTATE(s);) @@ -183,7 +166,7 @@ void MAKENAME(_filter_DX)(const SkBitmapProcState& s, } void MAKENAME(_filter_DXDY)(const SkBitmapProcState& s, const uint32_t* SK_RESTRICT xy, - int count, DSTTYPE* SK_RESTRICT colors) { + int count, SkPMColor* SK_RESTRICT colors) { SkASSERT(count > 0 && colors != nullptr); SkASSERT(s.fFilterLevel != kNone_SkFilterQuality); SkDEBUGCODE(CHECKSTATE(s);) @@ -225,8 +208,6 @@ void MAKENAME(_filter_DXDY)(const SkBitmapProcState& s, } #undef MAKENAME -#undef DSTSIZE -#undef DSTTYPE #undef SRCTYPE #undef CHECKSTATE #undef RETURNDST @@ -245,4 +226,3 @@ void MAKENAME(_filter_DXDY)(const SkBitmapProcState& s, #undef GET_FILTER_ROW #undef GET_FILTER_ROW_PROC #undef GET_FILTER_PROC -#undef BITMAPPROC_MEMSET diff --git a/gfx/skia/skia/src/core/SkBitmapProcState_shaderproc.h b/gfx/skia/skia/src/core/SkBitmapProcState_shaderproc.h index 94b2d3b48c..d41ff063e4 100644 --- a/gfx/skia/skia/src/core/SkBitmapProcState_shaderproc.h +++ b/gfx/skia/skia/src/core/SkBitmapProcState_shaderproc.h @@ -1,4 +1,3 @@ - /* * Copyright 2011 Google Inc. * @@ -12,11 +11,10 @@ // Can't be static in the general case because some of these implementations // will be defined and referenced in different object files. -void SCALE_FILTER_NAME(const SkBitmapProcState& s, int x, int y, - DSTTYPE* SK_RESTRICT colors, int count); +void SCALE_FILTER_NAME(const void* sIn, int x, int y, SkPMColor* SK_RESTRICT colors, int count); -void SCALE_FILTER_NAME(const SkBitmapProcState& s, int x, int y, - DSTTYPE* SK_RESTRICT colors, int count) { +void SCALE_FILTER_NAME(const void* sIn, int x, int y, SkPMColor* SK_RESTRICT colors, int count) { + const SkBitmapProcState& s = *static_cast(sIn); SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)) == 0); SkASSERT(s.fInvKy == 0); @@ -84,7 +82,6 @@ void SCALE_FILTER_NAME(const SkBitmapProcState& s, int x, int y, #undef TILEY_LOW_BITS #undef MAKENAME #undef SRCTYPE -#undef DSTTYPE #undef CHECKSTATE #undef SRC_TO_FILTER #undef FILTER_TO_DST diff --git a/gfx/skia/skia/src/core/SkBitmapScaler.cpp b/gfx/skia/skia/src/core/SkBitmapScaler.cpp index 965955a2dc..dc04691c79 100644 --- a/gfx/skia/skia/src/core/SkBitmapScaler.cpp +++ b/gfx/skia/skia/src/core/SkBitmapScaler.cpp @@ -11,7 +11,6 @@ #include "SkImageInfo.h" #include "SkPixmap.h" #include "SkRect.h" -#include "SkScalar.h" #include "SkTArray.h" // SkResizeFilter ---------------------------------------------------------------- @@ -74,7 +73,7 @@ SkResizeFilter::SkResizeFilter(SkBitmapScaler::ResizeMethod method, fBitmapFilter = new SkTriangleFilter; break; case SkBitmapScaler::RESIZE_MITCHELL: - fBitmapFilter = new SkMitchellFilter(1.f / 3.f, 1.f / 3.f); + fBitmapFilter = new SkMitchellFilter; break; case SkBitmapScaler::RESIZE_HAMMING: fBitmapFilter = new SkHammingFilter; @@ -130,45 +129,65 @@ void SkResizeFilter::computeFilters(int srcSize, // to support the filtering function. float srcSupport = fBitmapFilter->width() / clampedScale; - // Speed up the divisions below by turning them into multiplies. float invScale = 1.0f / scale; - SkTArray filterValues(64); - SkTArray fixedFilterValues(64); + SkSTArray<64, float, true> filterValuesArray; + SkSTArray<64, SkConvolutionFilter1D::ConvolutionFixed, true> fixedFilterValuesArray; // Loop over all pixels in the output range. We will generate one set of // filter values for each one. Those values will tell us how to blend the // source pixels to compute the destination pixel. + + // This is the pixel in the source directly under the pixel in the dest. + // Note that we base computations on the "center" of the pixels. To see + // why, observe that the destination pixel at coordinates (0, 0) in a 5.0x + // downscale should "cover" the pixels around the pixel with *its center* + // at coordinates (2.5, 2.5) in the source, not those around (0, 0). + // Hence we need to scale coordinates (0.5, 0.5), not (0, 0). +#ifdef SK_SUPPORT_LEGACY_BITMAP_FILTER + int destLimit = SkScalarTruncToInt(SkScalarCeilToScalar(destSubsetHi) + - SkScalarFloorToScalar(destSubsetLo)); +#else + destSubsetLo = SkScalarFloorToScalar(destSubsetLo); + destSubsetHi = SkScalarCeilToScalar(destSubsetHi); + float srcPixel = (destSubsetLo + 0.5f) * invScale; + int destLimit = SkScalarTruncToInt(destSubsetHi - destSubsetLo); +#endif + output->reserveAdditional(destLimit, SkScalarCeilToInt(destLimit * srcSupport * 2)); +#ifdef SK_SUPPORT_LEGACY_BITMAP_FILTER for (int destSubsetI = SkScalarFloorToInt(destSubsetLo); destSubsetI < SkScalarCeilToInt(destSubsetHi); - destSubsetI++) { - // Reset the arrays. We don't declare them inside so they can re-use the - // same malloc-ed buffer. - filterValues.reset(); - fixedFilterValues.reset(); - - // This is the pixel in the source directly under the pixel in the dest. - // Note that we base computations on the "center" of the pixels. To see - // why, observe that the destination pixel at coordinates (0, 0) in a 5.0x - // downscale should "cover" the pixels around the pixel with *its center* - // at coordinates (2.5, 2.5) in the source, not those around (0, 0). - // Hence we need to scale coordinates (0.5, 0.5), not (0, 0). - float srcPixel = (static_cast(destSubsetI) + 0.5f) * invScale; - + destSubsetI++) +#else + for (int destI = 0; destI < destLimit; srcPixel += invScale, destI++) +#endif + { // Compute the (inclusive) range of source pixels the filter covers. +#ifdef SK_SUPPORT_LEGACY_BITMAP_FILTER + float srcPixel = (static_cast(destSubsetI) + 0.5f) * invScale; int srcBegin = SkTMax(0, SkScalarFloorToInt(srcPixel - srcSupport)); int srcEnd = SkTMin(srcSize - 1, SkScalarCeilToInt(srcPixel + srcSupport)); +#else + float srcBegin = SkTMax(0.f, SkScalarFloorToScalar(srcPixel - srcSupport)); + float srcEnd = SkTMin(srcSize - 1.f, SkScalarCeilToScalar(srcPixel + srcSupport)); +#endif // Compute the unnormalized filter value at each location of the source // it covers. +#ifdef SK_SUPPORT_LEGACY_BITMAP_FILTER float filterSum = 0.0f; // Sub of the filter values for normalizing. + int filterCount = srcEnd - srcBegin + 1; + filterValuesArray.reset(filterCount); for (int curFilterPixel = srcBegin; curFilterPixel <= srcEnd; curFilterPixel++) { - // Distance from the center of the filter, this is the filter coordinate - // in source space. We also need to consider the center of the pixel - // when comparing distance against 'srcPixel'. In the 5x downscale - // example used above the distance from the center of the filter to - // the pixel with coordinates (2, 2) should be 0, because its center - // is at (2.5, 2.5). +#endif + // Sum of the filter values for normalizing. + // Distance from the center of the filter, this is the filter coordinate + // in source space. We also need to consider the center of the pixel + // when comparing distance against 'srcPixel'. In the 5x downscale + // example used above the distance from the center of the filter to + // the pixel with coordinates (2, 2) should be 0, because its center + // is at (2.5, 2.5). +#ifdef SK_SUPPORT_LEGACY_BITMAP_FILTER float srcFilterDist = ((static_cast(curFilterPixel) + 0.5f) - srcPixel); @@ -177,79 +196,108 @@ void SkResizeFilter::computeFilters(int srcSize, // Compute the filter value at that location. float filterValue = fBitmapFilter->evaluate(destFilterDist); - filterValues.push_back(filterValue); + filterValuesArray[curFilterPixel - srcBegin] = filterValue; filterSum += filterValue; } - SkASSERT(!filterValues.empty()); - +#else + float destFilterDist = (srcBegin + 0.5f - srcPixel) * clampedScale; + int filterCount = SkScalarTruncToInt(srcEnd - srcBegin) + 1; + SkASSERT(filterCount > 0); + filterValuesArray.reset(filterCount); + float filterSum = fBitmapFilter->evaluate_n(destFilterDist, clampedScale, filterCount, + filterValuesArray.begin()); +#endif // The filter must be normalized so that we don't affect the brightness of // the image. Convert to normalized fixed point. - short fixedSum = 0; - for (int i = 0; i < filterValues.count(); i++) { - short curFixed = output->FloatToFixed(filterValues[i] / filterSum); + int fixedSum = 0; + fixedFilterValuesArray.reset(filterCount); + const float* filterValues = filterValuesArray.begin(); + SkConvolutionFilter1D::ConvolutionFixed* fixedFilterValues = fixedFilterValuesArray.begin(); +#ifndef SK_SUPPORT_LEGACY_BITMAP_FILTER + float invFilterSum = 1 / filterSum; +#endif + for (int fixedI = 0; fixedI < filterCount; fixedI++) { +#ifdef SK_SUPPORT_LEGACY_BITMAP_FILTER + int curFixed = SkConvolutionFilter1D::FloatToFixed(filterValues[fixedI] / filterSum); +#else + int curFixed = SkConvolutionFilter1D::FloatToFixed(filterValues[fixedI] * invFilterSum); +#endif fixedSum += curFixed; - fixedFilterValues.push_back(curFixed); + fixedFilterValues[fixedI] = SkToS16(curFixed); } + SkASSERT(fixedSum <= 0x7FFF); // The conversion to fixed point will leave some rounding errors, which // we add back in to avoid affecting the brightness of the image. We // arbitrarily add this to the center of the filter array (this won't always // be the center of the filter function since it could get clipped on the // edges, but it doesn't matter enough to worry about that case). - short leftovers = output->FloatToFixed(1.0f) - fixedSum; - fixedFilterValues[fixedFilterValues.count() / 2] += leftovers; + int leftovers = SkConvolutionFilter1D::FloatToFixed(1) - fixedSum; + fixedFilterValues[filterCount / 2] += leftovers; // Now it's ready to go. - output->AddFilter(srcBegin, &fixedFilterValues[0], - static_cast(fixedFilterValues.count())); +#ifdef SK_SUPPORT_LEGACY_BITMAP_FILTER + output->AddFilter(srcBegin, fixedFilterValues, filterCount); +#else + output->AddFilter(SkScalarFloorToInt(srcBegin), fixedFilterValues, filterCount); +#endif } if (convolveProcs.fApplySIMDPadding) { - convolveProcs.fApplySIMDPadding( output ); + convolveProcs.fApplySIMDPadding(output); } } -bool SkBitmapScaler::Resize(SkBitmap* resultPtr, const SkPixmap& source, ResizeMethod method, - int destWidth, int destHeight, SkBitmap::Allocator* allocator) { - if (nullptr == source.addr() || source.colorType() != kN32_SkColorType || - source.width() < 1 || source.height() < 1) - { +/////////////////////////////////////////////////////////////////////////////////////////////////// + +static bool valid_for_resize(const SkPixmap& source, int dstW, int dstH) { + // TODO: Seems like we shouldn't care about the swizzle of source, just that it's 8888 + return source.addr() && source.colorType() == kN32_SkColorType && + source.width() >= 1 && source.height() >= 1 && dstW >= 1 && dstH >= 1; +} + +bool SkBitmapScaler::Resize(const SkPixmap& result, const SkPixmap& source, ResizeMethod method) { + if (!valid_for_resize(source, result.width(), result.height())) { return false; } - - if (destWidth < 1 || destHeight < 1) { + if (!result.addr() || result.colorType() != source.colorType()) { return false; } SkConvolutionProcs convolveProcs= { 0, nullptr, nullptr, nullptr, nullptr }; PlatformConvolutionProcs(&convolveProcs); - SkRect destSubset = SkRect::MakeIWH(destWidth, destHeight); + SkRect destSubset = SkRect::MakeIWH(result.width(), result.height()); SkResizeFilter filter(method, source.width(), source.height(), - destWidth, destHeight, destSubset, convolveProcs); + result.width(), result.height(), destSubset, convolveProcs); // Get a subset encompassing this touched area. We construct the // offsets and row strides such that it looks like a new bitmap, while // referring to the old data. const uint8_t* sourceSubset = reinterpret_cast(source.addr()); - // Convolve into the result. - SkBitmap result; - result.setInfo(SkImageInfo::MakeN32(SkScalarCeilToInt(destSubset.width()), - SkScalarCeilToInt(destSubset.height()), - source.alphaType())); - result.allocPixels(allocator, nullptr); - if (!result.readyToDraw()) { - return false; + return BGRAConvolve2D(sourceSubset, static_cast(source.rowBytes()), + !source.isOpaque(), filter.xFilter(), filter.yFilter(), + static_cast(result.rowBytes()), + static_cast(result.writable_addr()), + convolveProcs, true); +} + +bool SkBitmapScaler::Resize(SkBitmap* resultPtr, const SkPixmap& source, ResizeMethod method, + int destWidth, int destHeight, SkBitmap::Allocator* allocator) { + // Preflight some of the checks, to avoid allocating the result if we don't need it. + if (!valid_for_resize(source, destWidth, destHeight)) { + return false; } - if (!BGRAConvolve2D(sourceSubset, static_cast(source.rowBytes()), - !source.isOpaque(), filter.xFilter(), filter.yFilter(), - static_cast(result.rowBytes()), - static_cast(result.getPixels()), - convolveProcs, true)) { + SkBitmap result; + result.setInfo(SkImageInfo::MakeN32(destWidth, destHeight, source.alphaType())); + result.allocPixels(allocator, nullptr); + + SkPixmap resultPM; + if (!result.peekPixels(&resultPM) || !Resize(resultPM, source, method)) { return false; } diff --git a/gfx/skia/skia/src/core/SkBitmapScaler.h b/gfx/skia/skia/src/core/SkBitmapScaler.h index 03feec3e12..3d734d6efc 100644 --- a/gfx/skia/skia/src/core/SkBitmapScaler.h +++ b/gfx/skia/skia/src/core/SkBitmapScaler.h @@ -29,6 +29,16 @@ public: RESIZE_LastMethod = RESIZE_MITCHELL, }; + /** + * Given already-allocated src and dst pixmaps, this will scale the src pixels using the + * specified resize-method and write the results into the pixels pointed to by dst. + */ + static bool Resize(const SkPixmap& dst, const SkPixmap& src, ResizeMethod method); + + /** + * Helper function that manages allocating a bitmap to hold the dst pixels, and then calls + * the pixmap version of Resize. + */ static bool Resize(SkBitmap* result, const SkPixmap& src, ResizeMethod method, int dest_width, int dest_height, SkBitmap::Allocator* = nullptr); diff --git a/gfx/skia/skia/src/core/SkBlitMask.h b/gfx/skia/skia/src/core/SkBlitMask.h index f36f9f3d82..b53ff7dfd8 100644 --- a/gfx/skia/skia/src/core/SkBlitMask.h +++ b/gfx/skia/skia/src/core/SkBlitMask.h @@ -44,7 +44,7 @@ public: * onto a row of dst colors. The RowFactory that returns this function ptr * will have been told the formats for the mask and the dst. */ - typedef void (*RowProc)(void* dst, const void* mask, + typedef void (*RowProc)(SkPMColor* dst, const void* mask, const SkPMColor* src, int width); /** diff --git a/gfx/skia/skia/src/core/SkBlitMask_D32.cpp b/gfx/skia/skia/src/core/SkBlitMask_D32.cpp index 3cc791af4e..c39ecfb759 100644 --- a/gfx/skia/skia/src/core/SkBlitMask_D32.cpp +++ b/gfx/skia/skia/src/core/SkBlitMask_D32.cpp @@ -76,9 +76,9 @@ bool SkBlitMask::BlitColor(const SkPixmap& device, const SkMask& mask, /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// -static void BW_RowProc_Blend(SkPMColor* SK_RESTRICT dst, - const uint8_t* SK_RESTRICT mask, - const SkPMColor* SK_RESTRICT src, int count) { +static void BW_RowProc_Blend( + SkPMColor* SK_RESTRICT dst, const void* maskIn, const SkPMColor* SK_RESTRICT src, int count) { + const uint8_t* SK_RESTRICT mask = static_cast(maskIn); int i, octuple = (count + 7) >> 3; for (i = 0; i < octuple; ++i) { int m = *mask++; @@ -105,9 +105,9 @@ static void BW_RowProc_Blend(SkPMColor* SK_RESTRICT dst, } } -static void BW_RowProc_Opaque(SkPMColor* SK_RESTRICT dst, - const uint8_t* SK_RESTRICT mask, - const SkPMColor* SK_RESTRICT src, int count) { +static void BW_RowProc_Opaque( + SkPMColor* SK_RESTRICT dst, const void* maskIn, const SkPMColor* SK_RESTRICT src, int count) { + const uint8_t* SK_RESTRICT mask = static_cast(maskIn); int i, octuple = (count + 7) >> 3; for (i = 0; i < octuple; ++i) { int m = *mask++; @@ -134,9 +134,9 @@ static void BW_RowProc_Opaque(SkPMColor* SK_RESTRICT dst, } } -static void A8_RowProc_Blend(SkPMColor* SK_RESTRICT dst, - const uint8_t* SK_RESTRICT mask, - const SkPMColor* SK_RESTRICT src, int count) { +static void A8_RowProc_Blend( + SkPMColor* SK_RESTRICT dst, const void* maskIn, const SkPMColor* SK_RESTRICT src, int count) { + const uint8_t* SK_RESTRICT mask = static_cast(maskIn); for (int i = 0; i < count; ++i) { if (mask[i]) { dst[i] = SkBlendARGB32(src[i], dst[i], mask[i]); @@ -153,9 +153,9 @@ static void A8_RowProc_Blend(SkPMColor* SK_RESTRICT dst, #define EXPAND1(v, m, s) (((v) >> 8) & (m)) * (s) #define COMBINE(e0, e1, m) ((((e0) >> 8) & (m)) | ((e1) & ~(m))) -static void A8_RowProc_Opaque(SkPMColor* SK_RESTRICT dst, - const uint8_t* SK_RESTRICT mask, - const SkPMColor* SK_RESTRICT src, int count) { +static void A8_RowProc_Opaque( + SkPMColor* SK_RESTRICT dst, const void* maskIn, const SkPMColor* SK_RESTRICT src, int count) { + const uint8_t* SK_RESTRICT mask = static_cast(maskIn); for (int i = 0; i < count; ++i) { int m = mask[i]; if (m) { @@ -188,9 +188,9 @@ static int src_alpha_blend(int src, int dst, int srcA, int mask) { return dst + SkAlphaMul(src - SkAlphaMul(srcA, dst), mask); } -static void LCD16_RowProc_Blend(SkPMColor* SK_RESTRICT dst, - const uint16_t* SK_RESTRICT mask, - const SkPMColor* SK_RESTRICT src, int count) { +static void LCD16_RowProc_Blend( + SkPMColor* SK_RESTRICT dst, const void* maskIn, const SkPMColor* SK_RESTRICT src, int count) { + const uint16_t* SK_RESTRICT mask = static_cast(maskIn); for (int i = 0; i < count; ++i) { uint16_t m = mask[i]; if (0 == m) { @@ -231,9 +231,9 @@ static void LCD16_RowProc_Blend(SkPMColor* SK_RESTRICT dst, } } -static void LCD16_RowProc_Opaque(SkPMColor* SK_RESTRICT dst, - const uint16_t* SK_RESTRICT mask, - const SkPMColor* SK_RESTRICT src, int count) { +static void LCD16_RowProc_Opaque( + SkPMColor* SK_RESTRICT dst, const void* maskIn, const SkPMColor* SK_RESTRICT src, int count) { + const uint16_t* SK_RESTRICT mask = static_cast(maskIn); for (int i = 0; i < count; ++i) { uint16_t m = mask[i]; if (0 == m) { diff --git a/gfx/skia/skia/src/core/SkBlitter.cpp b/gfx/skia/skia/src/core/SkBlitter.cpp index 064dc16eda..b73be40a45 100644 --- a/gfx/skia/skia/src/core/SkBlitter.cpp +++ b/gfx/skia/skia/src/core/SkBlitter.cpp @@ -9,7 +9,6 @@ #include "SkAntiRun.h" #include "SkColor.h" #include "SkColorFilter.h" -#include "SkFilterShader.h" #include "SkReadBuffer.h" #include "SkWriteBuffer.h" #include "SkMask.h" @@ -858,7 +857,7 @@ SkBlitter* SkBlitter::Choose(const SkPixmap& device, if (cf) { SkASSERT(shader); - shader = new SkFilterShader(shader, cf); + shader = shader->newWithColorFilter(cf); paint.writable()->setShader(shader)->unref(); // blitters should ignore the presence/absence of a filter, since // if there is one, the shader will take care of it. diff --git a/gfx/skia/skia/src/core/SkBlitter_ARGB32.cpp b/gfx/skia/skia/src/core/SkBlitter_ARGB32.cpp index e44ad02da5..a00ed86d8e 100644 --- a/gfx/skia/skia/src/core/SkBlitter_ARGB32.cpp +++ b/gfx/skia/skia/src/core/SkBlitter_ARGB32.cpp @@ -570,7 +570,7 @@ void SkARGB32_Shader_Blitter::blitMask(const SkMask& mask, const SkIRect& clip) SkXfermode* xfer = fXfermode; do { shaderContext->shadeSpan(x, y, span, width); - xfer->xfer32((SkPMColor*)dstRow, span, width, maskRow); + xfer->xfer32(reinterpret_cast(dstRow), span, width, maskRow); dstRow += dstRB; maskRow += maskRB; y += 1; @@ -578,7 +578,7 @@ void SkARGB32_Shader_Blitter::blitMask(const SkMask& mask, const SkIRect& clip) } else { do { shaderContext->shadeSpan(x, y, span, width); - proc(dstRow, maskRow, span, width); + proc(reinterpret_cast(dstRow), maskRow, span, width); dstRow += dstRB; maskRow += maskRB; y += 1; diff --git a/gfx/skia/skia/src/core/SkBlitter_RGB16.cpp b/gfx/skia/skia/src/core/SkBlitter_RGB16.cpp index 06dfeea1c1..38edd60a9d 100644 --- a/gfx/skia/skia/src/core/SkBlitter_RGB16.cpp +++ b/gfx/skia/skia/src/core/SkBlitter_RGB16.cpp @@ -134,20 +134,6 @@ private: typedef SkShaderBlitter INHERITED; }; -// used only if the shader can perform shadSpan16 -class SkRGB16_Shader16_Blitter : public SkRGB16_Shader_Blitter { -public: - SkRGB16_Shader16_Blitter(const SkPixmap& device, const SkPaint& paint, - SkShader::Context* shaderContext); - void blitH(int x, int y, int width) override; - virtual void blitAntiH(int x, int y, const SkAlpha* antialias, - const int16_t* runs) override; - void blitRect(int x, int y, int width, int height) override; - -private: - typedef SkRGB16_Shader_Blitter INHERITED; -}; - class SkRGB16_Shader_Xfermode_Blitter : public SkShaderBlitter { public: SkRGB16_Shader_Xfermode_Blitter(const SkPixmap& device, const SkPaint& paint, @@ -692,131 +678,6 @@ void SkRGB16_Blitter::blitRect(int x, int y, int width, int height) { /////////////////////////////////////////////////////////////////////////////// -SkRGB16_Shader16_Blitter::SkRGB16_Shader16_Blitter(const SkPixmap& device, - const SkPaint& paint, - SkShader::Context* shaderContext) - : SkRGB16_Shader_Blitter(device, paint, shaderContext) -{ - SkASSERT(SkShader::CanCallShadeSpan16(fShaderFlags)); -} - -void SkRGB16_Shader16_Blitter::blitH(int x, int y, int width) { - SkASSERT(x + width <= fDevice.width()); - - uint16_t* SK_RESTRICT device = fDevice.writable_addr16(x, y); - SkShader::Context* shaderContext = fShaderContext; - - int alpha = shaderContext->getSpan16Alpha(); - if (0xFF == alpha) { - shaderContext->shadeSpan16(x, y, device, width); - } else { - uint16_t* span16 = (uint16_t*)fBuffer; - shaderContext->shadeSpan16(x, y, span16, width); - SkBlendRGB16(span16, device, SkAlpha255To256(alpha), width); - } -} - -void SkRGB16_Shader16_Blitter::blitRect(int x, int y, int width, int height) { - SkShader::Context* shaderContext = fShaderContext; - uint16_t* dst = fDevice.writable_addr16(x, y); - size_t dstRB = fDevice.rowBytes(); - int alpha = shaderContext->getSpan16Alpha(); - - if (0xFF == alpha) { - if (fShaderFlags & SkShader::kConstInY16_Flag) { - // have the shader blit directly into the device the first time - shaderContext->shadeSpan16(x, y, dst, width); - // and now just memcpy that line on the subsequent lines - if (--height > 0) { - const uint16_t* orig = dst; - do { - dst = (uint16_t*)((char*)dst + dstRB); - memcpy(dst, orig, width << 1); - } while (--height); - } - } else { // need to call shadeSpan16 for every line - do { - shaderContext->shadeSpan16(x, y, dst, width); - y += 1; - dst = (uint16_t*)((char*)dst + dstRB); - } while (--height); - } - } else { - int scale = SkAlpha255To256(alpha); - uint16_t* span16 = (uint16_t*)fBuffer; - if (fShaderFlags & SkShader::kConstInY16_Flag) { - shaderContext->shadeSpan16(x, y, span16, width); - do { - SkBlendRGB16(span16, dst, scale, width); - dst = (uint16_t*)((char*)dst + dstRB); - } while (--height); - } else { - do { - shaderContext->shadeSpan16(x, y, span16, width); - SkBlendRGB16(span16, dst, scale, width); - y += 1; - dst = (uint16_t*)((char*)dst + dstRB); - } while (--height); - } - } -} - -void SkRGB16_Shader16_Blitter::blitAntiH(int x, int y, - const SkAlpha* SK_RESTRICT antialias, - const int16_t* SK_RESTRICT runs) { - SkShader::Context* shaderContext = fShaderContext; - SkPMColor* SK_RESTRICT span = fBuffer; - uint16_t* SK_RESTRICT device = fDevice.writable_addr16(x, y); - - int alpha = shaderContext->getSpan16Alpha(); - uint16_t* span16 = (uint16_t*)span; - - if (0xFF == alpha) { - for (;;) { - int count = *runs; - if (count <= 0) { - break; - } - SkASSERT(count <= fDevice.width()); // don't overrun fBuffer - - int aa = *antialias; - if (aa == 255) { - // go direct to the device! - shaderContext->shadeSpan16(x, y, device, count); - } else if (aa) { - shaderContext->shadeSpan16(x, y, span16, count); - SkBlendRGB16(span16, device, SkAlpha255To256(aa), count); - } - device += count; - runs += count; - antialias += count; - x += count; - } - } else { // span alpha is < 255 - alpha = SkAlpha255To256(alpha); - for (;;) { - int count = *runs; - if (count <= 0) { - break; - } - SkASSERT(count <= fDevice.width()); // don't overrun fBuffer - - int aa = SkAlphaMul(*antialias, alpha); - if (aa) { - shaderContext->shadeSpan16(x, y, span16, count); - SkBlendRGB16(span16, device, SkAlpha255To256(aa), count); - } - - device += count; - runs += count; - antialias += count; - x += count; - } - } -} - -/////////////////////////////////////////////////////////////////////////////// - SkRGB16_Shader_Blitter::SkRGB16_Shader_Blitter(const SkPixmap& device, const SkPaint& paint, SkShader::Context* shaderContext) @@ -834,8 +695,7 @@ SkRGB16_Shader_Blitter::SkRGB16_Shader_Blitter(const SkPixmap& device, if (!(shaderFlags & SkShader::kOpaqueAlpha_Flag)) { flags |= SkBlitRow::kSrcPixelAlpha_Flag; } - // don't dither if the shader is really 16bit - if (paint.isDither() && !(shaderFlags & SkShader::kIntrinsicly16_Flag)) { + if (paint.isDither()) { flags |= SkBlitRow::kDither_Flag; } // used when we know our global alpha is 0xFF @@ -1047,8 +907,6 @@ SkBlitter* SkBlitter_ChooseD565(const SkPixmap& device, const SkPaint& paint, if (mode) { blitter = allocator->createT(device, paint, shaderContext); - } else if (shaderContext->canCallShadeSpan16()) { - blitter = allocator->createT(device, paint, shaderContext); } else { blitter = allocator->createT(device, paint, shaderContext); } diff --git a/gfx/skia/skia/src/core/SkBuffer.cpp b/gfx/skia/skia/src/core/SkBuffer.cpp index 86c3bed3f1..df8dc69594 100644 --- a/gfx/skia/skia/src/core/SkBuffer.cpp +++ b/gfx/skia/skia/src/core/SkBuffer.cpp @@ -35,7 +35,7 @@ size_t SkRBuffer::skipToAlign4() } bool SkRBufferWithSizeCheck::read(void* buffer, size_t size) { - fError = fError || (fPos + size > fStop); + fError = fError || (size > static_cast(fStop - fPos)); if (!fError && (size > 0)) { readNoSizeCheck(buffer, size); } diff --git a/gfx/skia/skia/src/core/SkCanvas.cpp b/gfx/skia/skia/src/core/SkCanvas.cpp index 1e49fe272d..e577b21932 100644 --- a/gfx/skia/skia/src/core/SkCanvas.cpp +++ b/gfx/skia/skia/src/core/SkCanvas.cpp @@ -17,6 +17,8 @@ #include "SkDrawLooper.h" #include "SkErrorInternals.h" #include "SkImage.h" +#include "SkImage_Base.h" +#include "SkMatrixUtils.h" #include "SkMetaData.h" #include "SkNinePatchIter.h" #include "SkPaintPriv.h" @@ -35,10 +37,12 @@ #include #if SK_SUPPORT_GPU +#include "GrContext.h" #include "GrRenderTarget.h" +#include "SkGr.h" #endif -#define SK_SUPPORT_SRC_BOUNDS_BLOAT_FOR_IMAGEFILTERS +#define RETURN_ON_NULL(ptr) do { if (nullptr == (ptr)) return; } while (0) /* * Return true if the drawing this rect would hit every pixels in the canvas. @@ -112,7 +116,6 @@ bool SkCanvas::Internal_Private_GetTreatSpriteAsBitmap() { // experimental for faster tiled drawing... //#define SK_ENABLE_CLIP_QUICKREJECT - //#define SK_TRACE_SAVERESTORE #ifdef SK_TRACE_SAVERESTORE @@ -479,7 +482,7 @@ public: // Make rawBounds include all paint outsets except for those due to image filters. rawBounds = &apply_paint_to_bounds_sans_imagefilter(*fPaint, *rawBounds, &storage); } - (void)canvas->internalSaveLayer(rawBounds, &tmp, SkCanvas::kARGB_ClipLayer_SaveFlag, + (void)canvas->internalSaveLayer(SkCanvas::SaveLayerRec(rawBounds, &tmp), SkCanvas::kFullLayer_SaveLayerStrategy); fTempLayerForImageFilter = true; // we remove the imagefilter/xfermode inside doNext() @@ -592,6 +595,13 @@ bool AutoDrawLooper::doNext(SkDrawFilter::Type drawType) { ////////// macros to place around the internal draw calls ////////////////// +#define LOOPER_BEGIN_DRAWBITMAP(paint, skipLayerForFilter, bounds) \ + this->predrawNotify(); \ + AutoDrawLooper looper(this, fProps, paint, skipLayerForFilter, bounds); \ + while (looper.next(SkDrawFilter::kBitmap_Type)) { \ + SkDrawIter iter(this); + + #define LOOPER_BEGIN_DRAWDEVICE(paint, type) \ this->predrawNotify(); \ AutoDrawLooper looper(this, fProps, paint, true); \ @@ -1044,15 +1054,15 @@ void SkCanvas::internalSave() { fClipStack->save(); } -static bool bounds_affects_clip(SkCanvas::SaveFlags flags) { +bool SkCanvas::BoundsAffectsClip(SaveLayerFlags saveLayerFlags) { #ifdef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG - return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0; + return !(saveLayerFlags & SkCanvas::kDontClipToLayer_PrivateSaveLayerFlag); #else return true; #endif } -bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveFlags flags, +bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveLayerFlags saveLayerFlags, SkIRect* intersection, const SkImageFilter* imageFilter) { SkIRect clipBounds; if (!this->getClipDeviceBounds(&clipBounds)) { @@ -1075,6 +1085,10 @@ bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveFlags flags, } else { bounds = nullptr; } +#else + if (bounds && !imageFilter->canComputeFastBounds()) { + bounds = nullptr; + } #endif } SkIRect ir; @@ -1085,7 +1099,7 @@ bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveFlags flags, r.roundOut(&ir); // early exit if the layer's bounds are clipped out if (!ir.intersect(clipBounds)) { - if (bounds_affects_clip(flags)) { + if (BoundsAffectsClip(saveLayerFlags)) { fCachedLocalClipBoundsDirty = true; fMCRec->fRasterClip.setEmpty(); } @@ -1096,7 +1110,7 @@ bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveFlags flags, } SkASSERT(!ir.isEmpty()); - if (bounds_affects_clip(flags)) { + if (BoundsAffectsClip(saveLayerFlags)) { // Simplify the current clips since they will be applied properly during restore() fCachedLocalClipBoundsDirty = true; fClipStack->clipDevRect(ir, SkRegion::kReplace_Op); @@ -1109,30 +1123,98 @@ bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveFlags flags, return true; } +#ifdef SK_SUPPORT_LEGACY_SAVEFLAGS +uint32_t SkCanvas::SaveFlagsToSaveLayerFlags(SaveFlags flags) { + uint32_t layerFlags = 0; + + if (0 == (flags & kClipToLayer_SaveFlag)) { + layerFlags |= kDontClipToLayer_PrivateSaveLayerFlag; + } + if (0 == (flags & kHasAlphaLayer_SaveFlag)) { + layerFlags |= kIsOpaque_SaveLayerFlag; + } + return layerFlags; +} + +uint32_t SkCanvas::SaveLayerFlagsToSaveFlags(SaveLayerFlags layerFlags) { + uint32_t saveFlags = 0; + + if (0 == (layerFlags & kDontClipToLayer_PrivateSaveLayerFlag)) { + saveFlags |= kClipToLayer_SaveFlag; + } + if (0 == (layerFlags & kIsOpaque_SaveLayerFlag)) { + saveFlags |= kHasAlphaLayer_SaveFlag; + } + return saveFlags; +} +#endif + int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint) { - if (gIgnoreSaveLayerBounds) { - bounds = nullptr; - } - SaveLayerStrategy strategy = this->willSaveLayer(bounds, paint, kARGB_ClipLayer_SaveFlag); - fSaveCount += 1; - this->internalSaveLayer(bounds, paint, kARGB_ClipLayer_SaveFlag, strategy); - return this->getSaveCount() - 1; + return this->saveLayer(SaveLayerRec(bounds, paint, 0)); } +#ifdef SK_SUPPORT_LEGACY_SAVEFLAGS int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint, SaveFlags flags) { + return this->saveLayer(SaveLayerRec(bounds, paint, SaveFlagsToSaveLayerFlags(flags))); +} +#endif + +int SkCanvas::saveLayerPreserveLCDTextRequests(const SkRect* bounds, const SkPaint* paint) { + return this->saveLayer(SaveLayerRec(bounds, paint, kPreserveLCDText_SaveLayerFlag)); +} + +int SkCanvas::saveLayer(const SaveLayerRec& origRec) { + SaveLayerRec rec(origRec); if (gIgnoreSaveLayerBounds) { - bounds = nullptr; + rec.fBounds = nullptr; } - SaveLayerStrategy strategy = this->willSaveLayer(bounds, paint, flags); + SaveLayerStrategy strategy = this->getSaveLayerStrategy(rec); fSaveCount += 1; - this->internalSaveLayer(bounds, paint, flags, strategy); + this->internalSaveLayer(rec, strategy); return this->getSaveCount() - 1; } -void SkCanvas::internalSaveLayer(const SkRect* bounds, const SkPaint* paint, SaveFlags flags, - SaveLayerStrategy strategy) { +static void draw_filter_into_device(SkBaseDevice* src, const SkImageFilter* filter, + SkBaseDevice* dst, const SkMatrix& ctm) { + + SkBitmap srcBM; + +#if SK_SUPPORT_GPU + GrRenderTarget* srcRT = src->accessRenderTarget(); + if (srcRT && !srcRT->asTexture() && dst->accessRenderTarget()) { + // When both the src & the dst are on the gpu but the src doesn't have a texture, + // we create a temporary texture for the draw. + // TODO: we should actually only copy the portion of the source needed to apply the image + // filter + GrContext* context = srcRT->getContext(); + SkAutoTUnref tex(context->textureProvider()->createTexture(srcRT->desc(), true)); + + context->copySurface(tex, srcRT); + + GrWrapTextureInBitmap(tex, src->width(), src->height(), src->isOpaque(), &srcBM); + } else +#endif + { + srcBM = src->accessBitmap(false); + } + + SkCanvas c(dst); + + SkAutoTUnref localF(filter->newWithLocalMatrix(ctm)); + SkPaint p; + p.setImageFilter(localF); + const SkScalar x = SkIntToScalar(src->getOrigin().x()); + const SkScalar y = SkIntToScalar(src->getOrigin().y()); + c.drawBitmap(srcBM, x, y, &p); +} + +void SkCanvas::internalSaveLayer(const SaveLayerRec& rec, SaveLayerStrategy strategy) { + const SkRect* bounds = rec.fBounds; + const SkPaint* paint = rec.fPaint; + SaveLayerFlags saveLayerFlags = rec.fSaveLayerFlags; + #ifndef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG - flags |= kClipToLayer_SaveFlag; + saveLayerFlags &= ~kDontClipToLayer_PrivateSaveLayerFlag; #endif // do this before we create the layer. We don't call the public save() since @@ -1142,7 +1224,7 @@ void SkCanvas::internalSaveLayer(const SkRect* bounds, const SkPaint* paint, Sav fDeviceCMDirty = true; SkIRect ir; - if (!this->clipRectBounds(bounds, flags, &ir, paint ? paint->getImageFilter() : nullptr)) { + if (!this->clipRectBounds(bounds, saveLayerFlags, &ir, paint ? paint->getImageFilter() : nullptr)) { return; } @@ -1152,7 +1234,7 @@ void SkCanvas::internalSaveLayer(const SkRect* bounds, const SkPaint* paint, Sav return; } - bool isOpaque = !SkToBool(flags & kHasAlphaLayer_SaveFlag); + bool isOpaque = SkToBool(saveLayerFlags & kIsOpaque_SaveLayerFlag); SkPixelGeometry geo = fProps.pixelGeometry(); if (paint) { // TODO: perhaps add a query to filters so we might preserve opaqueness... @@ -1172,8 +1254,11 @@ void SkCanvas::internalSaveLayer(const SkRect* bounds, const SkPaint* paint, Sav bool forceSpriteOnRestore = false; { + const bool preserveLCDText = kOpaque_SkAlphaType == info.alphaType() || + (saveLayerFlags & kPreserveLCDText_SaveLayerFlag); const SkBaseDevice::TileUsage usage = SkBaseDevice::kNever_TileUsage; - const SkBaseDevice::CreateInfo createInfo = SkBaseDevice::CreateInfo(info, usage, geo); + const SkBaseDevice::CreateInfo createInfo = SkBaseDevice::CreateInfo(info, usage, geo, + preserveLCDText, false); SkBaseDevice* newDev = device->onCreateDevice(createInfo, paint); if (nullptr == newDev) { // If onCreateDevice didn't succeed, try raster (e.g. PDF couldn't handle the paint) @@ -1188,8 +1273,12 @@ void SkCanvas::internalSaveLayer(const SkRect* bounds, const SkPaint* paint, Sav } device = newDev; } - device->setOrigin(ir.fLeft, ir.fTop); + + if (rec.fBackdrop) { + draw_filter_into_device(fMCRec->fTopLayer->fDevice, rec.fBackdrop, device, fMCRec->fMatrix); + } + DeviceCM* layer = new DeviceCM(device, paint, this, fConservativeRasterClip, forceSpriteOnRestore); device->unref(); @@ -1200,9 +1289,16 @@ void SkCanvas::internalSaveLayer(const SkRect* bounds, const SkPaint* paint, Sav } int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha) { - return this->saveLayerAlpha(bounds, alpha, kARGB_ClipLayer_SaveFlag); + if (0xFF == alpha) { + return this->saveLayer(bounds, nullptr); + } else { + SkPaint tmpPaint; + tmpPaint.setAlpha(alpha); + return this->saveLayer(bounds, &tmpPaint); + } } +#ifdef SK_SUPPORT_LEGACY_SAVEFLAGS int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha, SaveFlags flags) { if (0xFF == alpha) { @@ -1213,6 +1309,7 @@ int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha, return this->saveLayer(bounds, &tmpPaint, flags); } } +#endif void SkCanvas::internalRestore() { SkASSERT(fMCStack.count() != 0); @@ -1314,31 +1411,6 @@ bool SkCanvas::onAccessTopLayerPixels(SkPixmap* pmap) { return dev && dev->accessPixels(pmap); } -SkAutoROCanvasPixels::SkAutoROCanvasPixels(SkCanvas* canvas) { - fAddr = canvas->peekPixels(&fInfo, &fRowBytes); - if (nullptr == fAddr) { - fInfo = canvas->imageInfo(); - if (kUnknown_SkColorType == fInfo.colorType() || !fBitmap.tryAllocPixels(fInfo)) { - return; // failure, fAddr is nullptr - } - if (!canvas->readPixels(&fBitmap, 0, 0)) { - return; // failure, fAddr is nullptr - } - fAddr = fBitmap.getPixels(); - fRowBytes = fBitmap.rowBytes(); - } - SkASSERT(fAddr); // success -} - -bool SkAutoROCanvasPixels::asROBitmap(SkBitmap* bitmap) const { - if (fAddr) { - return bitmap->installPixels(fInfo, const_cast(fAddr), fRowBytes); - } else { - bitmap->reset(); - return false; - } -} - ///////////////////////////////////////////////////////////////////////////// void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y, @@ -1361,10 +1433,13 @@ void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y, const SkBitmap& src = srcDev->accessBitmap(false); SkMatrix matrix = *iter.fMatrix; matrix.postTranslate(SkIntToScalar(-pos.x()), SkIntToScalar(-pos.y())); +#ifdef SK_SUPPORT_SRC_BOUNDS_BLOAT_FOR_IMAGEFILTERS SkIRect clipBounds = SkIRect::MakeWH(srcDev->width(), srcDev->height()); +#else + SkIRect clipBounds = iter.fClip->getBounds().makeOffset(-pos.x(), -pos.y()); +#endif SkAutoTUnref cache(dstDev->getImageFilterCache()); - SkImageFilter::Context ctx(matrix, clipBounds, cache.get(), - SkImageFilter::kApprox_SizeConstraint); + SkImageFilter::Context ctx(matrix, clipBounds, cache.get()); if (filter->filterImage(&proxy, src, ctx, &dst, &offset)) { SkPaint tmpUnfiltered(*paint); tmpUnfiltered.setImageFilter(nullptr); @@ -1381,56 +1456,8 @@ void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y, LOOPER_END } -void SkCanvas::onDrawSprite(const SkBitmap& bitmap, int x, int y, const SkPaint* paint) { - if (gTreatSpriteAsBitmap) { - this->save(); - this->resetMatrix(); - this->drawBitmap(bitmap, SkIntToScalar(x), SkIntToScalar(y), paint); - this->restore(); - return; - } - - TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawSprite()"); - if (bitmap.drawsNothing()) { - return; - } - SkDEBUGCODE(bitmap.validate();) - - SkPaint tmp; - if (nullptr == paint) { - paint = &tmp; - } - - LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type) - - while (iter.next()) { - paint = &looper.paint(); - SkImageFilter* filter = paint->getImageFilter(); - SkIPoint pos = { x - iter.getX(), y - iter.getY() }; - if (filter && !iter.fDevice->canHandleImageFilter(filter)) { - SkImageFilter::DeviceProxy proxy(iter.fDevice); - SkBitmap dst; - SkIPoint offset = SkIPoint::Make(0, 0); - SkMatrix matrix = *iter.fMatrix; - matrix.postTranslate(SkIntToScalar(-pos.x()), SkIntToScalar(-pos.y())); - const SkIRect clipBounds = bitmap.bounds(); - SkAutoTUnref cache(iter.fDevice->getImageFilterCache()); - SkImageFilter::Context ctx(matrix, clipBounds, cache.get(), - SkImageFilter::kApprox_SizeConstraint); - if (filter->filterImage(&proxy, bitmap, ctx, &dst, &offset)) { - SkPaint tmpUnfiltered(*paint); - tmpUnfiltered.setImageFilter(nullptr); - iter.fDevice->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(), - tmpUnfiltered); - } - } else { - iter.fDevice->drawSprite(iter, bitmap, pos.x(), pos.y(), *paint); - } - } - LOOPER_END -} - ///////////////////////////////////////////////////////////////////////////// + void SkCanvas::translate(SkScalar dx, SkScalar dy) { SkMatrix m; m.setTranslate(dx, dy); @@ -1903,11 +1930,13 @@ void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) { } void SkCanvas::drawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) { + RETURN_ON_NULL(image); this->onDrawImage(image, x, y, paint); } void SkCanvas::drawImageRect(const SkImage* image, const SkRect& src, const SkRect& dst, const SkPaint* paint, SrcRectConstraint constraint) { + RETURN_ON_NULL(image); if (dst.isEmpty() || src.isEmpty()) { return; } @@ -1916,17 +1945,20 @@ void SkCanvas::drawImageRect(const SkImage* image, const SkRect& src, const SkRe void SkCanvas::drawImageRect(const SkImage* image, const SkIRect& isrc, const SkRect& dst, const SkPaint* paint, SrcRectConstraint constraint) { + RETURN_ON_NULL(image); this->drawImageRect(image, SkRect::Make(isrc), dst, paint, constraint); } void SkCanvas::drawImageRect(const SkImage* image, const SkRect& dst, const SkPaint* paint, SrcRectConstraint constraint) { + RETURN_ON_NULL(image); this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()), dst, paint, constraint); } void SkCanvas::drawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst, const SkPaint* paint) { + RETURN_ON_NULL(image); if (dst.isEmpty()) { return; } @@ -1973,16 +2005,10 @@ void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, con this->onDrawBitmapNine(bitmap, center, dst, paint); } -void SkCanvas::drawSprite(const SkBitmap& bitmap, int left, int top, const SkPaint* paint) { - if (bitmap.drawsNothing()) { - return; - } - this->onDrawSprite(bitmap, left, top, paint); -} - void SkCanvas::drawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[], const SkColor colors[], int count, SkXfermode::Mode mode, const SkRect* cull, const SkPaint* paint) { + RETURN_ON_NULL(atlas); if (count <= 0) { return; } @@ -2197,6 +2223,27 @@ void SkCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) { LOOPER_END } +bool SkCanvas::canDrawBitmapAsSprite(SkScalar x, SkScalar y, int w, int h, const SkPaint& paint) { + if (!paint.getImageFilter()) { + return false; + } + + const SkMatrix& ctm = this->getTotalMatrix(); + if (!SkTreatAsSprite(ctm, SkISize::Make(w, h), paint)) { + return false; + } + + // Currently we can only use the filterSprite code if we are clipped to the bitmap's bounds. + // Once we can filter and the filter will return a result larger than itself, we should be + // able to remove this constraint. + // skbug.com/4526 + // + SkPoint pt; + ctm.mapXY(x, y, &pt); + SkIRect ir = SkIRect::MakeXYWH(SkScalarRoundToInt(pt.x()), SkScalarRoundToInt(pt.y()), w, h); + return ir.contains(fMCRec->fRasterClip.getBounds()); +} + void SkCanvas::onDrawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) { TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImage()"); SkRect bounds = SkRect::MakeXYWH(x, y, @@ -2215,11 +2262,25 @@ void SkCanvas::onDrawImage(const SkImage* image, SkScalar x, SkScalar y, const S if (nullptr == paint) { paint = lazy.init(); } - - LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &bounds) - + + const bool drawAsSprite = this->canDrawBitmapAsSprite(x, y, image->width(), image->height(), + *paint); + LOOPER_BEGIN_DRAWBITMAP(*paint, drawAsSprite, &bounds) + while (iter.next()) { - iter.fDevice->drawImage(iter, image, x, y, looper.paint()); + const SkPaint& pnt = looper.paint(); + if (drawAsSprite && pnt.getImageFilter()) { + SkBitmap bitmap; + if (as_IB(image)->asBitmapForImageFilters(&bitmap)) { + SkPoint pt; + iter.fMatrix->mapXY(x, y, &pt); + iter.fDevice->drawBitmapAsSprite(iter, bitmap, + SkScalarRoundToInt(pt.fX), + SkScalarRoundToInt(pt.fY), pnt); + } + } else { + iter.fDevice->drawImage(iter, image, x, y, pnt); + } } LOOPER_END @@ -2228,10 +2289,8 @@ void SkCanvas::onDrawImage(const SkImage* image, SkScalar x, SkScalar y, const S void SkCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst, const SkPaint* paint, SrcRectConstraint constraint) { TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImageRect()"); - SkRect storage; - const SkRect* bounds = &dst; if (nullptr == paint || paint->canComputeFastBounds()) { - storage = dst; + SkRect storage = dst; if (paint) { paint->computeFastBounds(dst, &storage); } @@ -2244,7 +2303,7 @@ void SkCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const Sk paint = lazy.init(); } - LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, SkDrawFilter::kBitmap_Type, bounds, + LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, SkDrawFilter::kBitmap_Type, &dst, image->isOpaque()) while (iter.next()) { @@ -2281,12 +2340,23 @@ void SkCanvas::onDrawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y, cons bounds = &storage; } - LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds) + const bool drawAsSprite = bounds && this->canDrawBitmapAsSprite(x, y, bitmap.width(), + bitmap.height(), *paint); + LOOPER_BEGIN_DRAWBITMAP(*paint, drawAsSprite, bounds) while (iter.next()) { - iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint()); + const SkPaint& pnt = looper.paint(); + if (drawAsSprite && pnt.getImageFilter()) { + SkPoint pt; + iter.fMatrix->mapXY(x, y, &pt); + iter.fDevice->drawBitmapAsSprite(iter, bitmap, + SkScalarRoundToInt(pt.fX), + SkScalarRoundToInt(pt.fY), pnt); + } else { + iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint()); + } } - + LOOPER_END } @@ -2298,9 +2368,8 @@ void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src, return; } - SkRect storage; - const SkRect* bounds = &dst; if (nullptr == paint || paint->canComputeFastBounds()) { + SkRect storage; if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) { return; } @@ -2311,7 +2380,7 @@ void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src, paint = lazy.init(); } - LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, SkDrawFilter::kBitmap_Type, bounds, + LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, SkDrawFilter::kBitmap_Type, &dst, bitmap.isOpaque()) while (iter.next()) { @@ -2332,9 +2401,8 @@ void SkCanvas::onDrawImageNine(const SkImage* image, const SkIRect& center, cons const SkPaint* paint) { TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImageNine()"); - SkRect storage; - const SkRect* bounds = &dst; if (nullptr == paint || paint->canComputeFastBounds()) { + SkRect storage; if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) { return; } @@ -2345,7 +2413,7 @@ void SkCanvas::onDrawImageNine(const SkImage* image, const SkIRect& center, cons paint = lazy.init(); } - LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds) + LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst) while (iter.next()) { iter.fDevice->drawImageNine(iter, image, center, dst, looper.paint()); @@ -2359,9 +2427,8 @@ void SkCanvas::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, c TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmapNine()"); SkDEBUGCODE(bitmap.validate();) - SkRect storage; - const SkRect* bounds = &dst; if (nullptr == paint || paint->canComputeFastBounds()) { + SkRect storage; if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) { return; } @@ -2372,7 +2439,7 @@ void SkCanvas::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, c paint = lazy.init(); } - LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds) + LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst) while (iter.next()) { iter.fDevice->drawBitmapNine(iter, bitmap, center, dst, looper.paint()); @@ -2461,14 +2528,14 @@ void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint, start.fY); r.fTop = offset; r.fBottom = offset + height; - DrawRect(draw, paint, r, textSize); + DrawRect(draw, paint, r, 1); } if (flags & SkPaint::kStrikeThruText_Flag) { SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset, start.fY); r.fTop = offset; r.fBottom = offset + height; - DrawRect(draw, paint, r, textSize); + DrawRect(draw, paint, r, 1); } } } @@ -2584,10 +2651,9 @@ void SkCanvas::drawTextOnPath(const void* text, size_t byteLength, const SkPath& } void SkCanvas::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y, const SkPaint& paint) { + RETURN_ON_NULL(blob); TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextBlob()"); - if (blob) { - this->onDrawTextBlob(blob, x, y, paint); - } + this->onDrawTextBlob(blob, x, y, paint); } void SkCanvas::onDrawVertices(VertexMode vmode, int vertexCount, @@ -2638,23 +2704,21 @@ void SkCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4], } void SkCanvas::drawDrawable(SkDrawable* dr, SkScalar x, SkScalar y) { - if (dr) { - if (x || y) { - SkMatrix matrix = SkMatrix::MakeTrans(x, y); - this->onDrawDrawable(dr, &matrix); - } else { - this->onDrawDrawable(dr, nullptr); - } + RETURN_ON_NULL(dr); + if (x || y) { + SkMatrix matrix = SkMatrix::MakeTrans(x, y); + this->onDrawDrawable(dr, &matrix); + } else { + this->onDrawDrawable(dr, nullptr); } } void SkCanvas::drawDrawable(SkDrawable* dr, const SkMatrix* matrix) { - if (dr) { - if (matrix && matrix->isIdentity()) { - matrix = nullptr; - } - this->onDrawDrawable(dr, matrix); + RETURN_ON_NULL(dr); + if (matrix && matrix->isIdentity()) { + matrix = nullptr; } + this->onDrawDrawable(dr, matrix); } void SkCanvas::onDrawDrawable(SkDrawable* dr, const SkMatrix* matrix) { @@ -2824,17 +2888,17 @@ void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength, #define kMaxPictureOpsToUnrollInsteadOfRef 1 void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) { + RETURN_ON_NULL(picture); + TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPicture()"); - if (picture) { - if (matrix && matrix->isIdentity()) { - matrix = nullptr; - } - if (picture->approximateOpCount() <= kMaxPictureOpsToUnrollInsteadOfRef) { - SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect()); - picture->playback(this); - } else { - this->onDrawPicture(picture, matrix, paint); - } + if (matrix && matrix->isIdentity()) { + matrix = nullptr; + } + if (picture->approximateOpCount() <= kMaxPictureOpsToUnrollInsteadOfRef) { + SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect()); + picture->playback(this); + } else { + this->onDrawPicture(picture, matrix, paint); } } diff --git a/gfx/skia/skia/src/core/SkColorFilter.cpp b/gfx/skia/skia/src/core/SkColorFilter.cpp index 031f376eee..747e5ee107 100644 --- a/gfx/skia/skia/src/core/SkColorFilter.cpp +++ b/gfx/skia/skia/src/core/SkColorFilter.cpp @@ -140,7 +140,10 @@ SkColorFilter* SkColorFilter::CreateComposeFilter(SkColorFilter* outer, SkColorF return new SkComposeColorFilter(outer, inner, count); } +#include "SkModeColorFilter.h" + SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkColorFilter) SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkComposeColorFilter) +SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkModeColorFilter) SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END diff --git a/gfx/skia/skia/src/core/SkColorFilterShader.cpp b/gfx/skia/skia/src/core/SkColorFilterShader.cpp new file mode 100644 index 0000000000..dc90f95762 --- /dev/null +++ b/gfx/skia/skia/src/core/SkColorFilterShader.cpp @@ -0,0 +1,136 @@ +/* + * Copyright 2013 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkColorFilterShader.h" +#include "SkReadBuffer.h" +#include "SkWriteBuffer.h" +#include "SkShader.h" +#include "SkString.h" + +#if SK_SUPPORT_GPU +#include "GrFragmentProcessor.h" +#endif + +SkColorFilterShader::SkColorFilterShader(SkShader* shader, SkColorFilter* filter) + : fShader(SkRef(shader)) + , fFilter(SkRef(filter)) +{ + SkASSERT(shader); + SkASSERT(filter); +} + +SkFlattenable* SkColorFilterShader::CreateProc(SkReadBuffer& buffer) { + SkAutoTUnref shader(buffer.readShader()); + SkAutoTUnref filter(buffer.readColorFilter()); + if (!shader.get() || !filter.get()) { + return nullptr; + } + return new SkColorFilterShader(shader, filter); +} + +void SkColorFilterShader::flatten(SkWriteBuffer& buffer) const { + buffer.writeFlattenable(fShader); + buffer.writeFlattenable(fFilter); +} + +uint32_t SkColorFilterShader::FilterShaderContext::getFlags() const { + const SkColorFilterShader& filterShader = static_cast(fShader); + + uint32_t shaderF = fShaderContext->getFlags(); + uint32_t filterF = filterShader.fFilter->getFlags(); + + // if the filter might change alpha, clear the opaque flag in the shader + if (!(filterF & SkColorFilter::kAlphaUnchanged_Flag)) { + shaderF &= ~SkShader::kOpaqueAlpha_Flag; + } + return shaderF; +} + +SkShader::Context* SkColorFilterShader::onCreateContext(const ContextRec& rec, + void* storage) const { + char* shaderContextStorage = (char*)storage + sizeof(FilterShaderContext); + SkShader::Context* shaderContext = fShader->createContext(rec, shaderContextStorage); + if (nullptr == shaderContext) { + return nullptr; + } + return new (storage) FilterShaderContext(*this, shaderContext, rec); +} + +size_t SkColorFilterShader::contextSize() const { + return sizeof(FilterShaderContext) + fShader->contextSize(); +} + +SkColorFilterShader::FilterShaderContext::FilterShaderContext( + const SkColorFilterShader& filterShader, + SkShader::Context* shaderContext, + const ContextRec& rec) + : INHERITED(filterShader, rec) + , fShaderContext(shaderContext) +{} + +SkColorFilterShader::FilterShaderContext::~FilterShaderContext() { + fShaderContext->~Context(); +} + +void SkColorFilterShader::FilterShaderContext::shadeSpan(int x, int y, SkPMColor result[], + int count) { + const SkColorFilterShader& filterShader = static_cast(fShader); + + fShaderContext->shadeSpan(x, y, result, count); + filterShader.fFilter->filterSpan(result, count, result); +} + +#if SK_SUPPORT_GPU +///////////////////////////////////////////////////////////////////// + +const GrFragmentProcessor* SkColorFilterShader::asFragmentProcessor( + GrContext* context, + const SkMatrix& viewM, + const SkMatrix* localMatrix, + SkFilterQuality fq) const { + + SkAutoTUnref fp1(fShader->asFragmentProcessor(context, viewM, + localMatrix, fq)); + if (!fp1.get()) { + return nullptr; + } + + SkAutoTUnref fp2(fFilter->asFragmentProcessor(context)); + if (!fp2.get()) { + return fp1.release(); + } + + const GrFragmentProcessor* fpSeries[] = { fp1.get(), fp2.get() }; + + return GrFragmentProcessor::RunInSeries(fpSeries, 2); +} +#endif + +#ifndef SK_IGNORE_TO_STRING +void SkColorFilterShader::toString(SkString* str) const { + str->append("SkColorFilterShader: ("); + + str->append("Shader: "); + fShader->toString(str); + str->append(" Filter: "); + // TODO: add "fFilter->toString(str);" once SkColorFilter::toString is added + + this->INHERITED::toString(str); + + str->append(")"); +} +#endif + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +SkShader* SkShader::newWithColorFilter(SkColorFilter* filter) const { + SkShader* base = const_cast(this); + if (!filter) { + return SkRef(base); + } + return new SkColorFilterShader(base, filter); +} diff --git a/gfx/skia/skia/src/core/SkFilterShader.h b/gfx/skia/skia/src/core/SkColorFilterShader.h similarity index 51% rename from gfx/skia/skia/src/core/SkFilterShader.h rename to gfx/skia/skia/src/core/SkColorFilterShader.h index ea5eaed34f..e92908ee83 100644 --- a/gfx/skia/skia/src/core/SkFilterShader.h +++ b/gfx/skia/skia/src/core/SkColorFilterShader.h @@ -1,57 +1,62 @@ /* - * Copyright 2012 Google Inc. + * Copyright 2016 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ -#ifndef SkFilterShader_DEFINED -#define SkFilterShader_DEFINED +#ifndef SkColorFilterShader_DEFINED +#define SkColorFilterShader_DEFINED +#include "SkColorFilter.h" #include "SkShader.h" -class SkColorFilter; - -class SkFilterShader : public SkShader { +class SkColorFilterShader : public SkShader { public: - SkFilterShader(SkShader* shader, SkColorFilter* filter); - virtual ~SkFilterShader(); - + SkColorFilterShader(SkShader* shader, SkColorFilter* filter); + size_t contextSize() const override; + +#if SK_SUPPORT_GPU + const GrFragmentProcessor* asFragmentProcessor(GrContext*, + const SkMatrix& viewM, + const SkMatrix* localMatrix, + SkFilterQuality) const override; +#endif class FilterShaderContext : public SkShader::Context { public: // Takes ownership of shaderContext and calls its destructor. - FilterShaderContext(const SkFilterShader&, SkShader::Context*, const ContextRec&); + FilterShaderContext(const SkColorFilterShader&, SkShader::Context*, const ContextRec&); virtual ~FilterShaderContext(); - + uint32_t getFlags() const override; - + void shadeSpan(int x, int y, SkPMColor[], int count) override; - + void set3DMask(const SkMask* mask) override { // forward to our proxy fShaderContext->set3DMask(mask); } - + private: SkShader::Context* fShaderContext; - + typedef SkShader::Context INHERITED; }; - + SK_TO_STRING_OVERRIDE() - SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkFilterShader) - + SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkColorFilterShader) + protected: void flatten(SkWriteBuffer&) const override; Context* onCreateContext(const ContextRec&, void* storage) const override; - - + + private: - SkShader* fShader; - SkColorFilter* fFilter; - + SkAutoTUnref fShader; + SkAutoTUnref fFilter; + typedef SkShader INHERITED; }; diff --git a/gfx/skia/skia/src/core/SkColorShader.h b/gfx/skia/skia/src/core/SkColorShader.h index e3fddb231a..25a1d6c8d0 100644 --- a/gfx/skia/skia/src/core/SkColorShader.h +++ b/gfx/skia/skia/src/core/SkColorShader.h @@ -34,15 +34,12 @@ public: ColorShaderContext(const SkColorShader& shader, const ContextRec&); uint32_t getFlags() const override; - uint8_t getSpan16Alpha() const override; void shadeSpan(int x, int y, SkPMColor span[], int count) override; - void shadeSpan16(int x, int y, uint16_t span[], int count) override; void shadeSpanAlpha(int x, int y, uint8_t alpha[], int count) override; private: SkPMColor fPMColor; uint32_t fFlags; - uint16_t fColor16; typedef SkShader::Context INHERITED; }; diff --git a/gfx/skia/skia/src/core/SkComposeShader.cpp b/gfx/skia/skia/src/core/SkComposeShader.cpp index 0dc0da68ef..d433ff2d43 100644 --- a/gfx/skia/skia/src/core/SkComposeShader.cpp +++ b/gfx/skia/skia/src/core/SkComposeShader.cpp @@ -137,10 +137,6 @@ void SkComposeShader::ComposeShaderContext::shadeSpan(int x, int y, SkPMColor re SkXfermode* mode = static_cast(fShader).fMode; unsigned scale = SkAlpha255To256(this->getPaintAlpha()); -#ifdef SK_BUILD_FOR_ANDROID - scale = 256; // ugh -- maintain old bug/behavior for now -#endif - SkPMColor tmp[TMP_COLOR_COUNT]; if (nullptr == mode) { // implied SRC_OVER diff --git a/gfx/skia/skia/src/core/SkConvolver.cpp b/gfx/skia/skia/src/core/SkConvolver.cpp index 28d3ab139c..c662e2ddaf 100644 --- a/gfx/skia/skia/src/core/SkConvolver.cpp +++ b/gfx/skia/skia/src/core/SkConvolver.cpp @@ -3,9 +3,7 @@ // found in the LICENSE file. #include "SkConvolver.h" -#include "SkMath.h" -#include "SkSize.h" -#include "SkTypes.h" +#include "SkTArray.h" namespace { @@ -284,21 +282,6 @@ SkConvolutionFilter1D::SkConvolutionFilter1D() SkConvolutionFilter1D::~SkConvolutionFilter1D() { } -void SkConvolutionFilter1D::AddFilter(int filterOffset, - const float* filterValues, - int filterLength) { - SkASSERT(filterLength > 0); - - SkTArray fixedValues; - fixedValues.reset(filterLength); - - for (int i = 0; i < filterLength; ++i) { - fixedValues.push_back(FloatToFixed(filterValues[i])); - } - - AddFilter(filterOffset, &fixedValues[0], filterLength); -} - void SkConvolutionFilter1D::AddFilter(int filterOffset, const ConvolutionFixed* filterValues, int filterLength) { @@ -323,9 +306,7 @@ void SkConvolutionFilter1D::AddFilter(int filterOffset, filterLength = lastNonZero + 1 - firstNonZero; SkASSERT(filterLength > 0); - for (int i = firstNonZero; i <= lastNonZero; i++) { - fFilterValues.push_back(filterValues[i]); - } + fFilterValues.append(filterLength, &filterValues[firstNonZero]); } else { // Here all the factors were zeroes. filterLength = 0; @@ -339,7 +320,7 @@ void SkConvolutionFilter1D::AddFilter(int filterOffset, instance.fOffset = filterOffset; instance.fTrimmedLength = filterLength; instance.fLength = filterSize; - fFilters.push_back(instance); + fFilters.push(instance); fMaxFilter = SkTMax(fMaxFilter, filterLength); } diff --git a/gfx/skia/skia/src/core/SkConvolver.h b/gfx/skia/skia/src/core/SkConvolver.h index 00305fa9fa..4e23f6cc17 100644 --- a/gfx/skia/skia/src/core/SkConvolver.h +++ b/gfx/skia/skia/src/core/SkConvolver.h @@ -6,8 +6,7 @@ #define SK_CONVOLVER_H #include "SkSize.h" -#include "SkTypes.h" -#include "SkTArray.h" +#include "SkTDArray.h" // avoid confusion with Mac OS X's math library (Carbon) #if defined(__APPLE__) @@ -58,6 +57,11 @@ public: // output image. int numValues() const { return static_cast(fFilters.count()); } + void reserveAdditional(int filterCount, int filterValueCount) { + fFilters.setReserve(fFilters.count() + filterCount); + fFilterValues.setReserve(fFilterValues.count() + filterValueCount); + } + // Appends the given list of scaling values for generating a given output // pixel. |filterOffset| is the distance from the edge of the image to where // the scaling factors start. The scaling factors apply to the source pixels @@ -68,13 +72,6 @@ public: // brighness of the image. // // The filterLength must be > 0. - // - // This version will automatically convert your input to ConvolutionFixed point. - SK_API void AddFilter(int filterOffset, - const float* filterValues, - int filterLength); - - // Same as the above version, but the input is already ConvolutionFixed point. void AddFilter(int filterOffset, const ConvolutionFixed* filterValues, int filterLength); @@ -112,7 +109,7 @@ public: // SIMD padding which happens outside of this class. void addFilterValue( ConvolutionFixed val ) { - fFilterValues.push_back( val ); + fFilterValues.push( val ); } private: struct FilterInstance { @@ -132,12 +129,12 @@ private: }; // Stores the information for each filter added to this class. - SkTArray fFilters; + SkTDArray fFilters; // We store all the filter values in this flat list, indexed by // |FilterInstance.data_location| to avoid the mallocs required for storing // each one separately. - SkTArray fFilterValues; + SkTDArray fFilterValues; // The maximum size of any filter we've added. int fMaxFilter; diff --git a/gfx/skia/skia/src/core/SkCubicClipper.cpp b/gfx/skia/skia/src/core/SkCubicClipper.cpp index 81ef18de7a..469fc222e3 100644 --- a/gfx/skia/skia/src/core/SkCubicClipper.cpp +++ b/gfx/skia/skia/src/core/SkCubicClipper.cpp @@ -20,7 +20,7 @@ void SkCubicClipper::setClip(const SkIRect& clip) { } -static bool chopMonoCubicAtY(SkPoint pts[4], SkScalar y, SkScalar* t) { +bool SkCubicClipper::ChopMonoAtY(const SkPoint pts[4], SkScalar y, SkScalar* t) { SkScalar ycrv[4]; ycrv[0] = pts[0].fY - y; ycrv[1] = pts[1].fY - y; @@ -131,7 +131,7 @@ bool SkCubicClipper::clipCubic(const SkPoint srcPts[4], SkPoint dst[4]) { SkPoint tmp[7]; // for SkChopCubicAt // are we partially above - if (dst[0].fY < ctop && chopMonoCubicAtY(dst, ctop, &t)) { + if (dst[0].fY < ctop && ChopMonoAtY(dst, ctop, &t)) { SkChopCubicAt(dst, tmp, t); dst[0] = tmp[3]; dst[1] = tmp[4]; @@ -139,7 +139,7 @@ bool SkCubicClipper::clipCubic(const SkPoint srcPts[4], SkPoint dst[4]) { } // are we partially below - if (dst[3].fY > cbot && chopMonoCubicAtY(dst, cbot, &t)) { + if (dst[3].fY > cbot && ChopMonoAtY(dst, cbot, &t)) { SkChopCubicAt(dst, tmp, t); dst[1] = tmp[1]; dst[2] = tmp[2]; diff --git a/gfx/skia/skia/src/core/SkCubicClipper.h b/gfx/skia/skia/src/core/SkCubicClipper.h index c52eabe460..d7dc381233 100644 --- a/gfx/skia/skia/src/core/SkCubicClipper.h +++ b/gfx/skia/skia/src/core/SkCubicClipper.h @@ -27,6 +27,7 @@ public: bool clipCubic(const SkPoint src[4], SkPoint dst[4]); + static bool ChopMonoAtY(const SkPoint pts[4], SkScalar y, SkScalar* t); private: SkRect fClip; }; diff --git a/gfx/skia/skia/src/core/SkDevice.cpp b/gfx/skia/skia/src/core/SkDevice.cpp index 34c171db90..8ff5827ada 100644 --- a/gfx/skia/skia/src/core/SkDevice.cpp +++ b/gfx/skia/skia/src/core/SkDevice.cpp @@ -55,7 +55,8 @@ const SkBitmap& SkBaseDevice::accessBitmap(bool changePixels) { SkPixelGeometry SkBaseDevice::CreateInfo::AdjustGeometry(const SkImageInfo& info, TileUsage tileUsage, - SkPixelGeometry geo) { + SkPixelGeometry geo, + bool preserveLCDText) { switch (tileUsage) { case kPossible_TileUsage: // (we think) for compatibility with old clients, we assume this layer can support LCD @@ -63,7 +64,7 @@ SkPixelGeometry SkBaseDevice::CreateInfo::AdjustGeometry(const SkImageInfo& info // our callers (reed/robertphilips). break; case kNever_TileUsage: - if (info.alphaType() != kOpaque_SkAlphaType) { + if (!preserveLCDText) { geo = kUnknown_SkPixelGeometry; } break; @@ -402,6 +403,32 @@ void SkBaseDevice::drawTextOnPath(const SkDraw& draw, const void* text, size_t b ////////////////////////////////////////////////////////////////////////////////////////// +void SkBaseDevice::drawBitmapAsSprite(const SkDraw& draw, const SkBitmap& bitmap, int x, int y, + const SkPaint& paint) { + SkImageFilter* filter = paint.getImageFilter(); + if (filter && !this->canHandleImageFilter(filter)) { + SkImageFilter::DeviceProxy proxy(this); + SkBitmap dst; + SkIPoint offset = SkIPoint::Make(0, 0); + SkMatrix matrix = *draw.fMatrix; + matrix.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y)); +#ifdef SK_SUPPORT_SRC_BOUNDS_BLOAT_FOR_IMAGEFILTERS + const SkIRect clipBounds = bitmap.bounds(); +#else + const SkIRect clipBounds = draw.fClip->getBounds().makeOffset(-x, -y); +#endif + SkAutoTUnref cache(this->getImageFilterCache()); + SkImageFilter::Context ctx(matrix, clipBounds, cache.get()); + if (filter->filterImage(&proxy, bitmap, ctx, &dst, &offset)) { + SkPaint tmpUnfiltered(paint); + tmpUnfiltered.setImageFilter(nullptr); + this->drawSprite(draw, dst, x + offset.x(), y + offset.y(), tmpUnfiltered); + } + } else { + this->drawSprite(draw, bitmap, x, y, paint); + } +} + uint32_t SkBaseDevice::filterTextFlags(const SkPaint& paint) const { uint32_t flags = paint.getFlags(); diff --git a/gfx/skia/skia/src/core/SkDistanceFieldGen.cpp b/gfx/skia/skia/src/core/SkDistanceFieldGen.cpp index 30354e09f8..147aefad79 100644 --- a/gfx/skia/skia/src/core/SkDistanceFieldGen.cpp +++ b/gfx/skia/skia/src/core/SkDistanceFieldGen.cpp @@ -343,15 +343,10 @@ static bool generate_distance_field_from_image(unsigned char* distanceField, int dataWidth = width + 2*pad; int dataHeight = height + 2*pad; - // create temp data - size_t dataSize = dataWidth*dataHeight*sizeof(DFData); - SkAutoSMalloc<1024> dfStorage(dataSize); - DFData* dataPtr = (DFData*) dfStorage.get(); - sk_bzero(dataPtr, dataSize); - - SkAutoSMalloc<1024> edgeStorage(dataWidth*dataHeight*sizeof(char)); - unsigned char* edgePtr = (unsigned char*) edgeStorage.get(); - sk_bzero(edgePtr, dataWidth*dataHeight*sizeof(char)); + // create zeroed temp DFData+edge storage + SkAutoFree storage(sk_calloc_throw(dataWidth*dataHeight*(sizeof(DFData) + 1))); + DFData* dataPtr = (DFData*)storage.get(); + unsigned char* edgePtr = (unsigned char*)storage.get() + dataWidth*dataHeight*sizeof(DFData); // copy glyph into distance field storage init_glyph_data(dataPtr, edgePtr, copyPtr, diff --git a/gfx/skia/skia/include/core/SkDither.h b/gfx/skia/skia/src/core/SkDither.h similarity index 100% rename from gfx/skia/skia/include/core/SkDither.h rename to gfx/skia/skia/src/core/SkDither.h diff --git a/gfx/skia/skia/src/core/SkDraw.cpp b/gfx/skia/skia/src/core/SkDraw.cpp index e1a49d39b4..246c31ba51 100644 --- a/gfx/skia/skia/src/core/SkDraw.cpp +++ b/gfx/skia/skia/src/core/SkDraw.cpp @@ -30,7 +30,6 @@ #include "SkTemplates.h" #include "SkTextMapStateProc.h" #include "SkTLazy.h" -#include "SkUtility.h" #include "SkUtils.h" #include "SkVertState.h" @@ -40,6 +39,9 @@ //#define TRACE_BITMAP_DRAWS +// Helper function to fix code gen bug on ARM64. +// See SkFindAndPlaceGlyph.h for more details. +void FixGCC49Arm64Bug(int v) { } /** Helper for allocating small blitters on the stack. */ @@ -905,10 +907,8 @@ void SkDraw::drawDevMask(const SkMask& srcM, const SkPaint& paint) const { SkMask dstM; if (paint.getMaskFilter() && - paint.getMaskFilter()->filterMask(&dstM, srcM, *fMatrix, nullptr)) { + paint.getMaskFilter()->filterMask(&dstM, srcM, *fMatrix, nullptr)) { mask = &dstM; - } else { - dstM.fImage = nullptr; } SkAutoMaskFreeImage ami(dstM.fImage); @@ -1132,28 +1132,51 @@ void SkDraw::drawPath(const SkPath& origSrcPath, const SkPaint& origPaint, } } else { // hairline if (paint->isAntiAlias()) { + switch (paint->getStrokeCap()) { + case SkPaint::kButt_Cap: + proc = SkScan::AntiHairPath; + break; + case SkPaint::kSquare_Cap: + proc = SkScan::AntiHairSquarePath; + break; + case SkPaint::kRound_Cap: + proc = SkScan::AntiHairRoundPath; + break; + default: + proc SK_INIT_TO_AVOID_WARNING; + SkDEBUGFAIL("unknown paint cap type"); + } +#ifdef SK_SUPPORT_LEGACY_HAIR_IGNORES_CAPS proc = SkScan::AntiHairPath; +#endif } else { + switch (paint->getStrokeCap()) { + case SkPaint::kButt_Cap: + proc = SkScan::HairPath; + break; + case SkPaint::kSquare_Cap: + proc = SkScan::HairSquarePath; + break; + case SkPaint::kRound_Cap: + proc = SkScan::HairRoundPath; + break; + default: + proc SK_INIT_TO_AVOID_WARNING; + SkDEBUGFAIL("unknown paint cap type"); + } +#ifdef SK_SUPPORT_LEGACY_HAIR_IGNORES_CAPS proc = SkScan::HairPath; +#endif } } proc(*devPathPtr, *fRC, blitter); } -/** For the purposes of drawing bitmaps, if a matrix is "almost" translate - go ahead and treat it as if it were, so that subsequent code can go fast. - */ -static bool just_translate(const SkMatrix& matrix, const SkBitmap& bitmap) { - unsigned bits = 0; // TODO: find a way to allow the caller to tell us to - // respect filtering. - return SkTreatAsSprite(matrix, bitmap.width(), bitmap.height(), bits); -} - void SkDraw::drawBitmapAsMask(const SkBitmap& bitmap, const SkPaint& paint) const { SkASSERT(bitmap.colorType() == kAlpha_8_SkColorType); - if (just_translate(*fMatrix, bitmap)) { + if (SkTreatAsSprite(*fMatrix, bitmap.dimensions(), paint)) { int ix = SkScalarRoundToInt(fMatrix->getTranslateX()); int iy = SkScalarRoundToInt(fMatrix->getTranslateY()); @@ -1199,8 +1222,8 @@ void SkDraw::drawBitmapAsMask(const SkBitmap& bitmap, } // allocate (and clear) our temp buffer to hold the transformed bitmap - SkAutoMalloc storage(size); - mask.fImage = (uint8_t*)storage.get(); + SkAutoTMalloc storage(size); + mask.fImage = storage.get(); memset(mask.fImage, 0, size); // now draw our bitmap(src) into mask(dst), transformed by the matrix @@ -1268,7 +1291,8 @@ void SkDraw::drawBitmap(const SkBitmap& bitmap, const SkMatrix& prematrix, return; } - if (bitmap.colorType() != kAlpha_8_SkColorType && just_translate(matrix, bitmap)) { + if (bitmap.colorType() != kAlpha_8_SkColorType + && SkTreatAsSprite(matrix, bitmap.dimensions(), paint)) { // // It is safe to call lock pixels now, since we know the matrix is // (more or less) identity. @@ -1423,170 +1447,120 @@ void SkDraw::drawText_asPaths(const char text[], size_t byteLength, #endif //////////////////////////////////////////////////////////////////////////////////////////////////// -struct SkDraw1Glyph { - const SkDraw* fDraw; - const SkRegion* fClip; - const SkAAClip* fAAClip; - SkBlitter* fBlitter; - SkGlyphCache* fCache; - const SkPaint* fPaint; - SkIRect fClipBounds; - /** Half the sampling frequency of the rasterized glyph in x. */ - SkScalar fHalfSampleX; - /** Half the sampling frequency of the rasterized glyph in y. */ - SkScalar fHalfSampleY; - /** Draws one glyph. - * - * The x and y are pre-biased, so implementations may just truncate them. - * i.e. half the sampling frequency has been added. - * e.g. 1/2 or 1/(2^(SkGlyph::kSubBits+1)) has already been added. - * This added bias can be found in fHalfSampleX,Y. - */ - typedef void (*Proc)(const SkDraw1Glyph&, Sk48Dot16 x, Sk48Dot16 y, const SkGlyph&); +class DrawOneGlyph { +public: + DrawOneGlyph(const SkDraw& draw, const SkPaint& paint, SkGlyphCache* cache, SkBlitter* blitter) + : fUseRegionToDraw(UsingRegionToDraw(draw.fRC)) + , fGlyphCache(cache) + , fBlitter(blitter) + , fClip(fUseRegionToDraw ? &draw.fRC->bwRgn() : nullptr) + , fDraw(draw) + , fPaint(paint) + , fClipBounds(PickClipBounds(draw)) { } - Proc init(const SkDraw* draw, SkBlitter* blitter, SkGlyphCache* cache, - const SkPaint&); + void operator()(const SkGlyph& glyph, SkPoint position, SkPoint rounding) { + position += rounding; + Sk48Dot16 fx = SkScalarTo48Dot16(position.fX); + Sk48Dot16 fy = SkScalarTo48Dot16(position.fY); + // Prevent glyphs from being drawn outside of or straddling the edge of device space. + if ((fx >> 16) > INT_MAX - (INT16_MAX + UINT16_MAX) || + (fx >> 16) < INT_MIN - (INT16_MIN + 0 /*UINT16_MIN*/) || + (fy >> 16) > INT_MAX - (INT16_MAX + UINT16_MAX) || + (fy >> 16) < INT_MIN - (INT16_MIN + 0 /*UINT16_MIN*/)) { + return; + } + + int left = Sk48Dot16FloorToInt(fx); + int top = Sk48Dot16FloorToInt(fy); + SkASSERT(glyph.fWidth > 0 && glyph.fHeight > 0); + + left += glyph.fLeft; + top += glyph.fTop; + + int right = left + glyph.fWidth; + int bottom = top + glyph.fHeight; + + SkMask mask; + mask.fBounds.set(left, top, right, bottom); + + if (fUseRegionToDraw) { + SkRegion::Cliperator clipper(*fClip, mask.fBounds); + + if (!clipper.done() && this->getImageData(glyph, &mask)) { + const SkIRect& cr = clipper.rect(); + do { + this->blitMask(mask, cr); + clipper.next(); + } while (!clipper.done()); + } + } else { + SkIRect storage; + SkIRect* bounds = &mask.fBounds; + + // this extra test is worth it, assuming that most of the time it succeeds + // since we can avoid writing to storage + if (!fClipBounds.containsNoEmptyCheck(mask.fBounds)) { + if (!storage.intersectNoEmptyCheck(mask.fBounds, fClipBounds)) + return; + bounds = &storage; + } + + if (this->getImageData(glyph, &mask)) { + this->blitMask(mask, *bounds); + } + } + } + +private: + static bool UsingRegionToDraw(const SkRasterClip* rClip) { + return rClip->isBW() && !rClip->isRect(); + } + + static SkIRect PickClipBounds(const SkDraw& draw) { + const SkRasterClip& rasterClip = *draw.fRC; + + if (rasterClip.isBW()) { + return rasterClip.bwRgn().getBounds(); + } else { + return rasterClip.aaRgn().getBounds(); + } + } + + bool getImageData(const SkGlyph& glyph, SkMask* mask) { + uint8_t* bits = (uint8_t*)(fGlyphCache->findImage(glyph)); + if (nullptr == bits) { + return false; // can't rasterize glyph + } + mask->fImage = bits; + mask->fRowBytes = glyph.rowBytes(); + mask->fFormat = static_cast(glyph.fMaskFormat); + return true; + } - // call this instead of fBlitter->blitMask() since this wrapper will handle - // the case when the mask is ARGB32_Format - // void blitMask(const SkMask& mask, const SkIRect& clip) const { if (SkMask::kARGB32_Format == mask.fFormat) { - this->blitMaskAsSprite(mask); + SkBitmap bm; + bm.installPixels( + SkImageInfo::MakeN32Premul(mask.fBounds.width(), mask.fBounds.height()), + (SkPMColor*)mask.fImage, mask.fRowBytes); + + fDraw.drawSprite(bm, mask.fBounds.x(), mask.fBounds.y(), fPaint); } else { fBlitter->blitMask(mask, clip); } } - // mask must be kARGB32_Format - void blitMaskAsSprite(const SkMask& mask) const; + const bool fUseRegionToDraw; + SkGlyphCache * const fGlyphCache; + SkBlitter * const fBlitter; + const SkRegion* const fClip; + const SkDraw& fDraw; + const SkPaint& fPaint; + const SkIRect fClipBounds; }; -static void D1G_RectClip(const SkDraw1Glyph& state, Sk48Dot16 fx, Sk48Dot16 fy, - const SkGlyph& glyph) { - // Prevent glyphs from being drawn outside of or straddling the edge of device space. - if ((fx >> 16) > INT_MAX - (INT16_MAX + UINT16_MAX) || - (fx >> 16) < INT_MIN - (INT16_MIN + 0 /*UINT16_MIN*/) || - (fy >> 16) > INT_MAX - (INT16_MAX + UINT16_MAX) || - (fy >> 16) < INT_MIN - (INT16_MIN + 0 /*UINT16_MIN*/)) - { - return; - } - - int left = Sk48Dot16FloorToInt(fx); - int top = Sk48Dot16FloorToInt(fy); - SkASSERT(glyph.fWidth > 0 && glyph.fHeight > 0); - SkASSERT((nullptr == state.fClip && state.fAAClip) || - (state.fClip && nullptr == state.fAAClip && state.fClip->isRect())); - - left += glyph.fLeft; - top += glyph.fTop; - - int right = left + glyph.fWidth; - int bottom = top + glyph.fHeight; - - SkMask mask; - SkIRect storage; - SkIRect* bounds = &mask.fBounds; - - mask.fBounds.set(left, top, right, bottom); - - // this extra test is worth it, assuming that most of the time it succeeds - // since we can avoid writing to storage - if (!state.fClipBounds.containsNoEmptyCheck(left, top, right, bottom)) { - if (!storage.intersectNoEmptyCheck(mask.fBounds, state.fClipBounds)) - return; - bounds = &storage; - } - - uint8_t* aa = (uint8_t*)glyph.fImage; - if (nullptr == aa) { - aa = (uint8_t*)state.fCache->findImage(glyph); - if (nullptr == aa) { - return; // can't rasterize glyph - } - } - - mask.fRowBytes = glyph.rowBytes(); - mask.fFormat = static_cast(glyph.fMaskFormat); - mask.fImage = aa; - state.blitMask(mask, *bounds); -} - -static void D1G_RgnClip(const SkDraw1Glyph& state, Sk48Dot16 fx, Sk48Dot16 fy, - const SkGlyph& glyph) { - int left = Sk48Dot16FloorToInt(fx); - int top = Sk48Dot16FloorToInt(fy); - SkASSERT(glyph.fWidth > 0 && glyph.fHeight > 0); - SkASSERT(!state.fClip->isRect()); - - SkMask mask; - - left += glyph.fLeft; - top += glyph.fTop; - - mask.fBounds.set(left, top, left + glyph.fWidth, top + glyph.fHeight); - SkRegion::Cliperator clipper(*state.fClip, mask.fBounds); - - if (!clipper.done()) { - const SkIRect& cr = clipper.rect(); - const uint8_t* aa = (uint8_t*)state.fCache->findImage(glyph); - if (nullptr == aa) { - return; - } - - mask.fRowBytes = glyph.rowBytes(); - mask.fFormat = static_cast(glyph.fMaskFormat); - mask.fImage = (uint8_t*)aa; - do { - state.blitMask(mask, cr); - clipper.next(); - } while (!clipper.done()); - } -} - -SkDraw1Glyph::Proc SkDraw1Glyph::init(const SkDraw* draw, SkBlitter* blitter, SkGlyphCache* cache, - const SkPaint& pnt) { - fDraw = draw; - fBlitter = blitter; - fCache = cache; - fPaint = &pnt; - - if (cache->isSubpixel()) { - fHalfSampleX = fHalfSampleY = SkFixedToScalar(SkGlyph::kSubpixelRound); - } else { - fHalfSampleX = fHalfSampleY = SK_ScalarHalf; - } - - if (draw->fRC->isBW()) { - fAAClip = nullptr; - fClip = &draw->fRC->bwRgn(); - fClipBounds = fClip->getBounds(); - if (fClip->isRect()) { - return D1G_RectClip; - } else { - return D1G_RgnClip; - } - } else { // aaclip - fAAClip = &draw->fRC->aaRgn(); - fClip = nullptr; - fClipBounds = fAAClip->getBounds(); - return D1G_RectClip; - } -} - -void SkDraw1Glyph::blitMaskAsSprite(const SkMask& mask) const { - SkASSERT(SkMask::kARGB32_Format == mask.fFormat); - - SkBitmap bm; - bm.installPixels(SkImageInfo::MakeN32Premul(mask.fBounds.width(), mask.fBounds.height()), - (SkPMColor*)mask.fImage, mask.fRowBytes); - - fDraw->drawSprite(bm, mask.fBounds.x(), mask.fBounds.y(), *fPaint); -} - -/////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////// void SkDraw::drawText(const char text[], size_t byteLength, SkScalar x, SkScalar y, const SkPaint& paint) const { @@ -1606,25 +1580,17 @@ void SkDraw::drawText(const char text[], size_t byteLength, return; } - SkAutoGlyphCache autoCache(paint, &fDevice->surfaceProps(), fMatrix); - SkGlyphCache* cache = autoCache.getCache(); - + SkAutoGlyphCache autoCache(paint, &fDevice->surfaceProps(), fMatrix); + SkGlyphCache* cache = autoCache.getCache(); // The Blitter Choose needs to be live while using the blitter below. SkAutoBlitterChoose blitterChooser(fDst, *fMatrix, paint); SkAAClipBlitterWrapper wrapper(*fRC, blitterChooser.get()); - - SkDraw1Glyph d1g; - SkDraw1Glyph::Proc proc = d1g.init(this, wrapper.getBlitter(), cache, paint); + DrawOneGlyph drawOneGlyph(*this, paint, cache, wrapper.getBlitter()); SkFindAndPlaceGlyph::ProcessText( paint.getTextEncoding(), text, byteLength, - {x, y}, *fMatrix, paint.getTextAlign(), cache, - [&](const SkGlyph& glyph, SkPoint position, SkPoint rounding) { - position += rounding; - proc(d1g, SkScalarTo48Dot16(position.fX), SkScalarTo48Dot16(position.fY), glyph); - } - ); + {x, y}, *fMatrix, paint.getTextAlign(), cache, drawOneGlyph); } ////////////////////////////////////////////////////////////////////////////// @@ -1696,24 +1662,18 @@ void SkDraw::drawPosText(const char text[], size_t byteLength, return; } + SkAutoGlyphCache autoCache(paint, &fDevice->surfaceProps(), fMatrix); + SkGlyphCache* cache = autoCache.getCache(); + // The Blitter Choose needs to be live while using the blitter below. SkAutoBlitterChoose blitterChooser(fDst, *fMatrix, paint); SkAAClipBlitterWrapper wrapper(*fRC, blitterChooser.get()); - - SkAutoGlyphCache autoCache(paint, &fDevice->surfaceProps(), fMatrix); - SkGlyphCache* cache = autoCache.getCache(); - SkDraw1Glyph d1g; - SkDraw1Glyph::Proc proc = d1g.init(this, wrapper.getBlitter(), cache, paint); - SkPaint::Align textAlignment = paint.getTextAlign(); + DrawOneGlyph drawOneGlyph(*this, paint, cache, wrapper.getBlitter()); + SkPaint::Align textAlignment = paint.getTextAlign(); SkFindAndPlaceGlyph::ProcessPosText( paint.getTextEncoding(), text, byteLength, - offset, *fMatrix, pos, scalarsPerPosition, textAlignment, cache, - [&](const SkGlyph& glyph, SkPoint position, SkPoint rounding) { - position += rounding; - proc(d1g, SkScalarTo48Dot16(position.fX), SkScalarTo48Dot16(position.fY), glyph); - } - ); + offset, *fMatrix, pos, scalarsPerPosition, textAlignment, cache, drawOneGlyph); } #if defined _WIN32 && _MSC_VER >= 1300 @@ -2042,7 +2002,6 @@ static bool compute_bounds(const SkPath& devPath, const SkIRect* clipBounds, srcM.fBounds = *bounds; srcM.fFormat = SkMask::kA8_Format; - srcM.fImage = nullptr; if (!filter->filterMask(&dstM, srcM, *filterMatrix, &margin)) { return false; } diff --git a/gfx/skia/skia/src/core/SkEdge.cpp b/gfx/skia/skia/src/core/SkEdge.cpp index f91f5f8782..c64896f2e0 100644 --- a/gfx/skia/skia/src/core/SkEdge.cpp +++ b/gfx/skia/skia/src/core/SkEdge.cpp @@ -26,7 +26,7 @@ static inline SkFixed SkFDot6ToFixedDiv2(SkFDot6 value) { // we want to return SkFDot6ToFixed(value >> 1), but we don't want to throw // away data in value, so just perform a modify up-shift - return value << (16 - 6 - 1); + return SkLeftShift(value, 16 - 6 - 1); } ///////////////////////////////////////////////////////////////////////// @@ -214,8 +214,8 @@ int SkQuadraticEdge::setQuadratic(const SkPoint pts[3], int shift) // compute number of steps needed (1 << shift) { - SkFDot6 dx = ((x1 << 1) - x0 - x2) >> 2; - SkFDot6 dy = ((y1 << 1) - y0 - y2) >> 2; + SkFDot6 dx = (SkLeftShift(x1, 1) - x0 - x2) >> 2; + SkFDot6 dy = (SkLeftShift(y1, 1) - y0 - y2) >> 2; shift = diff_to_shift(dx, dy); SkASSERT(shift >= 0); } @@ -312,8 +312,8 @@ int SkQuadraticEdge::updateQuadratic() ///////////////////////////////////////////////////////////////////////// static inline int SkFDot6UpShift(SkFDot6 x, int upShift) { - SkASSERT((x << upShift >> upShift) == x); - return x << upShift; + SkASSERT((SkLeftShift(x, upShift) >> upShift) == x); + return SkLeftShift(x, upShift); } /* f(1/3) = (8a + 12b + 6c + d) / 27 @@ -403,7 +403,7 @@ int SkCubicEdge::setCubic(const SkPoint pts[4], int shift) { } fWinding = SkToS8(winding); - fCurveCount = SkToS8(-1 << shift); + fCurveCount = SkToS8(SkLeftShift(-1, shift)); fCurveShift = SkToU8(shift); fCubicDShift = SkToU8(downShift); diff --git a/gfx/skia/skia/src/core/SkEdge.h b/gfx/skia/skia/src/core/SkEdge.h index db6f43085d..c3adbf85cb 100644 --- a/gfx/skia/skia/src/core/SkEdge.h +++ b/gfx/skia/skia/src/core/SkEdge.h @@ -15,7 +15,7 @@ #include "SkMath.h" // This correctly favors the lower-pixel when y0 is on a 1/2 pixel boundary -#define SkEdge_Compute_DY(top, y0) ((top << 6) + 32 - (y0)) +#define SkEdge_Compute_DY(top, y0) (SkLeftShift(top, 6) + 32 - (y0)) struct SkEdge { enum Type { diff --git a/gfx/skia/skia/src/core/SkEdgeClipper.cpp b/gfx/skia/skia/src/core/SkEdgeClipper.cpp index c6a4fb2971..55f9192aea 100644 --- a/gfx/skia/skia/src/core/SkEdgeClipper.cpp +++ b/gfx/skia/skia/src/core/SkEdgeClipper.cpp @@ -358,14 +358,19 @@ void SkEdgeClipper::clipMonoCubic(const SkPoint src[4], const SkRect& clip) { } } +static bool quick_reject_in_y(const SkPoint pts[4], const SkRect& clip) { + Sk4s ys(pts[0].fY, pts[1].fY, pts[2].fY, pts[3].fY); + Sk4s t(clip.top()); + Sk4s b(clip.bottom()); + + return (ys < t).allTrue() || (ys > b).allTrue(); +} + bool SkEdgeClipper::clipCubic(const SkPoint srcPts[4], const SkRect& clip) { fCurrPoint = fPoints; fCurrVerb = fVerbs; - SkRect bounds; - bounds.set(srcPts, 4); - - if (!quick_reject(bounds, clip)) { + if (!quick_reject_in_y(srcPts, clip)) { SkPoint monoY[10]; int countY = SkChopCubicAtYExtrema(srcPts, monoY); for (int y = 0; y <= countY; y++) { diff --git a/gfx/skia/skia/src/core/SkFDot6.h b/gfx/skia/skia/src/core/SkFDot6.h index 3da753da41..b536729829 100644 --- a/gfx/skia/skia/src/core/SkFDot6.h +++ b/gfx/skia/skia/src/core/SkFDot6.h @@ -56,9 +56,9 @@ inline SkFDot6 SkScalarRoundToFDot6(SkScalar x, int shift = 0) #define SkFixedToFDot6(x) ((x) >> 10) inline SkFixed SkFDot6ToFixed(SkFDot6 x) { - SkASSERT((x << 10 >> 10) == x); + SkASSERT((SkLeftShift(x, 10) >> 10) == x); - return x << 10; + return SkLeftShift(x, 10); } #define SkScalarToFDot6(x) (SkFDot6)((x) * 64) @@ -68,7 +68,7 @@ inline SkFixed SkFDot6Div(SkFDot6 a, SkFDot6 b) { SkASSERT(b != 0); if (a == (int16_t)a) { - return (a << 16) / b; + return SkLeftShift(a, 16) / b; } else { return SkFixedDiv(a, b); } diff --git a/gfx/skia/skia/src/core/SkFilterShader.cpp b/gfx/skia/skia/src/core/SkFilterShader.cpp deleted file mode 100644 index b52c2be8c4..0000000000 --- a/gfx/skia/skia/src/core/SkFilterShader.cpp +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright 2013 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "SkFilterShader.h" - -#include "SkColorFilter.h" -#include "SkReadBuffer.h" -#include "SkWriteBuffer.h" -#include "SkShader.h" -#include "SkString.h" - -SkFilterShader::SkFilterShader(SkShader* shader, SkColorFilter* filter) { - fShader = shader; - shader->ref(); - - fFilter = filter; - filter->ref(); -} - -SkFilterShader::~SkFilterShader() { - fFilter->unref(); - fShader->unref(); -} - -SkFlattenable* SkFilterShader::CreateProc(SkReadBuffer& buffer) { - SkAutoTUnref shader(buffer.readShader()); - SkAutoTUnref filter(buffer.readColorFilter()); - if (!shader.get() || !filter.get()) { - return nullptr; - } - return new SkFilterShader(shader, filter); -} - -void SkFilterShader::flatten(SkWriteBuffer& buffer) const { - buffer.writeFlattenable(fShader); - buffer.writeFlattenable(fFilter); -} - -uint32_t SkFilterShader::FilterShaderContext::getFlags() const { - const SkFilterShader& filterShader = static_cast(fShader); - - uint32_t shaderF = fShaderContext->getFlags(); - uint32_t filterF = filterShader.fFilter->getFlags(); - - // filters don't support 16bit, so clear the matching bit in the shader - shaderF &= ~SkShader::kHasSpan16_Flag; - - // if the filter might change alpha, clear the opaque flag in the shader - if (!(filterF & SkColorFilter::kAlphaUnchanged_Flag)) { - shaderF &= ~SkShader::kOpaqueAlpha_Flag; - } - return shaderF; -} - -SkShader::Context* SkFilterShader::onCreateContext(const ContextRec& rec, void* storage) const { - char* shaderContextStorage = (char*)storage + sizeof(FilterShaderContext); - SkShader::Context* shaderContext = fShader->createContext(rec, shaderContextStorage); - if (nullptr == shaderContext) { - return nullptr; - } - return new (storage) FilterShaderContext(*this, shaderContext, rec); -} - -size_t SkFilterShader::contextSize() const { - return sizeof(FilterShaderContext) + fShader->contextSize(); -} - -SkFilterShader::FilterShaderContext::FilterShaderContext(const SkFilterShader& filterShader, - SkShader::Context* shaderContext, - const ContextRec& rec) - : INHERITED(filterShader, rec) - , fShaderContext(shaderContext) {} - -SkFilterShader::FilterShaderContext::~FilterShaderContext() { - fShaderContext->~Context(); -} - -void SkFilterShader::FilterShaderContext::shadeSpan(int x, int y, SkPMColor result[], int count) { - const SkFilterShader& filterShader = static_cast(fShader); - - fShaderContext->shadeSpan(x, y, result, count); - filterShader.fFilter->filterSpan(result, count, result); -} - -#ifndef SK_IGNORE_TO_STRING -void SkFilterShader::toString(SkString* str) const { - str->append("SkFilterShader: ("); - - str->append("Shader: "); - fShader->toString(str); - str->append(" Filter: "); - // TODO: add "fFilter->toString(str);" once SkColorFilter::toString is added - - this->INHERITED::toString(str); - - str->append(")"); -} -#endif diff --git a/gfx/skia/skia/src/core/SkFindAndPlaceGlyph.h b/gfx/skia/skia/src/core/SkFindAndPlaceGlyph.h index 278be07e0b..c74a9820e4 100644 --- a/gfx/skia/skia/src/core/SkFindAndPlaceGlyph.h +++ b/gfx/skia/skia/src/core/SkFindAndPlaceGlyph.h @@ -14,6 +14,7 @@ #include "SkPaint.h" #include "SkTemplates.h" #include "SkUtils.h" +#include // Calculate a type with the same size as the max of all the Ts. // This must be top level because the is no specialization of inner classes. @@ -30,6 +31,16 @@ struct SkMaxSizeOf { sizeof(H) >= SkMaxSizeOf::value ? sizeof(H) : SkMaxSizeOf::value; }; + +// This is a temporary helper function to work around a bug in the code generation +// for aarch64 (arm) on GCC 4.9. This bug does not show up on other platforms, so it +// seems to be an aarch64 backend problem. +// +// GCC 4.9 on ARM64 does not generate the proper constructor code for PositionReader or +// GlyphFindAndPlace. The vtable is not set properly without adding the fixme code. +// The implementation is in SkDraw.cpp. +extern void FixGCC49Arm64Bug(int v); + class SkFindAndPlaceGlyph { public: template @@ -81,7 +92,7 @@ private: #define alignof __alignof #endif SkASSERT(alignof(Variant) <= alignof(Space)); - new(&fSpace) Variant(skstd::forward(args)...); + new(&fSpace) Variant(std::forward(args)...); } private: @@ -229,6 +240,9 @@ private: public: virtual ~PositionReaderInterface() { } virtual SkPoint nextPoint() = 0; + // This is only here to fix a GCC 4.9 aarch64 code gen bug. + // See comment at the top of the file. + virtual int forceUseForBug() = 0; }; class HorizontalPositions final : public PositionReaderInterface { @@ -241,6 +255,8 @@ private: return {x, 0}; } + int forceUseForBug() override { return 1; } + private: const SkScalar* fPositions; }; @@ -256,6 +272,8 @@ private: return to_return; } + int forceUseForBug() override { return 2; } + private: const SkScalar* fPositions; }; @@ -410,7 +428,9 @@ private: class GlyphFindAndPlaceSubpixel final : public GlyphFindAndPlaceInterface { public: GlyphFindAndPlaceSubpixel(LookupGlyph& glyphFinder) - : fGlyphFinder(glyphFinder) { } + : fGlyphFinder(glyphFinder) { + FixGCC49Arm64Bug(1); + } SkPoint findAndPositionGlyph( const char** text, SkPoint position, ProcessOneGlyph&& processOneGlyph) override { @@ -464,6 +484,7 @@ private: public: GlyphFindAndPlaceFullPixel(LookupGlyph& glyphFinder) : fGlyphFinder(glyphFinder) { + FixGCC49Arm64Bug(2); // Kerning can only be used with SkPaint::kLeft_Align static_assert(!kUseKerning || SkPaint::kLeft_Align == kTextAlignment, "Kerning can only be used with left aligned text."); @@ -589,7 +610,7 @@ inline void SkFindAndPlaceGlyph::ProcessPosText( SkPoint mappedPoint = mapper.TranslationMapper::map( positions.HorizontalPositions::nextPoint()); positioner.Positioner::findAndPositionGlyph( - &cursor, mappedPoint, skstd::forward(processOneGlyph)); + &cursor, mappedPoint, std::forward(processOneGlyph)); } return; } @@ -601,6 +622,7 @@ inline void SkFindAndPlaceGlyph::ProcessPosText( } else { to_init->initialize(pos); } + positionReader->forceUseForBug(); } }; @@ -617,7 +639,7 @@ inline void SkFindAndPlaceGlyph::ProcessPosText( } }; - GlyphFindAndPlace findAndPosition{ + GlyphFindAndPlace findAndPosition { [&](typename GlyphFindAndPlace::Variants* to_init) { if (cache->isSubpixel()) { switch (textAlignment) { @@ -660,7 +682,7 @@ inline void SkFindAndPlaceGlyph::ProcessPosText( while (text < stop) { SkPoint mappedPoint = mapper->map(positionReader->nextPoint()); findAndPosition->findAndPositionGlyph( - &text, mappedPoint, skstd::forward(processOneGlyph)); + &text, mappedPoint, std::forward(processOneGlyph)); } } @@ -704,7 +726,7 @@ inline void SkFindAndPlaceGlyph::ProcessText( while (text < stop) { current = findAndPosition->findAndPositionGlyph( - &text, current, skstd::forward(processOneGlyph)); + &text, current, std::forward(processOneGlyph)); } } diff --git a/gfx/skia/skia/src/core/SkFloatBits.cpp b/gfx/skia/skia/src/core/SkFloatBits.cpp index 919fd0610f..ea705513ac 100644 --- a/gfx/skia/skia/src/core/SkFloatBits.cpp +++ b/gfx/skia/skia/src/core/SkFloatBits.cpp @@ -65,7 +65,7 @@ int32_t SkFloatBits_toIntCast(int32_t packed) { // same as (int)floor(float) int32_t SkFloatBits_toIntFloor(int32_t packed) { // curse you negative 0 - if ((packed << 1) == 0) { + if (SkLeftShift(packed, 1) == 0) { return 0; } @@ -104,7 +104,7 @@ int32_t SkFloatBits_toIntFloor(int32_t packed) { // same as (int)floor(float + 0.5) int32_t SkFloatBits_toIntRound(int32_t packed) { // curse you negative 0 - if ((packed << 1) == 0) { + if (SkLeftShift(packed, 1) == 0) { return 0; } @@ -134,7 +134,7 @@ int32_t SkFloatBits_toIntRound(int32_t packed) { // same as (int)ceil(float) int32_t SkFloatBits_toIntCeil(int32_t packed) { // curse you negative 0 - if ((packed << 1) == 0) { + if (SkLeftShift(packed, 1) == 0) { return 0; } @@ -200,6 +200,6 @@ float SkIntToFloatCast(int32_t value) { SkASSERT(shift >= 0 && shift <= 255); SkFloatIntUnion data; - data.fSignBitInt = (sign << 31) | (shift << 23) | (value & ~MATISSA_MAGIC_BIG); + data.fSignBitInt = SkLeftShift(sign, 31) | SkLeftShift(shift, 23) | (value & ~MATISSA_MAGIC_BIG); return data.fFloat; } diff --git a/gfx/skia/skia/src/core/SkGeometry.cpp b/gfx/skia/skia/src/core/SkGeometry.cpp index 01b618073a..c25e18641a 100644 --- a/gfx/skia/skia/src/core/SkGeometry.cpp +++ b/gfx/skia/skia/src/core/SkGeometry.cpp @@ -90,7 +90,13 @@ int SkFindUnitQuadRoots(SkScalar A, SkScalar B, SkScalar C, SkScalar roots[2]) { SkScalar* r = roots; SkScalar R = B*B - 4*A*C; - if (R < 0 || SkScalarIsNaN(R)) { // complex roots + if (R < 0 || !SkScalarIsFinite(R)) { // complex roots + // if R is infinite, it's possible that it may still produce + // useful results if the operation was repeated in doubles + // the flipside is determining if the more precise answer + // isn't useful because surrounding machinery (e.g., subtracting + // the axis offset from C) already discards the extra precision + // more investigation and unit tests required... return 0; } R = SkScalarSqrt(R); @@ -1146,24 +1152,6 @@ int SkBuildQuadArc(const SkVector& uStart, const SkVector& uStop, // {t^2 (2 - 2 w), t (-2 + 2 w), 1} // -static SkScalar conic_eval_pos(const SkScalar src[], SkScalar w, SkScalar t) { - SkASSERT(src); - SkASSERT(t >= 0 && t <= SK_Scalar1); - - SkScalar src2w = SkScalarMul(src[2], w); - SkScalar C = src[0]; - SkScalar A = src[4] - 2 * src2w + C; - SkScalar B = 2 * (src2w - C); - SkScalar numer = SkScalarMulAdd(SkScalarMulAdd(A, t, B), t, C); - - B = 2 * (w - SK_Scalar1); - C = SK_Scalar1; - A = -B; - SkScalar denom = SkScalarMulAdd(SkScalarMulAdd(A, t, B), t, C); - - return numer / denom; -} - // F' = 2 (C t (1 + t (-1 + w)) - A (-1 + t) (t (-1 + w) - w) + B (1 - 2 t) w) // // t^2 : (2 P0 - 2 P2 - 2 P0 w + 2 P2 w) @@ -1230,18 +1218,6 @@ static void ratquad_mapTo3D(const SkPoint src[3], SkScalar w, SkP3D dst[]) { dst[2].set(src[2].fX * 1, src[2].fY * 1, 1); } -void SkConic::evalAt(SkScalar t, SkPoint* pt, SkVector* tangent) const { - SkASSERT(t >= 0 && t <= SK_Scalar1); - - if (pt) { - pt->set(conic_eval_pos(&fPts[0].fX, fW, t), - conic_eval_pos(&fPts[0].fY, fW, t)); - } - if (tangent) { - *tangent = evalTangentAt(t); - } -} - void SkConic::chopAt(SkScalar t, SkConic dst[2]) const { SkP3D tmp[3], tmp2[3]; @@ -1317,6 +1293,17 @@ SkVector SkConic::evalTangentAt(SkScalar t) const { return to_vector(quad_poly_eval(A, B, C, Sk2s(t))); } +void SkConic::evalAt(SkScalar t, SkPoint* pt, SkVector* tangent) const { + SkASSERT(t >= 0 && t <= SK_Scalar1); + + if (pt) { + *pt = this->evalAt(t); + } + if (tangent) { + *tangent = this->evalTangentAt(t); + } +} + static SkScalar subdivide_w_value(SkScalar w) { return SkScalarSqrt(SK_ScalarHalf + w * SK_ScalarHalf); } diff --git a/gfx/skia/skia/src/core/SkGlobalInitialization_core.cpp b/gfx/skia/skia/src/core/SkGlobalInitialization_core.cpp new file mode 100644 index 0000000000..ac37073eb3 --- /dev/null +++ b/gfx/skia/skia/src/core/SkGlobalInitialization_core.cpp @@ -0,0 +1,59 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkBitmapProcShader.h" +#include "SkColorFilter.h" +#include "SkColorFilterShader.h" +#include "SkColorShader.h" +#include "SkComposeShader.h" +#include "SkEmptyShader.h" +#include "SkFlattenable.h" +#include "SkImageShader.h" +#include "SkLocalMatrixShader.h" +#include "SkOnce.h" +#include "SkPathEffect.h" +#include "SkPictureShader.h" +#include "SkMatrixImageFilter.h" +#include "SkXfermode.h" + +/* + * Registers all of the required effects subclasses for picture deserialization. + * + * Optional subclasses (e.g. Blur) should be registered in the ports/ version of this file, + * inside the InitEffects() method. + */ +void SkFlattenable::PrivateInitializer::InitCore() { + // Shader + SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkBitmapProcShader) + SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkColorFilterShader) + SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkColorShader) + SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkComposeShader) + SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkEmptyShader) + SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkImageShader) + SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkLocalMatrixShader) + SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkPictureShader) + + // PathEffect + SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkComposePathEffect) + + // ImageFilter + SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkMatrixImageFilter) + + // ColorFilter + SkColorFilter::InitializeFlattenables(); + + // Xfermode + SkXfermode::InitializeFlattenables(); + + // Now initialize any optional/additional effects (implemented in src/ports) + InitEffects(); +}; + +SK_DECLARE_STATIC_ONCE(once); +void SkFlattenable::InitializeFlattenablesIfNeeded() { + SkOnce(&once, SkFlattenable::PrivateInitializer::InitCore); +} diff --git a/gfx/skia/skia/src/core/SkImageCacherator.cpp b/gfx/skia/skia/src/core/SkImageCacherator.cpp index f4a2ab734a..572778e19c 100644 --- a/gfx/skia/skia/src/core/SkImageCacherator.cpp +++ b/gfx/skia/skia/src/core/SkImageCacherator.cpp @@ -17,6 +17,7 @@ #if SK_SUPPORT_GPU #include "GrContext.h" #include "GrGpuResourcePriv.h" +#include "GrImageIDTextureAdjuster.h" #include "GrResourceKey.h" #include "GrTextureParams.h" #include "GrYUVProvider.h" @@ -68,9 +69,9 @@ SkImageCacherator::SkImageCacherator(SkImageGenerator* gen, const SkImageInfo& i , fUniqueID(uniqueID) {} -SkData* SkImageCacherator::refEncoded() { +SkData* SkImageCacherator::refEncoded(GrContext* ctx) { ScopedGenerator generator(this); - return generator->refEncodedData(); + return generator->refEncodedData(ctx); } static bool check_output_bitmap(const SkBitmap& bitmap, uint32_t expectedID) { @@ -258,7 +259,7 @@ GrTexture* SkImageCacherator::lockTexture(GrContext* ctx, const GrUniqueKey& key const GrSurfaceDesc desc = GrImageInfoToSurfaceDesc(fInfo); // 3. Ask the generator to return a compressed form that the GPU might support - SkAutoTUnref data(this->refEncoded()); + SkAutoTUnref data(this->refEncoded(ctx)); if (data) { GrTexture* tex = load_compressed_into_texture(ctx, data, desc); if (tex) { @@ -289,60 +290,13 @@ GrTexture* SkImageCacherator::lockTexture(GrContext* ctx, const GrUniqueKey& key /////////////////////////////////////////////////////////////////////////////////////////////////// -#include "GrTextureParamsAdjuster.h" - -class Cacherator_GrTextureMaker : public GrTextureMaker { -public: - Cacherator_GrTextureMaker(GrContext* context, SkImageCacherator* cacher, const SkImage* client, - SkImage::CachingHint chint) - : INHERITED(context, cacher->info().width(), cacher->info().height()) - , fCacher(cacher) - , fClient(client) - , fCachingHint(chint) - { - if (client) { - GrMakeKeyFromImageID(&fOriginalKey, client->uniqueID(), - SkIRect::MakeWH(this->width(), this->height())); - } - } - -protected: - // TODO: consider overriding this, for the case where the underlying generator might be - // able to efficiently produce a "stretched" texture natively (e.g. picture-backed) - // GrTexture* generateTextureForParams(const CopyParams&) override; - - GrTexture* refOriginalTexture() override { - return fCacher->lockTexture(this->context(), fOriginalKey, fClient, fCachingHint); - } - - void makeCopyKey(const CopyParams& stretch, GrUniqueKey* paramsCopyKey) override { - if (fOriginalKey.isValid()) { - MakeCopyKeyFromOrigKey(fOriginalKey, stretch, paramsCopyKey); - } - } - - void didCacheCopy(const GrUniqueKey& copyKey) override { - if (fClient) { - as_IB(fClient)->notifyAddedToCache(); - } - } - -private: - SkImageCacherator* fCacher; - const SkImage* fClient; - GrUniqueKey fOriginalKey; - SkImage::CachingHint fCachingHint; - - typedef GrTextureMaker INHERITED; -}; - GrTexture* SkImageCacherator::lockAsTexture(GrContext* ctx, const GrTextureParams& params, const SkImage* client, SkImage::CachingHint chint) { if (!ctx) { return nullptr; } - return Cacherator_GrTextureMaker(ctx, this, client, chint).refTextureForParams(params); + return GrImageTextureMaker(ctx, this, client, chint).refTextureForParams(params); } #else diff --git a/gfx/skia/skia/src/core/SkImageCacherator.h b/gfx/skia/skia/src/core/SkImageCacherator.h index bce39679c6..6b000668dd 100644 --- a/gfx/skia/skia/src/core/SkImageCacherator.h +++ b/gfx/skia/skia/src/core/SkImageCacherator.h @@ -54,8 +54,11 @@ public: /** * If the underlying src naturally is represented by an encoded blob (in SkData), this returns * a ref to that data. If not, it returns null. + * + * If a GrContext is specified, then the caller is only interested in gpu-specific encoded + * formats, so others (e.g. PNG) can just return nullptr. */ - SkData* refEncoded(); + SkData* refEncoded(GrContext*); // Only return true if the generate has already been cached. bool lockAsBitmapOnlyIfAlreadyCached(SkBitmap*); @@ -95,7 +98,7 @@ private: const SkIPoint fOrigin; const uint32_t fUniqueID; - friend class Cacherator_GrTextureMaker; + friend class GrImageTextureMaker; }; #endif diff --git a/gfx/skia/skia/src/core/SkImageFilter.cpp b/gfx/skia/skia/src/core/SkImageFilter.cpp index 6a3286ed67..6fd098d2f7 100644 --- a/gfx/skia/skia/src/core/SkImageFilter.cpp +++ b/gfx/skia/skia/src/core/SkImageFilter.cpp @@ -18,6 +18,7 @@ #include "SkReadBuffer.h" #include "SkRect.h" #include "SkTDynamicHash.h" +#include "SkTHash.h" #include "SkTInternalLList.h" #include "SkValidationUtils.h" #include "SkWriteBuffer.h" @@ -201,6 +202,7 @@ SkImageFilter::~SkImageFilter() { SkSafeUnref(fInputs[i]); } delete[] fInputs; + Cache::Get()->purgeByImageFilterId(fUniqueID); } SkImageFilter::SkImageFilter(int inputCount, SkReadBuffer& buffer) @@ -263,25 +265,16 @@ bool SkImageFilter::filterImage(Proxy* proxy, const SkBitmap& src, } bool SkImageFilter::filterInput(int index, Proxy* proxy, const SkBitmap& src, - const Context& origCtx, - SkBitmap* result, SkIPoint* offset, - bool relaxSizeConstraint) const { + const Context& ctx, + SkBitmap* result, SkIPoint* offset) const { SkImageFilter* input = this->getInput(index); if (!input) { return true; } - - SizeConstraint constraint = origCtx.sizeConstraint(); - if (relaxSizeConstraint && (kExact_SizeConstraint == constraint)) { - constraint = kApprox_SizeConstraint; - } - Context ctx(origCtx.ctm(), origCtx.clipBounds(), origCtx.cache(), constraint); - - return input->filterImage(proxy, src, ctx, result, offset); + return input->filterImage(proxy, src, this->mapContext(ctx), result, offset); } -bool SkImageFilter::filterBounds(const SkIRect& src, const SkMatrix& ctm, - SkIRect* dst) const { +bool SkImageFilter::filterBounds(const SkIRect& src, const SkMatrix& ctm, SkIRect* dst) const { SkASSERT(dst); return this->onFilterBounds(src, ctm, dst); } @@ -309,9 +302,6 @@ void SkImageFilter::computeFastBounds(const SkRect& src, SkRect* dst) const { } bool SkImageFilter::canComputeFastBounds() const { - if (this->affectsTransparentBlack()) { - return false; - } for (int i = 0; i < fInputCount; i++) { SkImageFilter* input = this->getInput(i); if (input && !input->canComputeFastBounds()) { @@ -321,10 +311,6 @@ bool SkImageFilter::canComputeFastBounds() const { return true; } -bool SkImageFilter::affectsTransparentBlack() const { - return false; -} - bool SkImageFilter::onFilterImage(Proxy*, const SkBitmap&, const Context&, SkBitmap*, SkIPoint*) const { return false; @@ -348,8 +334,6 @@ bool SkImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, const Cont if (!this->applyCropRect(ctx, proxy, input, &srcOffset, &bounds, &input)) { return false; } - SkRect srcRect = SkRect::Make(bounds); - SkRect dstRect = SkRect::MakeWH(srcRect.width(), srcRect.height()); GrContext* context = srcTexture->getContext(); GrSurfaceDesc desc; @@ -358,15 +342,11 @@ bool SkImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, const Cont desc.fHeight = bounds.height(); desc.fConfig = kRGBA_8888_GrPixelConfig; - SkAutoTUnref dst(context->textureProvider()->createTexture(desc, - GrTextureProvider::FromImageFilter(ctx.sizeConstraint()))); + SkAutoTUnref dst(context->textureProvider()->createApproxTexture(desc)); if (!dst) { return false; } - // setup new clip - GrClip clip(dstRect); - GrFragmentProcessor* fp; offset->fX = bounds.left(); offset->fY = bounds.top(); @@ -381,9 +361,12 @@ bool SkImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, const Cont SkAutoTUnref drawContext(context->drawContext(dst->asRenderTarget())); if (drawContext) { + SkRect srcRect = SkRect::Make(bounds); + SkRect dstRect = SkRect::MakeWH(srcRect.width(), srcRect.height()); + GrClip clip(dstRect); drawContext->fillRectToRect(clip, paint, SkMatrix::I(), dstRect, srcRect); - WrapTexture(dst, bounds.width(), bounds.height(), result); + GrWrapTextureInBitmap(dst, bounds.width(), bounds.height(), false, result); return true; } } @@ -391,6 +374,18 @@ bool SkImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, const Cont return false; } +bool SkImageFilter::asAColorFilter(SkColorFilter** filterPtr) const { + SkASSERT(nullptr != filterPtr); + if (!this->isColorFilterNode(filterPtr)) { + return false; + } + if (nullptr != this->getInput(0) || (*filterPtr)->affectsTransparentBlack()) { + (*filterPtr)->unref(); + return false; + } + return true; +} + bool SkImageFilter::applyCropRect(const Context& ctx, const SkBitmap& src, const SkIPoint& srcOffset, SkIRect* dstBounds, SkIRect* srcBounds) const { @@ -400,7 +395,12 @@ bool SkImageFilter::applyCropRect(const Context& ctx, const SkBitmap& src, } src.getBounds(srcBounds); srcBounds->offset(srcOffset); - return fCropRect.applyTo(*srcBounds, ctx, dstBounds) && srcBounds->intersect(*dstBounds); +#ifdef SK_SUPPORT_SRC_BOUNDS_BLOAT_FOR_IMAGEFILTERS + return fCropRect.applyTo(*srcBounds, ctx, dstBounds); +#else + this->onFilterNodeBounds(*srcBounds, ctx.ctm(), dstBounds, kForward_MapDirection); + return fCropRect.applyTo(*dstBounds, ctx, dstBounds); +#endif } bool SkImageFilter::applyCropRect(const Context& ctx, Proxy* proxy, const SkBitmap& src, @@ -408,7 +408,13 @@ bool SkImageFilter::applyCropRect(const Context& ctx, Proxy* proxy, const SkBitm SkIRect srcBounds; src.getBounds(&srcBounds); srcBounds.offset(*srcOffset); +#ifdef SK_SUPPORT_SRC_BOUNDS_BLOAT_FOR_IMAGEFILTERS if (!fCropRect.applyTo(srcBounds, ctx, bounds)) { +#else + SkIRect dstBounds; + this->onFilterNodeBounds(srcBounds, ctx.ctm(), &dstBounds, kForward_MapDirection); + if (!fCropRect.applyTo(dstBounds, ctx, bounds)) { +#endif return false; } @@ -436,26 +442,44 @@ bool SkImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm, return true; } - SkIRect bounds; + SkIRect bounds, totalBounds; + this->onFilterNodeBounds(src, ctm, &bounds, kReverse_MapDirection); for (int i = 0; i < fInputCount; ++i) { SkImageFilter* filter = this->getInput(i); - SkIRect rect = src; - if (filter && !filter->filterBounds(src, ctm, &rect)) { + SkIRect rect = bounds; + if (filter && !filter->filterBounds(bounds, ctm, &rect)) { return false; } if (0 == i) { - bounds = rect; + totalBounds = rect; } else { - bounds.join(rect); + totalBounds.join(rect); } } // don't modify dst until now, so we don't accidentally change it in the // loop, but then return false on the next filter. - *dst = bounds; + *dst = totalBounds; return true; } +void SkImageFilter::onFilterNodeBounds(const SkIRect& src, const SkMatrix&, + SkIRect* dst, MapDirection) const { + *dst = src; +} + + +SkImageFilter::Context SkImageFilter::mapContext(const Context& ctx) const { +#ifdef SK_SUPPORT_SRC_BOUNDS_BLOAT_FOR_IMAGEFILTERS + return ctx; +#else + SkIRect clipBounds; + this->onFilterNodeBounds(ctx.clipBounds(), ctx.ctm(), &clipBounds, + MapDirection::kReverse_MapDirection); + return Context(ctx.ctm(), clipBounds, ctx.cache()); +#endif +} + bool SkImageFilter::asFragmentProcessor(GrFragmentProcessor**, GrTexture*, const SkMatrix&, const SkIRect&) const { return false; @@ -476,16 +500,9 @@ SkImageFilter* SkImageFilter::newWithLocalMatrix(const SkMatrix& matrix) const { #if SK_SUPPORT_GPU -void SkImageFilter::WrapTexture(GrTexture* texture, int width, int height, SkBitmap* result) { - SkImageInfo info = SkImageInfo::MakeN32Premul(width, height); - result->setInfo(info); - result->setPixelRef(new SkGrPixelRef(info, texture))->unref(); -} - bool SkImageFilter::filterInputGPU(int index, SkImageFilter::Proxy* proxy, - const SkBitmap& src, const Context& origCtx, - SkBitmap* result, SkIPoint* offset, - bool relaxSizeConstraint) const { + const SkBitmap& src, const Context& ctx, + SkBitmap* result, SkIPoint* offset) const { SkImageFilter* input = this->getInput(index); if (!input) { return true; @@ -494,21 +511,17 @@ bool SkImageFilter::filterInputGPU(int index, SkImageFilter::Proxy* proxy, // matrix with no clip and that the matrix, clip, and render target set before this function was // called are restored before we return to the caller. GrContext* context = src.getTexture()->getContext(); - - SizeConstraint constraint = origCtx.sizeConstraint(); - if (relaxSizeConstraint && (kExact_SizeConstraint == constraint)) { - constraint = kApprox_SizeConstraint; - } - Context ctx(origCtx.ctm(), origCtx.clipBounds(), origCtx.cache(), constraint); - - if (input->filterImage(proxy, src, ctx, result, offset)) { + if (input->filterImage(proxy, src, this->mapContext(ctx), result, offset)) { if (!result->getTexture()) { const SkImageInfo info = result->info(); if (kUnknown_SkColorType == info.colorType()) { return false; } SkAutoTUnref resultTex( - GrRefCachedBitmapTexture(context, *result,GrTextureParams::ClampNoFilter())); + GrRefCachedBitmapTexture(context, *result, GrTextureParams::ClampNoFilter())); + if (!resultTex) { + return false; + } result->setPixelRef(new SkGrPixelRef(info, resultTex))->unref(); } return true; @@ -532,6 +545,7 @@ public: ++iter; delete v; } + fIdToKeys.foreach([](uint32_t, SkTArray** array) { delete *array; }); } struct Value { Value(const Key& key, const SkBitmap& bitmap, const SkIPoint& offset) @@ -566,6 +580,13 @@ public: removeInternal(v); } Value* v = new Value(key, result, offset); + if (SkTArray** array = fIdToKeys.find(key.fUniqueID)) { + (*array)->push_back(key); + } else { + SkTArray* keyArray = new SkTArray(); + keyArray->push_back(key); + fIdToKeys.set(key.fUniqueID, keyArray); + } fLookup.add(v); fLRU.addToHead(v); fCurrentBytes += result.getSize(); @@ -588,6 +609,19 @@ public: } } + void purgeByImageFilterId(uint32_t uniqueID) override { + SkAutoMutexAcquire mutex(fMutex); + if (SkTArray** array = fIdToKeys.find(uniqueID)) { + for (auto& key : **array) { + if (Value* v = fLookup.find(key)) { + this->removeInternal(v); + } + } + fIdToKeys.remove(uniqueID); + delete *array; // This can be deleted outside the lock + } + } + private: void removeInternal(Value* v) { fCurrentBytes -= v->fBitmap.getSize(); @@ -596,11 +630,12 @@ private: delete v; } private: - SkTDynamicHash fLookup; - mutable SkTInternalLList fLRU; - size_t fMaxBytes; - size_t fCurrentBytes; - mutable SkMutex fMutex; + SkTDynamicHash fLookup; + SkTHashMap*> fIdToKeys; + mutable SkTInternalLList fLRU; + size_t fMaxBytes; + size_t fCurrentBytes; + mutable SkMutex fMutex; }; } // namespace @@ -620,10 +655,12 @@ void SkImageFilter::PurgeCache() { /////////////////////////////////////////////////////////////////////////////////////////////////// -SkBaseDevice* SkImageFilter::DeviceProxy::createDevice(int w, int h) { +SkBaseDevice* SkImageFilter::DeviceProxy::createDevice(int w, int h, TileUsage usage) { SkBaseDevice::CreateInfo cinfo(SkImageInfo::MakeN32Premul(w, h), - SkBaseDevice::kNever_TileUsage, + kPossible_TileUsage == usage ? SkBaseDevice::kPossible_TileUsage + : SkBaseDevice::kNever_TileUsage, kUnknown_SkPixelGeometry, + false, /* preserveLCDText */ true /*forImageFilter*/); SkBaseDevice* dev = fDevice->onCreateDevice(cinfo, nullptr); if (nullptr == dev) { diff --git a/gfx/skia/skia/src/core/SkImageGenerator.cpp b/gfx/skia/skia/src/core/SkImageGenerator.cpp index dcd3ad00b1..7d71b6701c 100644 --- a/gfx/skia/skia/src/core/SkImageGenerator.cpp +++ b/gfx/skia/skia/src/core/SkImageGenerator.cpp @@ -137,7 +137,7 @@ bool SkImageGenerator::generateScaledPixels(const SkISize& scaledSize, ///////////////////////////////////////////////////////////////////////////////////////////// -SkData* SkImageGenerator::onRefEncodedData() { +SkData* SkImageGenerator::onRefEncodedData(SK_REFENCODEDDATA_CTXPARAM) { return nullptr; } diff --git a/gfx/skia/skia/src/core/SkLightingShader.cpp b/gfx/skia/skia/src/core/SkLightingShader.cpp index 147a7ef915..a9fcac3b83 100644 --- a/gfx/skia/skia/src/core/SkLightingShader.cpp +++ b/gfx/skia/skia/src/core/SkLightingShader.cpp @@ -129,11 +129,12 @@ private: #include "GrCoordTransform.h" #include "GrFragmentProcessor.h" +#include "GrInvariantOutput.h" #include "GrTextureAccess.h" #include "glsl/GrGLSLFragmentProcessor.h" #include "glsl/GrGLSLFragmentShaderBuilder.h" -#include "glsl/GrGLSLProgramBuilder.h" #include "glsl/GrGLSLProgramDataManager.h" +#include "glsl/GrGLSLUniformHandler.h" #include "SkGr.h" #include "SkGrPriv.h" @@ -180,27 +181,28 @@ public: void emitCode(EmitArgs& args) override { GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder; + GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; // add uniforms const char* lightDirUniName = nullptr; - fLightDirUni = args.fBuilder->addUniform(GrGLSLProgramBuilder::kFragment_Visibility, - kVec3f_GrSLType, kDefault_GrSLPrecision, - "LightDir", &lightDirUniName); + fLightDirUni = uniformHandler->addUniform(GrGLSLUniformHandler::kFragment_Visibility, + kVec3f_GrSLType, kDefault_GrSLPrecision, + "LightDir", &lightDirUniName); const char* lightColorUniName = nullptr; - fLightColorUni = args.fBuilder->addUniform(GrGLSLProgramBuilder::kFragment_Visibility, - kVec3f_GrSLType, kDefault_GrSLPrecision, - "LightColor", &lightColorUniName); + fLightColorUni = uniformHandler->addUniform(GrGLSLUniformHandler::kFragment_Visibility, + kVec3f_GrSLType, kDefault_GrSLPrecision, + "LightColor", &lightColorUniName); const char* ambientColorUniName = nullptr; - fAmbientColorUni = args.fBuilder->addUniform(GrGLSLProgramBuilder::kFragment_Visibility, - kVec3f_GrSLType, kDefault_GrSLPrecision, - "AmbientColor", &ambientColorUniName); + fAmbientColorUni = uniformHandler->addUniform(GrGLSLUniformHandler::kFragment_Visibility, + kVec3f_GrSLType, kDefault_GrSLPrecision, + "AmbientColor", &ambientColorUniName); const char* xformUniName = nullptr; - fXformUni = args.fBuilder->addUniform(GrGLSLProgramBuilder::kFragment_Visibility, - kVec2f_GrSLType, kDefault_GrSLPrecision, - "Xform", &xformUniName); + fXformUni = uniformHandler->addUniform(GrGLSLUniformHandler::kFragment_Visibility, + kVec2f_GrSLType, kDefault_GrSLPrecision, + "Xform", &xformUniName); fragBuilder->codeAppend("vec4 diffuseColor = "); fragBuilder->appendTextureLookupAndModulate(args.fInputColor, args.fSamplers[0], diff --git a/gfx/skia/skia/src/core/SkLocalMatrixImageFilter.cpp b/gfx/skia/skia/src/core/SkLocalMatrixImageFilter.cpp index e4fc0ceb00..10d63ede66 100644 --- a/gfx/skia/skia/src/core/SkLocalMatrixImageFilter.cpp +++ b/gfx/skia/skia/src/core/SkLocalMatrixImageFilter.cpp @@ -40,9 +40,8 @@ void SkLocalMatrixImageFilter::flatten(SkWriteBuffer& buffer) const { bool SkLocalMatrixImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& src, const Context& ctx, SkBitmap* result, SkIPoint* offset) const { - Context localCtx(SkMatrix::Concat(ctx.ctm(), fLocalM), ctx.clipBounds(), ctx.cache(), - ctx.sizeConstraint()); - return this->filterInput(0, proxy, src, localCtx, result, offset, false); + Context localCtx(SkMatrix::Concat(ctx.ctm(), fLocalM), ctx.clipBounds(), ctx.cache()); + return this->filterInput(0, proxy, src, localCtx, result, offset); } bool SkLocalMatrixImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& matrix, diff --git a/gfx/skia/skia/src/core/SkLocalMatrixShader.cpp b/gfx/skia/skia/src/core/SkLocalMatrixShader.cpp index ce5a7cad71..336f1c49cf 100644 --- a/gfx/skia/skia/src/core/SkLocalMatrixShader.cpp +++ b/gfx/skia/skia/src/core/SkLocalMatrixShader.cpp @@ -10,11 +10,11 @@ SkFlattenable* SkLocalMatrixShader::CreateProc(SkReadBuffer& buffer) { SkMatrix lm; buffer.readMatrix(&lm); - SkAutoTUnref shader(buffer.readShader()); - if (!shader.get()) { + SkAutoTUnref baseShader(buffer.readShader()); + if (!baseShader) { return nullptr; } - return SkShader::CreateLocalMatrixShader(shader, lm); + return baseShader->newWithLocalMatrix(lm); } void SkLocalMatrixShader::flatten(SkWriteBuffer& buffer) const { @@ -47,24 +47,21 @@ void SkLocalMatrixShader::toString(SkString* str) const { } #endif -SkShader* SkShader::CreateLocalMatrixShader(SkShader* proxy, const SkMatrix& localMatrix) { - if (nullptr == proxy) { - return nullptr; - } - +SkShader* SkShader::newWithLocalMatrix(const SkMatrix& localMatrix) const { if (localMatrix.isIdentity()) { - return SkRef(proxy); + return SkRef(const_cast(this)); } const SkMatrix* lm = &localMatrix; + SkShader* baseShader = const_cast(this); SkMatrix otherLocalMatrix; - SkAutoTUnref otherProxy(proxy->refAsALocalMatrixShader(&otherLocalMatrix)); - if (otherProxy.get()) { + SkAutoTUnref proxy(this->refAsALocalMatrixShader(&otherLocalMatrix)); + if (proxy) { otherLocalMatrix.preConcat(localMatrix); lm = &otherLocalMatrix; - proxy = otherProxy.get(); + baseShader = proxy.get(); } - return new SkLocalMatrixShader(proxy, *lm); + return new SkLocalMatrixShader(baseShader, *lm); } diff --git a/gfx/skia/skia/src/core/SkMaskFilter.cpp b/gfx/skia/skia/src/core/SkMaskFilter.cpp index 0753944a3b..51c489721d 100644 --- a/gfx/skia/skia/src/core/SkMaskFilter.cpp +++ b/gfx/skia/skia/src/core/SkMaskFilter.cpp @@ -19,7 +19,6 @@ #if SK_SUPPORT_GPU #include "GrTexture.h" #include "SkGr.h" -#include "SkGrPixelRef.h" #endif SkMaskFilter::NinePatch::~NinePatch() { @@ -349,7 +348,6 @@ bool SkMaskFilter::filterMaskGPU(GrTexture* src, void SkMaskFilter::computeFastBounds(const SkRect& src, SkRect* dst) const { SkMask srcM, dstM; - srcM.fImage = nullptr; srcM.fBounds = src.roundOut(); srcM.fRowBytes = 0; srcM.fFormat = SkMask::kA8_Format; diff --git a/gfx/skia/skia/src/core/SkMatrix.cpp b/gfx/skia/skia/src/core/SkMatrix.cpp index b32f372bbd..4b11fdf331 100644 --- a/gfx/skia/skia/src/core/SkMatrix.cpp +++ b/gfx/skia/skia/src/core/SkMatrix.cpp @@ -156,7 +156,7 @@ uint8_t SkMatrix::computeTypeMask() const { } else { // Only test for scale explicitly if not affine, since affine sets the // scale bit. - if ((m00 - kScalar1Int) | (m11 - kScalar1Int)) { + if ((m00 ^ kScalar1Int) | (m11 ^ kScalar1Int)) { mask |= kScale_Mask; } @@ -380,7 +380,20 @@ void SkMatrix::preScale(SkScalar sx, SkScalar sy) { fMat[kMScaleY] *= sy; fMat[kMPersp1] *= sy; +#ifndef SK_SUPPORT_LEGACY_PRESCALE_SEMANTICS + // Attempt to simplify our type when applying an inverse scale. + // TODO: The persp/affine preconditions are in place to keep the mask consistent with + // what computeTypeMask() would produce (persp/skew always implies kScale). + // We should investigate whether these flag dependencies are truly needed. + if (fMat[kMScaleX] == 1 && fMat[kMScaleY] == 1 + && !(fTypeMask & (kPerspective_Mask | kAffine_Mask))) { + this->clearTypeMask(kScale_Mask); + } else { + this->orTypeMask(kScale_Mask); + } +#else this->orTypeMask(kScale_Mask); +#endif } void SkMatrix::postScale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py) { @@ -1475,9 +1488,15 @@ template bool get_scale_factor(SkMatrix::TypeMask results[1] = apluscdiv2 + x; } } + if (SkScalarIsNaN(results[0])) { + return false; + } SkASSERT(results[0] >= 0); results[0] = SkScalarSqrt(results[0]); if (kBoth_MinMaxOrBoth == MIN_MAX_OR_BOTH) { + if (SkScalarIsNaN(results[1])) { + return false; + } SkASSERT(results[1] >= 0); results[1] = SkScalarSqrt(results[1]); } @@ -1604,8 +1623,14 @@ void SkMatrix::toString(SkString* str) const { #include "SkMatrixUtils.h" -bool SkTreatAsSprite(const SkMatrix& mat, int width, int height, - unsigned subpixelBits) { +bool SkTreatAsSprite(const SkMatrix& mat, const SkISize& size, const SkPaint& paint) { + // Our path aa is 2-bits, and our rect aa is 8, so we could use 8, + // but in practice 4 seems enough (still looks smooth) and allows + // more slightly fractional cases to fall into the fast (sprite) case. + static const unsigned kAntiAliasSubpixelBits = 4; + + const unsigned subpixelBits = paint.isAntiAlias() ? kAntiAliasSubpixelBits : 0; + // quick reject on affine or perspective if (mat.getType() & ~(SkMatrix::kScale_Mask | SkMatrix::kTranslate_Mask)) { return false; @@ -1622,7 +1647,7 @@ bool SkTreatAsSprite(const SkMatrix& mat, int width, int height, } SkRect dst; - SkIRect isrc = { 0, 0, width, height }; + SkIRect isrc = SkIRect::MakeSize(size); { SkRect src; @@ -1635,10 +1660,10 @@ bool SkTreatAsSprite(const SkMatrix& mat, int width, int height, SkScalarRoundToInt(mat.getTranslateY())); if (subpixelBits) { - isrc.fLeft <<= subpixelBits; - isrc.fTop <<= subpixelBits; - isrc.fRight <<= subpixelBits; - isrc.fBottom <<= subpixelBits; + isrc.fLeft = SkLeftShift(isrc.fLeft, subpixelBits); + isrc.fTop = SkLeftShift(isrc.fTop, subpixelBits); + isrc.fRight = SkLeftShift(isrc.fRight, subpixelBits); + isrc.fBottom = SkLeftShift(isrc.fBottom, subpixelBits); const float scale = 1 << subpixelBits; dst.fLeft *= scale; diff --git a/gfx/skia/skia/src/core/SkMatrixImageFilter.cpp b/gfx/skia/skia/src/core/SkMatrixImageFilter.cpp index 4370ddadee..4138ccf677 100644 --- a/gfx/skia/skia/src/core/SkMatrixImageFilter.cpp +++ b/gfx/skia/skia/src/core/SkMatrixImageFilter.cpp @@ -97,30 +97,33 @@ void SkMatrixImageFilter::computeFastBounds(const SkRect& src, SkRect* dst) cons getInput(0)->computeFastBounds(src, &bounds); } fTransform.mapRect(dst, bounds); +#ifdef SK_SUPPORT_SRC_BOUNDS_BLOAT_FOR_IMAGEFILTERS dst->join(bounds); // Work around for skia:3194 +#endif } -bool SkMatrixImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm, - SkIRect* dst) const { - SkMatrix transformInverse; - if (!fTransform.invert(&transformInverse)) { - return false; - } +void SkMatrixImageFilter::onFilterNodeBounds(const SkIRect& src, const SkMatrix& ctm, + SkIRect* dst, MapDirection direction) const { SkMatrix matrix; if (!ctm.invert(&matrix)) { - return false; + *dst = src; + return; + } + if (kForward_MapDirection == direction) { + matrix.postConcat(fTransform); + } else { + SkMatrix transformInverse; + if (!fTransform.invert(&transformInverse)) { + *dst = src; + return; + } + matrix.postConcat(transformInverse); } - matrix.postConcat(transformInverse); matrix.postConcat(ctm); SkRect floatBounds; matrix.mapRect(&floatBounds, SkRect::Make(src)); SkIRect bounds = floatBounds.roundOut(); - if (getInput(0) && !getInput(0)->filterBounds(bounds, ctm, &bounds)) { - return false; - } - *dst = bounds; - return true; } #ifndef SK_IGNORE_TO_STRING diff --git a/gfx/skia/skia/src/core/SkMatrixImageFilter.h b/gfx/skia/skia/src/core/SkMatrixImageFilter.h index 86734b629c..09bfca9fe1 100644 --- a/gfx/skia/skia/src/core/SkMatrixImageFilter.h +++ b/gfx/skia/skia/src/core/SkMatrixImageFilter.h @@ -46,8 +46,8 @@ protected: virtual bool onFilterImage(Proxy*, const SkBitmap& src, const Context&, SkBitmap* result, SkIPoint* loc) const override; - virtual bool onFilterBounds(const SkIRect& src, const SkMatrix&, - SkIRect* dst) const override; + virtual void onFilterNodeBounds(const SkIRect& src, const SkMatrix&, + SkIRect* dst, MapDirection) const override; private: SkMatrix fTransform; diff --git a/gfx/skia/skia/src/core/SkMatrixUtils.h b/gfx/skia/skia/src/core/SkMatrixUtils.h index d1b6658d07..0e01fbe953 100644 --- a/gfx/skia/skia/src/core/SkMatrixUtils.h +++ b/gfx/skia/skia/src/core/SkMatrixUtils.h @@ -8,37 +8,20 @@ #ifndef SkMatrixUtils_DEFINED #define SkMatrixUtils_DEFINED -#include "SkMatrix.h" +#include "SkSize.h" + +class SkMatrix; +class SkPaint; /** - * Number of subpixel bits used in skia's bilerp. - * See SkBitmapProcState_procs.h and SkBitmapProcState_filter.h - */ -#define kSkSubPixelBitsForBilerp 4 - -/** - * Given a matrix and width/height, return true if the computed dst-rect would + * Given a matrix, size and paint, return true if the computed dst-rect would * align such that there is a 1-to-1 coorspondence between src and dst pixels. * This can be called by drawing code to see if drawBitmap can be turned into * drawSprite (which is faster). * - * The src-rect is defined to be { 0, 0, width, height } - * - * The "closeness" test is based on the subpixelBits parameter. Pass 0 for - * round-to-nearest behavior (e.g. nearest neighbor sampling). Pass the number - * of subpixel-bits to simulate filtering. + * The src-rect is defined to be { 0, 0, size.width(), size.height() } */ -bool SkTreatAsSprite(const SkMatrix&, int width, int height, - unsigned subpixelBits); - -/** - * Calls SkTreatAsSprite() with default subpixelBits value to match Skia's - * filter-bitmap implementation (i.e. kSkSubPixelBitsForBilerp). - */ -static inline bool SkTreatAsSpriteFilter(const SkMatrix& matrix, - int width, int height) { - return SkTreatAsSprite(matrix, width, height, kSkSubPixelBitsForBilerp); -} +bool SkTreatAsSprite(const SkMatrix&, const SkISize& size, const SkPaint& paint); /** Decomposes the upper-left 2x2 of the matrix into a rotation (represented by the cosine and sine of the rotation angle), followed by a non-uniform scale, diff --git a/gfx/skia/skia/src/effects/SkColorFilters.cpp b/gfx/skia/skia/src/core/SkModeColorFilter.cpp similarity index 85% rename from gfx/skia/skia/src/effects/SkColorFilters.cpp rename to gfx/skia/skia/src/core/SkModeColorFilter.cpp index 4515c0d3fe..df1051abb0 100644 --- a/gfx/skia/skia/src/effects/SkColorFilters.cpp +++ b/gfx/skia/skia/src/core/SkModeColorFilter.cpp @@ -14,7 +14,17 @@ #include "SkUtils.h" #include "SkString.h" #include "SkValidationUtils.h" -#include "SkColorMatrixFilter.h" + +////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef SK_IGNORE_TO_STRING +void SkModeColorFilter::toString(SkString* str) const { + str->append("SkModeColorFilter: color: 0x"); + str->appendHex(fColor); + str->append(" mode: "); + str->append(SkXfermode::ModeName(fMode)); +} +#endif bool SkModeColorFilter::asColorMode(SkColor* color, SkXfermode::Mode* mode) const { if (color) { @@ -101,7 +111,7 @@ const GrFragmentProcessor* SkModeColorFilter::asFragmentProcessor(GrContext*) co /////////////////////////////////////////////////////////////////////////////// -class Src_SkModeColorFilter : public SkModeColorFilter { +class Src_SkModeColorFilter final : public SkModeColorFilter { public: Src_SkModeColorFilter(SkColor color) : INHERITED(color, SkXfermode::kSrc_Mode) {} @@ -113,7 +123,7 @@ private: typedef SkModeColorFilter INHERITED; }; -class SrcOver_SkModeColorFilter : public SkModeColorFilter { +class SrcOver_SkModeColorFilter final : public SkModeColorFilter { public: SrcOver_SkModeColorFilter(SkColor color) : INHERITED(color, SkXfermode::kSrcOver_Mode) { } @@ -169,28 +179,3 @@ SkColorFilter* SkColorFilter::CreateModeFilter(SkColor color, SkXfermode::Mode m return SkModeColorFilter::Create(color, mode); } } - -/////////////////////////////////////////////////////////////////////////////// - -static SkScalar byte_to_scale(U8CPU byte) { - if (0xFF == byte) { - // want to get this exact - return 1; - } else { - return byte * 0.00392156862745f; - } -} - -SkColorFilter* SkColorFilter::CreateLightingFilter(SkColor mul, SkColor add) { - SkColorMatrix matrix; - matrix.setScale(byte_to_scale(SkColorGetR(mul)), - byte_to_scale(SkColorGetG(mul)), - byte_to_scale(SkColorGetB(mul)), - 1); - matrix.postTranslate(SkIntToScalar(SkColorGetR(add)), - SkIntToScalar(SkColorGetG(add)), - SkIntToScalar(SkColorGetB(add)), - 0); - return SkColorMatrixFilter::Create(matrix); -} - diff --git a/gfx/skia/skia/include/effects/SkModeColorFilter.h b/gfx/skia/skia/src/core/SkModeColorFilter.h similarity index 82% rename from gfx/skia/skia/include/effects/SkModeColorFilter.h rename to gfx/skia/skia/src/core/SkModeColorFilter.h index 06eec43572..9fa7717b84 100644 --- a/gfx/skia/skia/include/effects/SkModeColorFilter.h +++ b/gfx/skia/skia/src/core/SkModeColorFilter.h @@ -1,18 +1,16 @@ /* - * Copyright 2015 Google Inc. + * Copyright 2016 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ +#include "SkColorFilter.h" +#include "SkXfermode.h" + #ifndef SkModeColorFilter_DEFINED #define SkModeColorFilter_DEFINED -#include "SkColorFilter.h" -#include "SkColorPriv.h" -#include "SkString.h" -#include "SkXfermode.h" - class SkModeColorFilter : public SkColorFilter { public: static SkColorFilter* Create(SkColor color, SkXfermode::Mode mode) { @@ -28,12 +26,7 @@ public: void filterSpan(const SkPMColor shader[], int count, SkPMColor result[]) const override; #ifndef SK_IGNORE_TO_STRING - void toString(SkString* str) const override { - str->append("SkModeColorFilter: color: 0x"); - str->appendHex(fColor); - str->append(" mode: "); - str->append(SkXfermode::ModeName(fMode)); - } + void toString(SkString* str) const override; #endif #if SK_SUPPORT_GPU @@ -59,6 +52,8 @@ private: void updateCache(); + friend class SkColorFilter; + typedef SkColorFilter INHERITED; }; diff --git a/gfx/skia/skia/src/core/SkMultiPictureDraw.cpp b/gfx/skia/skia/src/core/SkMultiPictureDraw.cpp index 672bd628b7..5745664858 100644 --- a/gfx/skia/skia/src/core/SkMultiPictureDraw.cpp +++ b/gfx/skia/skia/src/core/SkMultiPictureDraw.cpp @@ -94,7 +94,7 @@ void SkMultiPictureDraw::draw(bool flush) { fThreadSafeDrawData[i].draw(); } #else - sk_parallel_for(fThreadSafeDrawData.count(), [&](int i) { + SkTaskGroup().batch(fThreadSafeDrawData.count(), [&](int i) { fThreadSafeDrawData[i].draw(); }); #endif diff --git a/gfx/skia/skia/src/core/SkNx.h b/gfx/skia/skia/src/core/SkNx.h index 36a645cac1..684a31dcef 100644 --- a/gfx/skia/skia/src/core/SkNx.h +++ b/gfx/skia/skia/src/core/SkNx.h @@ -9,8 +9,7 @@ #define SkNx_DEFINED -#define SKNX_NO_SIMDx // Remove the x to disable SIMD for all SkNx types. - +//#define SKNX_NO_SIMD #include "SkScalar.h" #include "SkTypes.h" @@ -25,7 +24,8 @@ namespace { // The default implementations just fall back on a pair of size N/2. - +// These support the union of operations we might do to ints and floats, but +// platform specializations might support fewer (e.g. no float <<, no int /). template class SkNx { public: @@ -55,14 +55,33 @@ public: SkNx operator + (const SkNx& o) const { return SkNx(fLo + o.fLo, fHi + o.fHi); } SkNx operator - (const SkNx& o) const { return SkNx(fLo - o.fLo, fHi - o.fHi); } SkNx operator * (const SkNx& o) const { return SkNx(fLo * o.fLo, fHi * o.fHi); } + SkNx operator / (const SkNx& o) const { return SkNx(fLo / o.fLo, fHi / o.fHi); } SkNx operator << (int bits) const { return SkNx(fLo << bits, fHi << bits); } SkNx operator >> (int bits) const { return SkNx(fLo >> bits, fHi >> bits); } + SkNx operator == (const SkNx& o) const { return SkNx(fLo == o.fLo, fHi == o.fHi); } + SkNx operator != (const SkNx& o) const { return SkNx(fLo != o.fLo, fHi != o.fHi); } + SkNx operator < (const SkNx& o) const { return SkNx(fLo < o.fLo, fHi < o.fHi); } + SkNx operator > (const SkNx& o) const { return SkNx(fLo > o.fLo, fHi > o.fHi); } + SkNx operator <= (const SkNx& o) const { return SkNx(fLo <= o.fLo, fHi <= o.fHi); } + SkNx operator >= (const SkNx& o) const { return SkNx(fLo >= o.fLo, fHi >= o.fHi); } + static SkNx Min(const SkNx& a, const SkNx& b) { return SkNx(SkNx::Min(a.fLo, b.fLo), SkNx::Min(a.fHi, b.fHi)); } - SkNx operator < (const SkNx& o) const { return SkNx(fLo < o.fLo, fHi < o.fHi); } + static SkNx Max(const SkNx& a, const SkNx& b) { + return SkNx(SkNx::Max(a.fLo, b.fLo), SkNx::Max(a.fHi, b.fHi)); + } + + SkNx sqrt() const { return SkNx(fLo.sqrt(), fHi.sqrt()); } + // Generally, increasing precision, increasing cost. + SkNx rsqrt0() const { return SkNx(fLo.rsqrt0(), fHi.rsqrt0()); } + SkNx rsqrt1() const { return SkNx(fLo.rsqrt1(), fHi.rsqrt1()); } + SkNx rsqrt2() const { return SkNx(fLo.rsqrt2(), fHi.rsqrt2()); } + + SkNx invert() const { return SkNx(fLo. invert(), fHi. invert()); } + SkNx approxInvert() const { return SkNx(fLo.approxInvert(), fHi.approxInvert()); } template T kth() const { SkASSERT(0 <= k && k < N); @@ -81,97 +100,7 @@ protected: SkNx fLo, fHi; }; -template -class SkNx { -public: - SkNx() {} - SkNx(float val) : fLo(val), fHi(val) {} - static SkNx Load(const float vals[N]) { - return SkNx(SkNx::Load(vals), SkNx::Load(vals+N/2)); - } - // FromBytes() and toBytes() specializations may assume their argument is N-byte aligned. - // E.g. Sk4f::FromBytes() may assume it's reading from a 4-byte-aligned pointer. - // Converts [0,255] bytes to [0.0, 255.0] floats. - static SkNx FromBytes(const uint8_t bytes[N]) { - return SkNx(SkNx::FromBytes(bytes), SkNx::FromBytes(bytes+N/2)); - } - - SkNx(float a, float b) : fLo(a), fHi(b) { REQUIRE(N==2); } - SkNx(float a, float b, float c, float d) : fLo(a,b), fHi(c,d) { REQUIRE(N==4); } - SkNx(float a, float b, float c, float d, float e, float f, float g, float h) - : fLo(a,b,c,d) - , fHi(e,f,g,h) { REQUIRE(N==8); } - - void store(float vals[N]) const { - fLo.store(vals); - fHi.store(vals+N/2); - } - // Please see note on FromBytes(). - // Clamps to [0.0,255.0] floats and truncates to [0,255] bytes. - void toBytes(uint8_t bytes[N]) const { - fLo.toBytes(bytes); - fHi.toBytes(bytes+N/2); - } - - // Some implementations can do this faster. - static void ToBytes(uint8_t bytes[4*N], - const SkNx& a, const SkNx& b, const SkNx& c, const SkNx& d) { - a.toBytes(bytes+0*N); - b.toBytes(bytes+1*N); - c.toBytes(bytes+2*N); - d.toBytes(bytes+3*N); - } - - SkNx operator + (const SkNx& o) const { return SkNx(fLo + o.fLo, fHi + o.fHi); } - SkNx operator - (const SkNx& o) const { return SkNx(fLo - o.fLo, fHi - o.fHi); } - SkNx operator * (const SkNx& o) const { return SkNx(fLo * o.fLo, fHi * o.fHi); } - SkNx operator / (const SkNx& o) const { return SkNx(fLo / o.fLo, fHi / o.fHi); } - - SkNx operator == (const SkNx& o) const { return SkNx(fLo == o.fLo, fHi == o.fHi); } - SkNx operator != (const SkNx& o) const { return SkNx(fLo != o.fLo, fHi != o.fHi); } - SkNx operator < (const SkNx& o) const { return SkNx(fLo < o.fLo, fHi < o.fHi); } - SkNx operator > (const SkNx& o) const { return SkNx(fLo > o.fLo, fHi > o.fHi); } - SkNx operator <= (const SkNx& o) const { return SkNx(fLo <= o.fLo, fHi <= o.fHi); } - SkNx operator >= (const SkNx& o) const { return SkNx(fLo >= o.fLo, fHi >= o.fHi); } - - static SkNx Min(const SkNx& l, const SkNx& r) { - return SkNx(SkNx::Min(l.fLo, r.fLo), SkNx::Min(l.fHi, r.fHi)); - } - static SkNx Max(const SkNx& l, const SkNx& r) { - return SkNx(SkNx::Max(l.fLo, r.fLo), SkNx::Max(l.fHi, r.fHi)); - } - - SkNx sqrt() const { return SkNx(fLo. sqrt(), fHi. sqrt()); } - - // Generally, increasing precision, increasing cost. - SkNx rsqrt0() const { return SkNx(fLo.rsqrt0(), fHi.rsqrt0()); } - SkNx rsqrt1() const { return SkNx(fLo.rsqrt1(), fHi.rsqrt1()); } - SkNx rsqrt2() const { return SkNx(fLo.rsqrt2(), fHi.rsqrt2()); } - - SkNx invert() const { return SkNx(fLo. invert(), fHi. invert()); } - SkNx approxInvert() const { return SkNx(fLo.approxInvert(), fHi.approxInvert()); } - - template float kth() const { - SkASSERT(0 <= k && k < N); - return k < N/2 ? fLo.template kth() : fHi.template kth(); - } - - bool allTrue() const { return fLo.allTrue() && fHi.allTrue(); } - bool anyTrue() const { return fLo.anyTrue() || fHi.anyTrue(); } - SkNx thenElse(const SkNx& t, const SkNx& e) const { - return SkNx(fLo.thenElse(t.fLo, e.fLo), fHi.thenElse(t.fHi, e.fHi)); - } - -protected: - REQUIRE(0 == (N & (N-1))); - SkNx(const SkNx& lo, const SkNx& hi) : fLo(lo), fHi(hi) {} - - SkNx fLo, fHi; -}; - - // Bottom out the default implementations with scalars when nothing's been specialized. - template class SkNx<1,T> { public: @@ -190,42 +119,11 @@ public: SkNx operator + (const SkNx& o) const { return SkNx(fVal + o.fVal); } SkNx operator - (const SkNx& o) const { return SkNx(fVal - o.fVal); } SkNx operator * (const SkNx& o) const { return SkNx(fVal * o.fVal); } + SkNx operator / (const SkNx& o) const { return SkNx(fVal / o.fVal); } SkNx operator << (int bits) const { return SkNx(fVal << bits); } SkNx operator >> (int bits) const { return SkNx(fVal >> bits); } - static SkNx Min(const SkNx& a, const SkNx& b) { return SkNx(SkTMin(a.fVal, b.fVal)); } - SkNx operator <(const SkNx& o) const { return SkNx(fVal < o.fVal); } - - template T kth() const { - SkASSERT(0 == k); - return fVal; - } - - bool allTrue() const { return fVal; } - bool anyTrue() const { return fVal; } - SkNx thenElse(const SkNx& t, const SkNx& e) const { return fVal ? t : e; } - -protected: - T fVal; -}; - -template <> -class SkNx<1,float> { -public: - SkNx() {} - SkNx(float val) : fVal(val) {} - static SkNx Load(const float vals[1]) { return SkNx(vals[0]); } - static SkNx FromBytes(const uint8_t bytes[1]) { return SkNx((float)bytes[0]); } - - void store(float vals[1]) const { vals[0] = fVal; } - void toBytes(uint8_t bytes[1]) const { bytes[0] = (uint8_t)(SkTMin(fVal, 255.0f)); } - - SkNx operator + (const SkNx& o) const { return SkNx(fVal + o.fVal); } - SkNx operator - (const SkNx& o) const { return SkNx(fVal - o.fVal); } - SkNx operator * (const SkNx& o) const { return SkNx(fVal * o.fVal); } - SkNx operator / (const SkNx& o) const { return SkNx(fVal / o.fVal); } - SkNx operator == (const SkNx& o) const { return SkNx(fVal == o.fVal); } SkNx operator != (const SkNx& o) const { return SkNx(fVal != o.fVal); } SkNx operator < (const SkNx& o) const { return SkNx(fVal < o.fVal); } @@ -233,33 +131,31 @@ public: SkNx operator <= (const SkNx& o) const { return SkNx(fVal <= o.fVal); } SkNx operator >= (const SkNx& o) const { return SkNx(fVal >= o.fVal); } - static SkNx Min(const SkNx& l, const SkNx& r) { return SkNx(SkTMin(l.fVal, r.fVal)); } - static SkNx Max(const SkNx& l, const SkNx& r) { return SkNx(SkTMax(l.fVal, r.fVal)); } + static SkNx Min(const SkNx& a, const SkNx& b) { return SkNx(SkTMin(a.fVal, b.fVal)); } + static SkNx Max(const SkNx& a, const SkNx& b) { return SkNx(SkTMax(a.fVal, b.fVal)); } - SkNx sqrt() const { return SkNx(sqrtf(fVal)); } - SkNx rsqrt0() const { return SkNx(1.0f / sqrtf(fVal)); } + SkNx sqrt () const { return SkNx(Sqrt(fVal)); } + SkNx rsqrt0() const { return this->sqrt().invert(); } SkNx rsqrt1() const { return this->rsqrt0(); } SkNx rsqrt2() const { return this->rsqrt1(); } - SkNx invert() const { return SkNx(1.0f / fVal); } + SkNx invert() const { return SkNx(1) / SkNx(fVal); } SkNx approxInvert() const { return this->invert(); } - template float kth() const { - SkASSERT(k == 0); + template T kth() const { + SkASSERT(0 == k); return fVal; } - bool allTrue() const { return this->pun() != 0; } - bool anyTrue() const { return this->pun() != 0; } - SkNx thenElse(const SkNx& t, const SkNx& e) const { return this->pun() ? t : e; } + bool allTrue() const { return fVal != 0; } + bool anyTrue() const { return fVal != 0; } + SkNx thenElse(const SkNx& t, const SkNx& e) const { return fVal != 0 ? t : e; } protected: - uint32_t pun() const { - union { float f; uint32_t i; } pun = { fVal }; - return pun.i; - } + static double Sqrt(double val) { return ::sqrt (val); } + static float Sqrt(float val) { return ::sqrtf(val); } - float fVal; + T fVal; }; // This default implementation can be specialized by ../opts/SkNx_foo.h @@ -296,7 +192,7 @@ SkNx SkNx_cast_fallback(const SkNx& src, SkIntSequence) { // This is a generic cast between two SkNx with the same number of elements N. E.g. // Sk4b bs = ...; // Load 4 bytes. -// Sk4f fs = SkNx_cast(bs); // (This will replace SkNf::FromBytes() one day.) +// Sk4f fs = SkNx_cast(bs); // Cast each byte to a float. // Sk4i is = SkNx_cast(fs); // Cast each float to int. // This can be specialized in ../opts/SkNx_foo.h if there's a better platform-specific cast. template @@ -306,31 +202,46 @@ SkNx SkNx_cast(const SkNx& src) { } // namespace - -// Include platform specific specializations if available. -#ifndef SKNX_NO_SIMD - #if SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_AVX - #include "../opts/SkNx_avx.h" - #elif SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE2 - #include "../opts/SkNx_sse.h" - #elif defined(SK_ARM_HAS_NEON) - #include "../opts/SkNx_neon.h" - #endif -#endif - -#undef REQUIRE - typedef SkNx<2, float> Sk2f; -typedef SkNx<2, float> Sk2s; typedef SkNx<4, float> Sk4f; -typedef SkNx<4, float> Sk4s; typedef SkNx<8, float> Sk8f; -typedef SkNx<8, float> Sk8s; -typedef SkNx<8, uint16_t> Sk8h; +typedef SkNx<2, double> Sk2d; +typedef SkNx<4, double> Sk4d; +typedef SkNx<8, double> Sk8d; + +typedef SkNx<2, SkScalar> Sk2s; +typedef SkNx<4, SkScalar> Sk4s; +typedef SkNx<8, SkScalar> Sk8s; + +typedef SkNx< 4, uint16_t> Sk4h; +typedef SkNx< 8, uint16_t> Sk8h; typedef SkNx<16, uint16_t> Sk16h; + +typedef SkNx< 4, uint8_t> Sk4b; +typedef SkNx< 8, uint8_t> Sk8b; typedef SkNx<16, uint8_t> Sk16b; typedef SkNx<4, int> Sk4i; +// Include platform specific specializations if available. +#if !defined(SKNX_NO_SIMD) && SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_AVX + #include "../opts/SkNx_avx.h" +#elif !defined(SKNX_NO_SIMD) && SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE2 + #include "../opts/SkNx_sse.h" +#elif !defined(SKNX_NO_SIMD) && defined(SK_ARM_HAS_NEON) + #include "../opts/SkNx_neon.h" +#else + static inline + void Sk4f_ToBytes(uint8_t p[16], const Sk4f& a, const Sk4f& b, const Sk4f& c, const Sk4f& d) { + SkNx_cast(a).store(p+ 0); + SkNx_cast(b).store(p+ 4); + SkNx_cast(c).store(p+ 8); + SkNx_cast(d).store(p+12); + } +#endif + +#undef REQUIRE + + #endif//SkNx_DEFINED diff --git a/gfx/skia/skia/src/core/SkOpts.cpp b/gfx/skia/skia/src/core/SkOpts.cpp index fc70a570db..970672e32c 100644 --- a/gfx/skia/skia/src/core/SkOpts.cpp +++ b/gfx/skia/skia/src/core/SkOpts.cpp @@ -16,11 +16,12 @@ #include "SkFloatingPoint_opts.h" #include "SkMatrix_opts.h" #include "SkMorphologyImageFilter_opts.h" +#include "SkSwizzler_opts.h" #include "SkTextureCompressor_opts.h" #include "SkUtils_opts.h" #include "SkXfermode_opts.h" -#if defined(SK_CPU_X86) +#if defined(SK_CPU_X86) && !defined(SK_BUILD_FOR_IOS) #if defined(SK_BUILD_FOR_WIN32) #include static void cpuid (uint32_t abcd[4]) { __cpuid ((int*)abcd, 1); } @@ -84,6 +85,10 @@ namespace SkOpts { decltype(matrix_scale_translate) matrix_scale_translate = sk_default::matrix_scale_translate; decltype(matrix_affine) matrix_affine = sk_default::matrix_affine; + decltype( premul_xxxa) premul_xxxa = sk_default:: premul_xxxa; + decltype( swaprb_xxxa) swaprb_xxxa = sk_default:: swaprb_xxxa; + decltype(premul_swaprb_xxxa) premul_swaprb_xxxa = sk_default::premul_swaprb_xxxa; + // Each Init_foo() is defined in src/opts/SkOpts_foo.cpp. void Init_sse2(); void Init_ssse3(); diff --git a/gfx/skia/skia/src/core/SkOpts.h b/gfx/skia/skia/src/core/SkOpts.h index 1b94126cf7..85e38fe139 100644 --- a/gfx/skia/skia/src/core/SkOpts.h +++ b/gfx/skia/skia/src/core/SkOpts.h @@ -57,6 +57,11 @@ namespace SkOpts { const SkColor*); extern SkMatrix::MapPtsProc matrix_translate, matrix_scale_translate, matrix_affine; + + typedef void (*Swizzle_8888_8888)(uint32_t[], const uint32_t[], int); + extern Swizzle_8888_8888 premul_xxxa, // BGRA -> bgrA or RGBA -> rgbA + swaprb_xxxa, // BGRA -> RGBA or RGBA -> BGRA + premul_swaprb_xxxa; // BGRA -> rgbA or RGBA -> bgrA } #endif//SkOpts_DEFINED diff --git a/gfx/skia/skia/src/core/SkPaint.cpp b/gfx/skia/skia/src/core/SkPaint.cpp index 2623052ec4..e5fe975bcd 100644 --- a/gfx/skia/skia/src/core/SkPaint.cpp +++ b/gfx/skia/skia/src/core/SkPaint.cpp @@ -36,6 +36,10 @@ #include "SkSurfacePriv.h" #include "SkXfermode.h" +static inline uint32_t set_clear_mask(uint32_t bits, bool cond, uint32_t mask) { + return cond ? bits | mask : bits & ~mask; +} + // define this to get a printf for out-of-range parameter in setters // e.g. setTextSize(-1) //#define SK_REPORT_API_RANGE_CHECK @@ -184,51 +188,51 @@ void SkPaint::setFlags(uint32_t flags) { } void SkPaint::setAntiAlias(bool doAA) { - this->setFlags(SkSetClearMask(fBitfields.fFlags, doAA, kAntiAlias_Flag)); + this->setFlags(set_clear_mask(fBitfields.fFlags, doAA, kAntiAlias_Flag)); } void SkPaint::setDither(bool doDither) { - this->setFlags(SkSetClearMask(fBitfields.fFlags, doDither, kDither_Flag)); + this->setFlags(set_clear_mask(fBitfields.fFlags, doDither, kDither_Flag)); } void SkPaint::setSubpixelText(bool doSubpixel) { - this->setFlags(SkSetClearMask(fBitfields.fFlags, doSubpixel, kSubpixelText_Flag)); + this->setFlags(set_clear_mask(fBitfields.fFlags, doSubpixel, kSubpixelText_Flag)); } void SkPaint::setLCDRenderText(bool doLCDRender) { - this->setFlags(SkSetClearMask(fBitfields.fFlags, doLCDRender, kLCDRenderText_Flag)); + this->setFlags(set_clear_mask(fBitfields.fFlags, doLCDRender, kLCDRenderText_Flag)); } void SkPaint::setEmbeddedBitmapText(bool doEmbeddedBitmapText) { - this->setFlags(SkSetClearMask(fBitfields.fFlags, doEmbeddedBitmapText, kEmbeddedBitmapText_Flag)); + this->setFlags(set_clear_mask(fBitfields.fFlags, doEmbeddedBitmapText, kEmbeddedBitmapText_Flag)); } void SkPaint::setAutohinted(bool useAutohinter) { - this->setFlags(SkSetClearMask(fBitfields.fFlags, useAutohinter, kAutoHinting_Flag)); + this->setFlags(set_clear_mask(fBitfields.fFlags, useAutohinter, kAutoHinting_Flag)); } void SkPaint::setLinearText(bool doLinearText) { - this->setFlags(SkSetClearMask(fBitfields.fFlags, doLinearText, kLinearText_Flag)); + this->setFlags(set_clear_mask(fBitfields.fFlags, doLinearText, kLinearText_Flag)); } void SkPaint::setVerticalText(bool doVertical) { - this->setFlags(SkSetClearMask(fBitfields.fFlags, doVertical, kVerticalText_Flag)); + this->setFlags(set_clear_mask(fBitfields.fFlags, doVertical, kVerticalText_Flag)); } void SkPaint::setUnderlineText(bool doUnderline) { - this->setFlags(SkSetClearMask(fBitfields.fFlags, doUnderline, kUnderlineText_Flag)); + this->setFlags(set_clear_mask(fBitfields.fFlags, doUnderline, kUnderlineText_Flag)); } void SkPaint::setStrikeThruText(bool doStrikeThru) { - this->setFlags(SkSetClearMask(fBitfields.fFlags, doStrikeThru, kStrikeThruText_Flag)); + this->setFlags(set_clear_mask(fBitfields.fFlags, doStrikeThru, kStrikeThruText_Flag)); } void SkPaint::setFakeBoldText(bool doFakeBold) { - this->setFlags(SkSetClearMask(fBitfields.fFlags, doFakeBold, kFakeBoldText_Flag)); + this->setFlags(set_clear_mask(fBitfields.fFlags, doFakeBold, kFakeBoldText_Flag)); } void SkPaint::setDevKernText(bool doDevKern) { - this->setFlags(SkSetClearMask(fBitfields.fFlags, doDevKern, kDevKernText_Flag)); + this->setFlags(set_clear_mask(fBitfields.fFlags, doDevKern, kDevKernText_Flag)); } void SkPaint::setStyle(Style style) { @@ -1942,6 +1946,9 @@ void SkPaint::flatten(SkWriteBuffer& buffer) const { void SkPaint::unflatten(SkReadBuffer& buffer) { SkASSERT(SkAlign4(kPODPaintSize) == kPODPaintSize); + if (!buffer.validateAvailable(kPODPaintSize)) { + return; + } const void* podData = buffer.skip(kPODPaintSize); const uint32_t* pod = reinterpret_cast(podData); diff --git a/gfx/skia/skia/src/core/SkPath.cpp b/gfx/skia/skia/src/core/SkPath.cpp index 351629d125..4af2dad526 100644 --- a/gfx/skia/skia/src/core/SkPath.cpp +++ b/gfx/skia/skia/src/core/SkPath.cpp @@ -6,6 +6,7 @@ */ #include "SkBuffer.h" +#include "SkCubicClipper.h" #include "SkErrorInternals.h" #include "SkGeometry.h" #include "SkMath.h" @@ -583,7 +584,7 @@ int SkPath::getPoints(SkPoint dst[], int max) const { SkASSERT(max >= 0); SkASSERT(!max || dst); int count = SkMin32(max, fPathRef->countPoints()); - memcpy(dst, fPathRef->points(), count * sizeof(SkPoint)); + sk_careful_memcpy(dst, fPathRef->points(), count * sizeof(SkPoint)); return fPathRef->countPoints(); } @@ -1908,6 +1909,13 @@ size_t SkPath::readFromMemory(const void* storage, size_t length) { uint8_t dir = (packed >> kDirection_SerializationShift) & 0x3; fIsVolatile = (packed >> kIsVolatile_SerializationShift) & 0x1; SkPathRef* pathRef = SkPathRef::CreateFromBuffer(&buffer); + if (!pathRef) { + return 0; + } + + fPathRef.reset(pathRef); + SkDEBUGCODE(this->validate();) + buffer.skipToAlign4(); // compatibility check if (version < kPathPrivFirstDirection_Version) { @@ -1928,17 +1936,7 @@ size_t SkPath::readFromMemory(const void* storage, size_t length) { fFirstDirection = dir; } - size_t sizeRead = 0; - if (buffer.isValid()) { - fPathRef.reset(pathRef); - SkDEBUGCODE(this->validate();) - buffer.skipToAlign4(); - sizeRead = buffer.pos(); - } else if (pathRef) { - // If the buffer is not valid, pathRef should be nullptr - sk_throw(); - } - return sizeRead; + return buffer.pos(); } /////////////////////////////////////////////////////////////////////////////// @@ -2588,6 +2586,12 @@ bool SkPathPriv::CheapComputeFirstDirection(const SkPath& path, FirstDirection* /////////////////////////////////////////////////////////////////////////////// +static bool between(SkScalar a, SkScalar b, SkScalar c) { + SkASSERT(((a <= b && b <= c) || (a >= b && b >= c)) == ((a - b) * (c - b) <= 0) + || (SkScalarNearlyZero(a) && SkScalarNearlyZero(b) && SkScalarNearlyZero(c))); + return (a - b) * (c - b) <= 0; +} + static SkScalar eval_cubic_coeff(SkScalar A, SkScalar B, SkScalar C, SkScalar D, SkScalar t) { return SkScalarMulAdd(SkScalarMulAdd(SkScalarMulAdd(A, t, B), t, C), t, D); @@ -2602,40 +2606,6 @@ static SkScalar eval_cubic_pts(SkScalar c0, SkScalar c1, SkScalar c2, SkScalar c return eval_cubic_coeff(A, B, C, D, t); } -/* Given 4 cubic points (either Xs or Ys), and a target X or Y, compute the - t value such that cubic(t) = target - */ -static void chopMonoCubicAt(SkScalar c0, SkScalar c1, SkScalar c2, SkScalar c3, - SkScalar target, SkScalar* t) { - // SkASSERT(c0 <= c1 && c1 <= c2 && c2 <= c3); - SkASSERT(c0 < target && target < c3); - - SkScalar D = c0 - target; - SkScalar A = c3 + 3*(c1 - c2) - c0; - SkScalar B = 3*(c2 - c1 - c1 + c0); - SkScalar C = 3*(c1 - c0); - - const SkScalar TOLERANCE = SK_Scalar1 / 4096; - SkScalar minT = 0; - SkScalar maxT = SK_Scalar1; - SkScalar mid; - int i; - for (i = 0; i < 16; i++) { - mid = SkScalarAve(minT, maxT); - SkScalar delta = eval_cubic_coeff(A, B, C, D, mid); - if (delta < 0) { - minT = mid; - delta = -delta; - } else { - maxT = mid; - } - if (delta < TOLERANCE) { - break; - } - } - *t = mid; -} - template static void find_minmax(const SkPoint pts[], SkScalar* minPtr, SkScalar* maxPtr) { SkScalar min, max; @@ -2648,19 +2618,31 @@ template static void find_minmax(const SkPoint pts[], *maxPtr = max; } -static int winding_mono_cubic(const SkPoint pts[], SkScalar x, SkScalar y) { - SkPoint storage[4]; +static bool checkOnCurve(SkScalar x, SkScalar y, const SkPoint& start, const SkPoint& end) { + if (start.fY == end.fY) { + return between(start.fX, x, end.fX) && x != end.fX; + } else { + return x == start.fX && y == start.fY; + } +} + +static int winding_mono_cubic(const SkPoint pts[], SkScalar x, SkScalar y, int* onCurveCount) { + SkScalar y0 = pts[0].fY; + SkScalar y3 = pts[3].fY; int dir = 1; - if (pts[0].fY > pts[3].fY) { - storage[0] = pts[3]; - storage[1] = pts[2]; - storage[2] = pts[1]; - storage[3] = pts[0]; - pts = storage; + if (y0 > y3) { + SkTSwap(y0, y3); dir = -1; } - if (y < pts[0].fY || y >= pts[3].fY) { + if (y < y0 || y > y3) { + return 0; + } + if (checkOnCurve(x, y, pts[0], pts[3])) { + *onCurveCount += 1; + return 0; + } + if (y == y3) { return 0; } @@ -2676,22 +2658,47 @@ static int winding_mono_cubic(const SkPoint pts[], SkScalar x, SkScalar y) { // compute the actual x(t) value SkScalar t; - chopMonoCubicAt(pts[0].fY, pts[1].fY, pts[2].fY, pts[3].fY, y, &t); + SkAssertResult(SkCubicClipper::ChopMonoAtY(pts, y, &t)); SkScalar xt = eval_cubic_pts(pts[0].fX, pts[1].fX, pts[2].fX, pts[3].fX, t); + if (SkScalarNearlyEqual(xt, x)) { + if (x != pts[3].fX || y != pts[3].fY) { // don't test end points; they're start points + *onCurveCount += 1; + return 0; + } + } return xt < x ? dir : 0; } -static int winding_cubic(const SkPoint pts[], SkScalar x, SkScalar y) { +static int winding_cubic(const SkPoint pts[], SkScalar x, SkScalar y, int* onCurveCount) { SkPoint dst[10]; int n = SkChopCubicAtYExtrema(pts, dst); int w = 0; for (int i = 0; i <= n; ++i) { - w += winding_mono_cubic(&dst[i * 3], x, y); + w += winding_mono_cubic(&dst[i * 3], x, y, onCurveCount); } return w; } -static int winding_mono_quad(const SkPoint pts[], SkScalar x, SkScalar y) { +static double conic_eval_numerator(const SkScalar src[], SkScalar w, SkScalar t) { + SkASSERT(src); + SkASSERT(t >= 0 && t <= 1); + SkScalar src2w = src[2] * w; + SkScalar C = src[0]; + SkScalar A = src[4] - 2 * src2w + C; + SkScalar B = 2 * (src2w - C); + return (A * t + B) * t + C; +} + + +static double conic_eval_denominator(SkScalar w, SkScalar t) { + SkScalar B = 2 * (w - 1); + SkScalar C = 1; + SkScalar A = -B; + return (A * t + B) * t + C; +} + +static int winding_mono_conic(const SkConic& conic, SkScalar x, SkScalar y, int* onCurveCount) { + const SkPoint* pts = conic.fPts; SkScalar y0 = pts[0].fY; SkScalar y2 = pts[2].fY; @@ -2700,35 +2707,41 @@ static int winding_mono_quad(const SkPoint pts[], SkScalar x, SkScalar y) { SkTSwap(y0, y2); dir = -1; } - if (y < y0 || y >= y2) { + if (y < y0 || y > y2) { return 0; } - - // bounds check on X (not required. is it faster?) -#if 0 - if (pts[0].fX > x && pts[1].fX > x && pts[2].fX > x) { + if (checkOnCurve(x, y, pts[0], pts[2])) { + *onCurveCount += 1; + return 0; + } + if (y == y2) { return 0; } -#endif SkScalar roots[2]; - int n = SkFindUnitQuadRoots(pts[0].fY - 2 * pts[1].fY + pts[2].fY, - 2 * (pts[1].fY - pts[0].fY), - pts[0].fY - y, - roots); + SkScalar A = pts[2].fY; + SkScalar B = pts[1].fY * conic.fW - y * conic.fW + y; + SkScalar C = pts[0].fY; + A += C - 2 * B; // A = a + c - 2*(b*w - yCept*w + yCept) + B -= C; // B = b*w - w * yCept + yCept - a + C -= y; + int n = SkFindUnitQuadRoots(A, 2 * B, C, roots); SkASSERT(n <= 1); SkScalar xt; if (0 == n) { - SkScalar mid = SkScalarAve(y0, y2); - // Need [0] and [2] if dir == 1 - // and [2] and [0] if dir == -1 - xt = y < mid ? pts[1 - dir].fX : pts[dir - 1].fX; + // zero roots are returned only when y0 == y + // Need [0] if dir == 1 + // and [2] if dir == -1 + xt = pts[1 - dir].fX; } else { SkScalar t = roots[0]; - SkScalar C = pts[0].fX; - SkScalar A = pts[2].fX - 2 * pts[1].fX + C; - SkScalar B = 2 * (pts[1].fX - C); - xt = SkScalarMulAdd(SkScalarMulAdd(A, t, B), t, C); + xt = conic_eval_numerator(&pts[0].fX, conic.fW, t) / conic_eval_denominator(conic.fW, t); + } + if (SkScalarNearlyEqual(xt, x)) { + if (x != pts[2].fX || y != pts[2].fY) { // don't test end points; they're start points + *onCurveCount += 1; + return 0; + } } return xt < x ? dir : 0; } @@ -2745,7 +2758,75 @@ static bool is_mono_quad(SkScalar y0, SkScalar y1, SkScalar y2) { } } -static int winding_quad(const SkPoint pts[], SkScalar x, SkScalar y) { +static int winding_conic(const SkPoint pts[], SkScalar x, SkScalar y, SkScalar weight, + int* onCurveCount) { + SkConic conic(pts, weight); + SkConic chopped[2]; + // If the data points are very large, the conic may not be monotonic but may also + // fail to chop. Then, the chopper does not split the original conic in two. + bool isMono = is_mono_quad(pts[0].fY, pts[1].fY, pts[2].fY) || !conic.chopAtYExtrema(chopped); + int w = winding_mono_conic(isMono ? conic : chopped[0], x, y, onCurveCount); + if (!isMono) { + w += winding_mono_conic(chopped[1], x, y, onCurveCount); + } + return w; +} + +static int winding_mono_quad(const SkPoint pts[], SkScalar x, SkScalar y, int* onCurveCount) { + SkScalar y0 = pts[0].fY; + SkScalar y2 = pts[2].fY; + + int dir = 1; + if (y0 > y2) { + SkTSwap(y0, y2); + dir = -1; + } + if (y < y0 || y > y2) { + return 0; + } + if (checkOnCurve(x, y, pts[0], pts[2])) { + *onCurveCount += 1; + return 0; + } + if (y == y2) { + return 0; + } + // bounds check on X (not required. is it faster?) +#if 0 + if (pts[0].fX > x && pts[1].fX > x && pts[2].fX > x) { + return 0; + } +#endif + + SkScalar roots[2]; + int n = SkFindUnitQuadRoots(pts[0].fY - 2 * pts[1].fY + pts[2].fY, + 2 * (pts[1].fY - pts[0].fY), + pts[0].fY - y, + roots); + SkASSERT(n <= 1); + SkScalar xt; + if (0 == n) { + // zero roots are returned only when y0 == y + // Need [0] if dir == 1 + // and [2] if dir == -1 + xt = pts[1 - dir].fX; + } else { + SkScalar t = roots[0]; + SkScalar C = pts[0].fX; + SkScalar A = pts[2].fX - 2 * pts[1].fX + C; + SkScalar B = 2 * (pts[1].fX - C); + xt = SkScalarMulAdd(SkScalarMulAdd(A, t, B), t, C); + } + if (SkScalarNearlyEqual(xt, x)) { + if (x != pts[2].fX || y != pts[2].fY) { // don't test end points; they're start points + *onCurveCount += 1; + return 0; + } + } + return xt < x ? dir : 0; +} + +static int winding_quad(const SkPoint pts[], SkScalar x, SkScalar y, int* onCurveCount) { SkPoint dst[5]; int n = 0; @@ -2753,14 +2834,14 @@ static int winding_quad(const SkPoint pts[], SkScalar x, SkScalar y) { n = SkChopQuadAtYExtrema(pts, dst); pts = dst; } - int w = winding_mono_quad(pts, x, y); + int w = winding_mono_quad(pts, x, y, onCurveCount); if (n > 0) { - w += winding_mono_quad(&pts[2], x, y); + w += winding_mono_quad(&pts[2], x, y, onCurveCount); } return w; } -static int winding_line(const SkPoint pts[], SkScalar x, SkScalar y) { +static int winding_line(const SkPoint pts[], SkScalar x, SkScalar y, int* onCurveCount) { SkScalar x0 = pts[0].fX; SkScalar y0 = pts[0].fY; SkScalar x1 = pts[1].fX; @@ -2773,19 +2854,133 @@ static int winding_line(const SkPoint pts[], SkScalar x, SkScalar y) { SkTSwap(y0, y1); dir = -1; } - if (y < y0 || y >= y1) { + if (y < y0 || y > y1) { return 0; } + if (checkOnCurve(x, y, pts[0], pts[1])) { + *onCurveCount += 1; + return 0; + } + if (y == y1) { + return 0; + } + SkScalar cross = SkScalarMul(x1 - x0, y - pts[0].fY) - SkScalarMul(dy, x - x0); - SkScalar cross = SkScalarMul(x1 - x0, y - pts[0].fY) - - SkScalarMul(dy, x - pts[0].fX); - - if (SkScalarSignAsInt(cross) == dir) { + if (!cross) { + // zero cross means the point is on the line, and since the case where + // y of the query point is at the end point is handled above, we can be + // sure that we're on the line (excluding the end point) here + if (x != x1 || y != pts[1].fY) { + *onCurveCount += 1; + } + dir = 0; + } else if (SkScalarSignAsInt(cross) == dir) { dir = 0; } return dir; } +static void tangent_cubic(const SkPoint pts[], SkScalar x, SkScalar y, + SkTDArray* tangents) { + if (!between(pts[0].fY, y, pts[1].fY) && !between(pts[1].fY, y, pts[2].fY) + && !between(pts[2].fY, y, pts[3].fY)) { + return; + } + if (!between(pts[0].fX, x, pts[1].fX) && !between(pts[1].fX, x, pts[2].fX) + && !between(pts[2].fX, x, pts[3].fX)) { + return; + } + SkPoint dst[10]; + int n = SkChopCubicAtYExtrema(pts, dst); + for (int i = 0; i <= n; ++i) { + SkPoint* c = &dst[i * 3]; + SkScalar t; + SkAssertResult(SkCubicClipper::ChopMonoAtY(c, y, &t)); + SkScalar xt = eval_cubic_pts(c[0].fX, c[1].fX, c[2].fX, c[3].fX, t); + if (!SkScalarNearlyEqual(x, xt)) { + continue; + } + SkVector tangent; + SkEvalCubicAt(c, t, nullptr, &tangent, nullptr); + tangents->push(tangent); + } +} + +static void tangent_conic(const SkPoint pts[], SkScalar x, SkScalar y, SkScalar w, + SkTDArray* tangents) { + if (!between(pts[0].fY, y, pts[1].fY) && !between(pts[1].fY, y, pts[2].fY)) { + return; + } + if (!between(pts[0].fX, x, pts[1].fX) && !between(pts[1].fX, x, pts[2].fX)) { + return; + } + SkScalar roots[2]; + SkScalar A = pts[2].fY; + SkScalar B = pts[1].fY * w - y * w + y; + SkScalar C = pts[0].fY; + A += C - 2 * B; // A = a + c - 2*(b*w - yCept*w + yCept) + B -= C; // B = b*w - w * yCept + yCept - a + C -= y; + int n = SkFindUnitQuadRoots(A, 2 * B, C, roots); + for (int index = 0; index < n; ++index) { + SkScalar t = roots[index]; + SkScalar xt = conic_eval_numerator(&pts[0].fX, w, t) / conic_eval_denominator(w, t); + if (!SkScalarNearlyEqual(x, xt)) { + continue; + } + SkConic conic(pts, w); + tangents->push(conic.evalTangentAt(t)); + } +} + +static void tangent_quad(const SkPoint pts[], SkScalar x, SkScalar y, + SkTDArray* tangents) { + if (!between(pts[0].fY, y, pts[1].fY) && !between(pts[1].fY, y, pts[2].fY)) { + return; + } + if (!between(pts[0].fX, x, pts[1].fX) && !between(pts[1].fX, x, pts[2].fX)) { + return; + } + SkScalar roots[2]; + int n = SkFindUnitQuadRoots(pts[0].fY - 2 * pts[1].fY + pts[2].fY, + 2 * (pts[1].fY - pts[0].fY), + pts[0].fY - y, + roots); + for (int index = 0; index < n; ++index) { + SkScalar t = roots[index]; + SkScalar C = pts[0].fX; + SkScalar A = pts[2].fX - 2 * pts[1].fX + C; + SkScalar B = 2 * (pts[1].fX - C); + SkScalar xt = (A * t + B) * t + C; + if (!SkScalarNearlyEqual(x, xt)) { + continue; + } + tangents->push(SkEvalQuadTangentAt(pts, t)); + } +} + +static void tangent_line(const SkPoint pts[], SkScalar x, SkScalar y, + SkTDArray* tangents) { + SkScalar y0 = pts[0].fY; + SkScalar y1 = pts[1].fY; + if (!between(y0, y, y1)) { + return; + } + SkScalar x0 = pts[0].fX; + SkScalar x1 = pts[1].fX; + if (!between(x0, x, x1)) { + return; + } + SkScalar dx = x1 - x0; + SkScalar dy = y1 - y0; + if (!SkScalarNearlyEqual((x - x0) * dy, dx * (y - y0))) { + return; + } + SkVector v; + v.set(dx, dy); + tangents->push(v); +} + static bool contains_inclusive(const SkRect& r, SkScalar x, SkScalar y) { return r.fLeft <= x && x <= r.fRight && r.fTop <= y && y <= r.fBottom; } @@ -2803,6 +2998,7 @@ bool SkPath::contains(SkScalar x, SkScalar y) const { SkPath::Iter iter(*this, true); bool done = false; int w = 0; + int onCurveCount = 0; do { SkPoint pts[4]; switch (iter.next(pts, false)) { @@ -2810,32 +3006,84 @@ bool SkPath::contains(SkScalar x, SkScalar y) const { case SkPath::kClose_Verb: break; case SkPath::kLine_Verb: - w += winding_line(pts, x, y); + w += winding_line(pts, x, y, &onCurveCount); break; case SkPath::kQuad_Verb: - w += winding_quad(pts, x, y); + w += winding_quad(pts, x, y, &onCurveCount); break; case SkPath::kConic_Verb: - SkASSERT(0); + w += winding_conic(pts, x, y, iter.conicWeight(), &onCurveCount); break; case SkPath::kCubic_Verb: - w += winding_cubic(pts, x, y); + w += winding_cubic(pts, x, y, &onCurveCount); break; case SkPath::kDone_Verb: done = true; break; } } while (!done); - - switch (this->getFillType()) { - case SkPath::kEvenOdd_FillType: - case SkPath::kInverseEvenOdd_FillType: - w &= 1; - break; - default: - break; + bool evenOddFill = SkPath::kEvenOdd_FillType == this->getFillType() + || SkPath::kInverseEvenOdd_FillType == this->getFillType(); + if (evenOddFill) { + w &= 1; } - return SkToBool(w); + if (w) { + return !isInverse; + } + if (onCurveCount <= 1) { + return SkToBool(onCurveCount) ^ isInverse; + } + if ((onCurveCount & 1) || evenOddFill) { + return SkToBool(onCurveCount & 1) ^ isInverse; + } + // If the point touches an even number of curves, and the fill is winding, check for + // coincidence. Count coincidence as places where the on curve points have identical tangents. + iter.setPath(*this, true); + done = false; + SkTDArray tangents; + do { + SkPoint pts[4]; + int oldCount = tangents.count(); + switch (iter.next(pts, false)) { + case SkPath::kMove_Verb: + case SkPath::kClose_Verb: + break; + case SkPath::kLine_Verb: + tangent_line(pts, x, y, &tangents); + break; + case SkPath::kQuad_Verb: + tangent_quad(pts, x, y, &tangents); + break; + case SkPath::kConic_Verb: + tangent_conic(pts, x, y, iter.conicWeight(), &tangents); + break; + case SkPath::kCubic_Verb: + tangent_cubic(pts, x, y, &tangents); + break; + case SkPath::kDone_Verb: + done = true; + break; + } + if (tangents.count() > oldCount) { + int last = tangents.count() - 1; + const SkVector& tangent = tangents[last]; + if (SkScalarNearlyZero(tangent.lengthSqd())) { + tangents.remove(last); + } else { + for (int index = 0; index < last; ++index) { + const SkVector& test = tangents[index]; + if (SkScalarNearlyZero(test.cross(tangent)) + && SkScalarSignAsInt(tangent.fX * test.fX) <= 0 + && SkScalarSignAsInt(tangent.fY * test.fY) <= 0) { + tangents.remove(last); + tangents.removeShuffle(index); + break; + } + } + } + } + } while (!done); + return SkToBool(tangents.count()) ^ isInverse; } int SkPath::ConvertConicToQuads(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2, diff --git a/gfx/skia/skia/src/core/SkPathEffect.cpp b/gfx/skia/skia/src/core/SkPathEffect.cpp index 2403ffcb30..293bb53b2c 100644 --- a/gfx/skia/skia/src/core/SkPathEffect.cpp +++ b/gfx/skia/skia/src/core/SkPathEffect.cpp @@ -67,7 +67,11 @@ void SkPairPathEffect::toString(SkString* str) const { SkFlattenable* SkComposePathEffect::CreateProc(SkReadBuffer& buffer) { SkAutoTUnref pe0(buffer.readPathEffect()); SkAutoTUnref pe1(buffer.readPathEffect()); - return SkComposePathEffect::Create(pe0, pe1); + if (pe0 && pe1) { + return SkComposePathEffect::Create(pe0, pe1); + } else { + return nullptr; + } } bool SkComposePathEffect::filterPath(SkPath* dst, const SkPath& src, @@ -100,7 +104,11 @@ void SkComposePathEffect::toString(SkString* str) const { SkFlattenable* SkSumPathEffect::CreateProc(SkReadBuffer& buffer) { SkAutoTUnref pe0(buffer.readPathEffect()); SkAutoTUnref pe1(buffer.readPathEffect()); - return SkSumPathEffect::Create(pe0, pe1); + if (pe0 && pe1) { + return SkSumPathEffect::Create(pe0, pe1); + } else { + return nullptr; + } } bool SkSumPathEffect::filterPath(SkPath* dst, const SkPath& src, diff --git a/gfx/skia/skia/src/core/SkPathMeasure.cpp b/gfx/skia/skia/src/core/SkPathMeasure.cpp index caff6df399..eb80cf3b83 100644 --- a/gfx/skia/skia/src/core/SkPathMeasure.cpp +++ b/gfx/skia/skia/src/core/SkPathMeasure.cpp @@ -20,11 +20,20 @@ enum { kConic_SegType, }; +#ifdef SK_SUPPORT_LEGACY_PATH_MEASURE_TVALUE #define kMaxTValue 32767 +#else +#define kMaxTValue 0x3FFFFFFF +#endif static inline SkScalar tValue2Scalar(int t) { SkASSERT((unsigned)t <= kMaxTValue); +#ifdef SK_SUPPORT_LEGACY_PATH_MEASURE_TVALUE return t * 3.05185e-5f; // t / 32767 +#else + const SkScalar kMaxTReciprocal = 1.0f / kMaxTValue; + return t * kMaxTReciprocal; +#endif } SkScalar SkPathMeasure::Segment::getScalarT() const { @@ -81,6 +90,28 @@ static bool cubic_too_curvy(const SkPoint pts[4]) { SkScalarInterp(pts[0].fY, pts[3].fY, SK_Scalar1*2/3)); } +/* from http://www.malczak.linuxpl.com/blog/quadratic-bezier-curve-length/ */ +static SkScalar compute_quad_len(const SkPoint pts[3]) { + SkPoint a,b; + a.fX = pts[0].fX - 2 * pts[1].fX + pts[2].fX; + a.fY = pts[0].fY - 2 * pts[1].fY + pts[2].fY; + b.fX = 2 * (pts[1].fX - pts[0].fX); + b.fY = 2 * (pts[1].fY - pts[0].fY); + SkScalar A = 4 * (a.fX * a.fX + a.fY * a.fY); + SkScalar B = 4 * (a.fX * b.fX + a.fY * b.fY); + SkScalar C = b.fX * b.fX + b.fY * b.fY; + + SkScalar Sabc = 2 * SkScalarSqrt(A + B + C); + SkScalar A_2 = SkScalarSqrt(A); + SkScalar A_32 = 2 * A * A_2; + SkScalar C_2 = 2 * SkScalarSqrt(C); + SkScalar BA = B / A_2; + + return (A_32 * Sabc + A_2 * B * (Sabc - C_2) + + (4 * C * A - B * B) * SkScalarLog((2 * A_2 + BA + Sabc) / (BA + C_2))) / (4 * A_32); +} + + SkScalar SkPathMeasure::compute_quad_segs(const SkPoint pts[3], SkScalar distance, int mint, int maxt, int ptIndex) { if (tspan_big_enough(maxt - mint) && quad_too_curvy(pts)) { @@ -200,7 +231,19 @@ void SkPathMeasure::buildSegments() { case SkPath::kQuad_Verb: { SkScalar prevD = distance; - distance = this->compute_quad_segs(pts, distance, 0, kMaxTValue, ptIndex); + if (false) { + SkScalar length = compute_quad_len(pts); + if (length) { + distance += length; + Segment* seg = fSegments.append(); + seg->fDistance = distance; + seg->fPtIndex = ptIndex; + seg->fType = kQuad_SegType; + seg->fTValue = kMaxTValue; + } + } else { + distance = this->compute_quad_segs(pts, distance, 0, kMaxTValue, ptIndex); + } if (distance > prevD) { fPts.append(2, pts + 1); ptIndex += 2; diff --git a/gfx/skia/skia/src/core/SkPathPriv.h b/gfx/skia/skia/src/core/SkPathPriv.h index 51a1a80094..048c926ce2 100644 --- a/gfx/skia/skia/src/core/SkPathPriv.h +++ b/gfx/skia/skia/src/core/SkPathPriv.h @@ -55,9 +55,29 @@ public: return computedDir == dir; } - static bool LastVerbIsClose(const SkPath& path) { - int count = path.countVerbs(); - return count >= 1 && path.fPathRef->verbs()[~(count - 1)] == SkPath::Verb::kClose_Verb; + static bool IsClosedSingleContour(const SkPath& path) { + int verbCount = path.countVerbs(); + if (verbCount == 0) + return false; + int moveCount = 0; + auto verbs = path.fPathRef->verbs(); + for (int i = 0; i < verbCount; i++) { + switch (verbs[~i]) { // verbs are stored backwards; we use [~i] to get the i'th verb + case SkPath::Verb::kMove_Verb: + moveCount += 1; + if (moveCount > 1) { + return false; + } + break; + case SkPath::Verb::kClose_Verb: + if (i == verbCount - 1) { + return true; + } + return false; + default: break; + } + } + return false; } static void AddGenIDChangeListener(const SkPath& path, SkPathRef::GenIDChangeListener* listener) { diff --git a/gfx/skia/skia/src/core/SkPathRef.cpp b/gfx/skia/skia/src/core/SkPathRef.cpp index fa073db3e0..a6aa5df372 100644 --- a/gfx/skia/skia/src/core/SkPathRef.cpp +++ b/gfx/skia/skia/src/core/SkPathRef.cpp @@ -9,6 +9,7 @@ #include "SkOncePtr.h" #include "SkPath.h" #include "SkPathRef.h" +#include ////////////////////////////////////////////////////////////////////////////// SkPathRef::Editor::Editor(SkAutoTUnref* pathRef, @@ -72,7 +73,8 @@ void SkPathRef::CreateTransformedCopy(SkAutoTUnref* dst, if (*dst != &src) { (*dst)->resetToSize(src.fVerbCnt, src.fPointCnt, src.fConicWeights.count()); - memcpy((*dst)->verbsMemWritable(), src.verbsMemBegin(), src.fVerbCnt * sizeof(uint8_t)); + sk_careful_memcpy((*dst)->verbsMemWritable(), src.verbsMemBegin(), + src.fVerbCnt * sizeof(uint8_t)); (*dst)->fConicWeights = src.fConicWeights; } @@ -141,10 +143,18 @@ SkPathRef* SkPathRef::CreateFromBuffer(SkRBuffer* buffer) { bool isRRect = (packed >> kIsRRect_SerializationShift) & 1; int32_t verbCount, pointCount, conicCount; + ptrdiff_t maxPtrDiff = std::numeric_limits::max(); if (!buffer->readU32(&(ref->fGenerationID)) || !buffer->readS32(&verbCount) || + verbCount < 0 || + static_cast(verbCount) > maxPtrDiff/sizeof(uint8_t) || !buffer->readS32(&pointCount) || - !buffer->readS32(&conicCount)) { + pointCount < 0 || + static_cast(pointCount) > maxPtrDiff/sizeof(SkPoint) || + sizeof(uint8_t) * verbCount + sizeof(SkPoint) * pointCount > + static_cast(maxPtrDiff) || + !buffer->readS32(&conicCount) || + conicCount < 0) { delete ref; return nullptr; } @@ -281,8 +291,8 @@ void SkPathRef::copy(const SkPathRef& ref, SkDEBUGCODE(this->validate();) this->resetToSize(ref.fVerbCnt, ref.fPointCnt, ref.fConicWeights.count(), additionalReserveVerbs, additionalReservePoints); - memcpy(this->verbsMemWritable(), ref.verbsMemBegin(), ref.fVerbCnt * sizeof(uint8_t)); - memcpy(this->fPoints, ref.fPoints, ref.fPointCnt * sizeof(SkPoint)); + sk_careful_memcpy(this->verbsMemWritable(), ref.verbsMemBegin(), ref.fVerbCnt*sizeof(uint8_t)); + sk_careful_memcpy(this->fPoints, ref.fPoints, ref.fPointCnt * sizeof(SkPoint)); fConicWeights = ref.fConicWeights; fBoundsIsDirty = ref.fBoundsIsDirty; if (!fBoundsIsDirty) { @@ -579,6 +589,11 @@ uint8_t SkPathRef::Iter::next(SkPoint pts[4]) { return (uint8_t) verb; } +uint8_t SkPathRef::Iter::peek() const { + const uint8_t* next = fVerbs - 1; + return next <= fVerbStop ? (uint8_t) SkPath::kDone_Verb : *next; +} + #ifdef SK_DEBUG void SkPathRef::validate() const { this->INHERITED::validate(); diff --git a/gfx/skia/skia/src/core/SkPictureFlat.h b/gfx/skia/skia/src/core/SkPictureFlat.h index 8befca0538..4eee04fcda 100644 --- a/gfx/skia/skia/src/core/SkPictureFlat.h +++ b/gfx/skia/skia/src/core/SkPictureFlat.h @@ -51,7 +51,7 @@ enum DrawType { RESTORE, ROTATE, SAVE, - SAVE_LAYER, + SAVE_LAYER_SAVEFLAGS_DEPRECATED, SCALE, SET_MATRIX, SKEW, @@ -75,7 +75,10 @@ enum DrawType { DRAW_IMAGE_NINE, DRAW_IMAGE_RECT, - LAST_DRAWTYPE_ENUM = DRAW_IMAGE_RECT + SAVE_LAYER_SAVELAYERFLAGS_DEPRECATED_JAN_2016, + SAVE_LAYER_SAVELAYERREC, + + LAST_DRAWTYPE_ENUM = SAVE_LAYER_SAVELAYERREC, }; // In the 'match' method, this constant will match any flavor of DRAW_BITMAP* @@ -93,6 +96,13 @@ enum DrawAtlasFlags { DRAW_ATLAS_HAS_CULL = 1 << 1, }; +enum SaveLayerRecFlatFlags { + SAVELAYERREC_HAS_BOUNDS = 1 << 0, + SAVELAYERREC_HAS_PAINT = 1 << 1, + SAVELAYERREC_HAS_BACKDROP = 1 << 2, + SAVELAYERREC_HAS_FLAGS = 1 << 3, +}; + /////////////////////////////////////////////////////////////////////////////// // clipparams are packed in 5 bits // doAA:1 | regionOp:4 diff --git a/gfx/skia/skia/src/core/SkPictureImageGenerator.cpp b/gfx/skia/skia/src/core/SkPictureImageGenerator.cpp index 39caa55d0c..398c6eb5df 100644 --- a/gfx/skia/skia/src/core/SkPictureImageGenerator.cpp +++ b/gfx/skia/skia/src/core/SkPictureImageGenerator.cpp @@ -108,8 +108,7 @@ bool SkPictureImageGenerator::onGenerateScaledPixels(const SkISize& scaledSize, matrix.postTranslate(-SkIntToScalar(scaledOrigin.x()), -SkIntToScalar(scaledOrigin.y())); SkBitmap bitmap; - if (!bitmap.installPixels(scaledPixels.info(), scaledPixels.writable_addr(), - scaledPixels.rowBytes())) { + if (!bitmap.installPixels(scaledPixels)) { return false; } diff --git a/gfx/skia/skia/src/core/SkPicturePlayback.cpp b/gfx/skia/skia/src/core/SkPicturePlayback.cpp index b994071cbb..4b028f714e 100644 --- a/gfx/skia/skia/src/core/SkPicturePlayback.cpp +++ b/gfx/skia/skia/src/core/SkPicturePlayback.cpp @@ -16,6 +16,28 @@ #include "SkTDArray.h" #include "SkTypes.h" +// matches old SkCanvas::SaveFlags +enum LegacySaveFlags { + kHasAlphaLayer_LegacySaveFlags = 0x04, + kClipToLayer_LegacySaveFlags = 0x10, +}; +#ifdef SK_SUPPORT_LEGACY_SAVEFLAGS +static_assert(kHasAlphaLayer_LegacySaveFlags == (int)SkCanvas::kHasAlphaLayer_SaveFlag, ""); +static_assert(kClipToLayer_LegacySaveFlags == (int)SkCanvas::kClipToLayer_SaveFlag, ""); +#endif + +SkCanvas::SaveLayerFlags SkCanvas::LegacySaveFlagsToSaveLayerFlags(uint32_t flags) { + uint32_t layerFlags = 0; + + if (0 == (flags & kClipToLayer_LegacySaveFlags)) { + layerFlags |= SkCanvas::kDontClipToLayer_PrivateSaveLayerFlag; + } + if (0 == (flags & kHasAlphaLayer_LegacySaveFlags)) { + layerFlags |= kIsOpaque_SaveLayerFlag; + } + return layerFlags; +} + /* * Read the next op code and chunk size from 'reader'. The returned size * is the entire size of the chunk (including the opcode). Thus, the @@ -368,11 +390,11 @@ void SkPicturePlayback::handleOp(SkReader32* reader, canvas->drawRRect(rrect, paint); } break; case DRAW_SPRITE: { - const SkPaint* paint = fPictureData->getPaint(reader); - const SkBitmap bitmap = shallow_copy(fPictureData->getBitmap(reader)); - int left = reader->readInt(); - int top = reader->readInt(); - canvas->drawSprite(bitmap, left, top, paint); + /* const SkPaint* paint = */ fPictureData->getPaint(reader); + /* const SkBitmap bitmap = */ shallow_copy(fPictureData->getBitmap(reader)); + /* int left = */ reader->readInt(); + /* int top = */ reader->readInt(); + // drawSprite removed dec-2015 } break; case DRAW_TEXT: { const SkPaint& paint = *fPictureData->getPaint(reader); @@ -455,10 +477,34 @@ void SkPicturePlayback::handleOp(SkReader32* reader, } canvas->save(); break; - case SAVE_LAYER: { + case SAVE_LAYER_SAVEFLAGS_DEPRECATED: { const SkRect* boundsPtr = get_rect_ptr(reader); const SkPaint* paint = fPictureData->getPaint(reader); - canvas->saveLayer(boundsPtr, paint, (SkCanvas::SaveFlags) reader->readInt()); + auto flags = SkCanvas::LegacySaveFlagsToSaveLayerFlags(reader->readInt()); + canvas->saveLayer(SkCanvas::SaveLayerRec(boundsPtr, paint, flags)); + } break; + case SAVE_LAYER_SAVELAYERFLAGS_DEPRECATED_JAN_2016: { + const SkRect* boundsPtr = get_rect_ptr(reader); + const SkPaint* paint = fPictureData->getPaint(reader); + canvas->saveLayer(SkCanvas::SaveLayerRec(boundsPtr, paint, reader->readInt())); + } break; + case SAVE_LAYER_SAVELAYERREC: { + SkCanvas::SaveLayerRec rec(nullptr, nullptr, nullptr, 0); + const uint32_t flatFlags = reader->readInt(); + if (flatFlags & SAVELAYERREC_HAS_BOUNDS) { + rec.fBounds = &reader->skipT(); + } + if (flatFlags & SAVELAYERREC_HAS_PAINT) { + rec.fPaint = fPictureData->getPaint(reader); + } + if (flatFlags & SAVELAYERREC_HAS_BACKDROP) { + const SkPaint* paint = fPictureData->getPaint(reader); + rec.fBackdrop = paint->getImageFilter(); + } + if (flatFlags & SAVELAYERREC_HAS_FLAGS) { + rec.fSaveLayerFlags = reader->readInt(); + } + canvas->saveLayer(rec); } break; case SCALE: { SkScalar sx = reader->readScalar(); diff --git a/gfx/skia/skia/src/core/SkPictureRecord.cpp b/gfx/skia/skia/src/core/SkPictureRecord.cpp index 2b56b74dad..2822a1ac16 100644 --- a/gfx/skia/skia/src/core/SkPictureRecord.cpp +++ b/gfx/skia/skia/src/core/SkPictureRecord.cpp @@ -25,12 +25,6 @@ enum { // A lot of basic types get stored as a uint32_t: bools, ints, paint indices, etc. static int const kUInt32Size = 4; -static const uint32_t kSaveSize = kUInt32Size; -#ifdef SK_DEBUG -static const uint32_t kSaveLayerNoBoundsSize = 4 * kUInt32Size; -static const uint32_t kSaveLayerWithBoundsSize = 4 * kUInt32Size + sizeof(SkRect); -#endif//SK_DEBUG - SkPictureRecord::SkPictureRecord(const SkISize& dimensions, uint32_t flags) : INHERITED(dimensions.width(), dimensions.height()) , fRecordFlags(flags) @@ -45,93 +39,6 @@ SkPictureRecord::~SkPictureRecord() { /////////////////////////////////////////////////////////////////////////////// -#ifdef SK_DEBUG -// Return the offset of the paint inside a given op's byte stream. A zero -// return value means there is no paint (and you really shouldn't be calling -// this method) -static inline size_t get_paint_offset(DrawType op, size_t opSize) { - // These offsets are where the paint would be if the op size doesn't overflow - static const uint8_t gPaintOffsets[] = { - 0, // UNUSED - no paint - 0, // CLIP_PATH - no paint - 0, // CLIP_REGION - no paint - 0, // CLIP_RECT - no paint - 0, // CLIP_RRECT - no paint - 0, // CONCAT - no paint - 1, // DRAW_BITMAP - right after op code - 1, // DRAW_BITMAP_MATRIX - right after op code, deprecated - 1, // DRAW_BITMAP_NINE - right after op code - 1, // DRAW_BITMAP_RECT - right after op code - 0, // DRAW_CLEAR - no paint - 0, // DRAW_DATA - no paint - 1, // DRAW_OVAL - right after op code - 1, // DRAW_PAINT - right after op code - 1, // DRAW_PATH - right after op code - 0, // DRAW_PICTURE - no paint - 1, // DRAW_POINTS - right after op code - 1, // DRAW_POS_TEXT - right after op code - 1, // DRAW_POS_TEXT_TOP_BOTTOM - right after op code - 1, // DRAW_POS_TEXT_H - right after op code - 1, // DRAW_POS_TEXT_H_TOP_BOTTOM - right after op code - 1, // DRAW_RECT - right after op code - 1, // DRAW_RRECT - right after op code - 1, // DRAW_SPRITE - right after op code - 1, // DRAW_TEXT - right after op code - 1, // DRAW_TEXT_ON_PATH - right after op code - 1, // DRAW_TEXT_TOP_BOTTOM - right after op code - 1, // DRAW_VERTICES - right after op code - 0, // RESTORE - no paint - 0, // ROTATE - no paint - 0, // SAVE - no paint - 0, // SAVE_LAYER - see below - this paint's location varies - 0, // SCALE - no paint - 0, // SET_MATRIX - no paint - 0, // SKEW - no paint - 0, // TRANSLATE - no paint - 0, // NOOP - no paint - 0, // BEGIN_GROUP - no paint - 0, // COMMENT - no paint - 0, // END_GROUP - no paint - 1, // DRAWDRRECT - right after op code - 0, // PUSH_CULL - no paint - 0, // POP_CULL - no paint - 1, // DRAW_PATCH - right after op code - 1, // DRAW_PICTURE_MATRIX_PAINT - right after op code - 1, // DRAW_TEXT_BLOB- right after op code - 1, // DRAW_IMAGE - right after op code - 1, // DRAW_IMAGE_RECT_STRICT - right after op code - 1, // DRAW_ATLAS - right after op code - 1, // DRAW_IMAGE_NINE - right after op code - 1, // DRAW_IMAGE_RECT - right after op code - }; - - static_assert(sizeof(gPaintOffsets) == LAST_DRAWTYPE_ENUM + 1, "need_to_be_in_sync"); - SkASSERT((unsigned)op <= (unsigned)LAST_DRAWTYPE_ENUM); - - int overflow = 0; - if (0 != (opSize & ~MASK_24) || opSize == MASK_24) { - // This op's size overflows so an extra uint32_t will be written - // after the op code - overflow = sizeof(uint32_t); - } - - if (SAVE_LAYER == op) { - static const uint32_t kSaveLayerNoBoundsPaintOffset = 2 * kUInt32Size; - static const uint32_t kSaveLayerWithBoundsPaintOffset = 2 * kUInt32Size + sizeof(SkRect); - - if (kSaveLayerNoBoundsSize == opSize) { - return kSaveLayerNoBoundsPaintOffset + overflow; - } else { - SkASSERT(kSaveLayerWithBoundsSize == opSize); - return kSaveLayerWithBoundsPaintOffset + overflow; - } - } - - SkASSERT(0 != gPaintOffsets[op]); // really shouldn't be calling this method - return gPaintOffsets[op] * sizeof(uint32_t) + overflow; -} -#endif//SK_DEBUG - void SkPictureRecord::willSave() { // record the offset to us, making it non-positive to distinguish a save // from a clip entry. @@ -145,20 +52,19 @@ void SkPictureRecord::recordSave() { fContentInfo.onSave(); // op only - size_t size = kSaveSize; + size_t size = sizeof(kUInt32Size); size_t initialOffset = this->addDraw(SAVE, &size); this->validate(initialOffset, size); } -SkCanvas::SaveLayerStrategy SkPictureRecord::willSaveLayer(const SkRect* bounds, - const SkPaint* paint, SaveFlags flags) { +SkCanvas::SaveLayerStrategy SkPictureRecord::getSaveLayerStrategy(const SaveLayerRec& rec) { // record the offset to us, making it non-positive to distinguish a save // from a clip entry. fRestoreOffsetStack.push(-(int32_t)fWriter.bytesWritten()); - this->recordSaveLayer(bounds, paint, flags); + this->recordSaveLayer(rec); - this->INHERITED::willSaveLayer(bounds, paint, flags); + (void)this->INHERITED::getSaveLayerStrategy(rec); /* No need for a (potentially very big) layer which we don't actually need at this time (and may not be able to afford since during record our clip starts out the size of the picture, which is often much larger @@ -167,26 +73,47 @@ SkCanvas::SaveLayerStrategy SkPictureRecord::willSaveLayer(const SkRect* bounds, return kNoLayer_SaveLayerStrategy; } -void SkPictureRecord::recordSaveLayer(const SkRect* bounds, const SkPaint* paint, - SaveFlags flags) { +void SkPictureRecord::recordSaveLayer(const SaveLayerRec& rec) { fContentInfo.onSaveLayer(); - // op + bool for 'bounds' + // op + flatflags size_t size = 2 * kUInt32Size; - if (bounds) { - size += sizeof(*bounds); // + rect + uint32_t flatFlags = 0; + + if (rec.fBounds) { + flatFlags |= SAVELAYERREC_HAS_BOUNDS; + size += sizeof(*rec.fBounds); + } + if (rec.fPaint) { + flatFlags |= SAVELAYERREC_HAS_PAINT; + size += sizeof(uint32_t); // index + } + if (rec.fBackdrop) { + flatFlags |= SAVELAYERREC_HAS_BACKDROP; + size += sizeof(uint32_t); // (paint) index + } + if (rec.fSaveLayerFlags) { + flatFlags |= SAVELAYERREC_HAS_FLAGS; + size += sizeof(uint32_t); } - // + paint index + flags - size += 2 * kUInt32Size; - - SkASSERT(kSaveLayerNoBoundsSize == size || kSaveLayerWithBoundsSize == size); - - size_t initialOffset = this->addDraw(SAVE_LAYER, &size); - this->addRectPtr(bounds); - SkASSERT(initialOffset+get_paint_offset(SAVE_LAYER, size) == fWriter.bytesWritten()); - this->addPaintPtr(paint); - this->addInt(flags); + const size_t initialOffset = this->addDraw(SAVE_LAYER_SAVELAYERREC, &size); + this->addInt(flatFlags); + if (flatFlags & SAVELAYERREC_HAS_BOUNDS) { + this->addRect(*rec.fBounds); + } + if (flatFlags & SAVELAYERREC_HAS_PAINT) { + this->addPaintPtr(rec.fPaint); + } + if (flatFlags & SAVELAYERREC_HAS_BACKDROP) { + // overkill, but we didn't already track single flattenables, so using a paint for that + SkPaint paint; + paint.setImageFilter(const_cast(rec.fBackdrop)); + this->addPaint(paint); + } + if (flatFlags & SAVELAYERREC_HAS_FLAGS) { + this->addInt(rec.fSaveLayerFlags); + } this->validate(initialOffset, size); } @@ -321,7 +248,9 @@ void SkPictureRecord::fillRestoreOffsetPlaceholdersForCurrentStackLevel(uint32_t // assert that the final offset value points to a save verb uint32_t opSize; DrawType drawOp = peek_op_and_size(&fWriter, -offset, &opSize); - SkASSERT(SAVE == drawOp || SAVE_LAYER == drawOp); + SkASSERT(SAVE_LAYER_SAVEFLAGS_DEPRECATED != drawOp); + SkASSERT(SAVE_LAYER_SAVELAYERFLAGS_DEPRECATED_JAN_2016 != drawOp); + SkASSERT(SAVE == drawOp || SAVE_LAYER_SAVELAYERREC == drawOp); } #endif } @@ -459,7 +388,6 @@ void SkPictureRecord::onDrawPaint(const SkPaint& paint) { // op + paint index size_t size = 2 * kUInt32Size; size_t initialOffset = this->addDraw(DRAW_PAINT, &size); - SkASSERT(initialOffset+get_paint_offset(DRAW_PAINT, size) == fWriter.bytesWritten()); this->addPaint(paint); this->validate(initialOffset, size); } @@ -471,7 +399,6 @@ void SkPictureRecord::onDrawPoints(PointMode mode, size_t count, const SkPoint p // op + paint index + mode + count + point data size_t size = 4 * kUInt32Size + count * sizeof(SkPoint); size_t initialOffset = this->addDraw(DRAW_POINTS, &size); - SkASSERT(initialOffset+get_paint_offset(DRAW_POINTS, size) == fWriter.bytesWritten()); this->addPaint(paint); this->addInt(mode); @@ -484,7 +411,6 @@ void SkPictureRecord::onDrawOval(const SkRect& oval, const SkPaint& paint) { // op + paint index + rect size_t size = 2 * kUInt32Size + sizeof(oval); size_t initialOffset = this->addDraw(DRAW_OVAL, &size); - SkASSERT(initialOffset+get_paint_offset(DRAW_OVAL, size) == fWriter.bytesWritten()); this->addPaint(paint); this->addRect(oval); this->validate(initialOffset, size); @@ -494,7 +420,6 @@ void SkPictureRecord::onDrawRect(const SkRect& rect, const SkPaint& paint) { // op + paint index + rect size_t size = 2 * kUInt32Size + sizeof(rect); size_t initialOffset = this->addDraw(DRAW_RECT, &size); - SkASSERT(initialOffset+get_paint_offset(DRAW_RECT, size) == fWriter.bytesWritten()); this->addPaint(paint); this->addRect(rect); this->validate(initialOffset, size); @@ -504,7 +429,6 @@ void SkPictureRecord::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) { // op + paint index + rrect size_t size = 2 * kUInt32Size + SkRRect::kSizeInMemory; size_t initialOffset = this->addDraw(DRAW_RRECT, &size); - SkASSERT(initialOffset+get_paint_offset(DRAW_RRECT, size) == fWriter.bytesWritten()); this->addPaint(paint); this->addRRect(rrect); this->validate(initialOffset, size); @@ -515,7 +439,6 @@ void SkPictureRecord::onDrawDRRect(const SkRRect& outer, const SkRRect& inner, // op + paint index + rrects size_t size = 2 * kUInt32Size + SkRRect::kSizeInMemory * 2; size_t initialOffset = this->addDraw(DRAW_DRRECT, &size); - SkASSERT(initialOffset+get_paint_offset(DRAW_DRRECT, size) == fWriter.bytesWritten()); this->addPaint(paint); this->addRRect(outer); this->addRRect(inner); @@ -528,7 +451,6 @@ void SkPictureRecord::onDrawPath(const SkPath& path, const SkPaint& paint) { // op + paint index + path index size_t size = 3 * kUInt32Size; size_t initialOffset = this->addDraw(DRAW_PATH, &size); - SkASSERT(initialOffset+get_paint_offset(DRAW_PATH, size) == fWriter.bytesWritten()); this->addPaint(paint); this->addPath(path); this->validate(initialOffset, size); @@ -539,7 +461,6 @@ void SkPictureRecord::onDrawBitmap(const SkBitmap& bitmap, SkScalar left, SkScal // op + paint index + bitmap index + left + top size_t size = 3 * kUInt32Size + 2 * sizeof(SkScalar); size_t initialOffset = this->addDraw(DRAW_BITMAP, &size); - SkASSERT(initialOffset+get_paint_offset(DRAW_BITMAP, size) == fWriter.bytesWritten()); this->addPaintPtr(paint); this->addBitmap(bitmap); this->addScalar(left); @@ -557,7 +478,6 @@ void SkPictureRecord::onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src size += sizeof(dst); // + rect size_t initialOffset = this->addDraw(DRAW_BITMAP_RECT, &size); - SkASSERT(initialOffset+get_paint_offset(DRAW_BITMAP_RECT, size) == fWriter.bytesWritten()); this->addPaintPtr(paint); this->addBitmap(bitmap); this->addRectPtr(src); // may be null @@ -571,7 +491,6 @@ void SkPictureRecord::onDrawImage(const SkImage* image, SkScalar x, SkScalar y, // op + paint_index + image_index + x + y size_t size = 3 * kUInt32Size + 2 * sizeof(SkScalar); size_t initialOffset = this->addDraw(DRAW_IMAGE, &size); - SkASSERT(initialOffset+get_paint_offset(DRAW_IMAGE, size) == fWriter.bytesWritten()); this->addPaintPtr(paint); this->addImage(image); this->addScalar(x); @@ -589,8 +508,6 @@ void SkPictureRecord::onDrawImageRect(const SkImage* image, const SkRect* src, c size += sizeof(dst); // + rect size_t initialOffset = this->addDraw(DRAW_IMAGE_RECT, &size); - SkASSERT(initialOffset+get_paint_offset(DRAW_IMAGE_RECT, size) - == fWriter.bytesWritten()); this->addPaintPtr(paint); this->addImage(image); this->addRectPtr(src); // may be null @@ -605,7 +522,6 @@ void SkPictureRecord::onDrawImageNine(const SkImage* img, const SkIRect& center, size_t size = 3 * kUInt32Size + sizeof(SkIRect) + sizeof(SkRect); size_t initialOffset = this->addDraw(DRAW_IMAGE_NINE, &size); - SkASSERT(initialOffset+get_paint_offset(DRAW_IMAGE_NINE, size) == fWriter.bytesWritten()); this->addPaintPtr(paint); this->addImage(img); this->addIRect(center); @@ -618,7 +534,6 @@ void SkPictureRecord::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& ce // op + paint index + bitmap id + center + dst rect size_t size = 3 * kUInt32Size + sizeof(center) + sizeof(dst); size_t initialOffset = this->addDraw(DRAW_BITMAP_NINE, &size); - SkASSERT(initialOffset+get_paint_offset(DRAW_BITMAP_NINE, size) == fWriter.bytesWritten()); this->addPaintPtr(paint); this->addBitmap(bitmap); this->addIRect(center); @@ -626,19 +541,6 @@ void SkPictureRecord::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& ce this->validate(initialOffset, size); } -void SkPictureRecord::onDrawSprite(const SkBitmap& bitmap, int left, int top, - const SkPaint* paint) { - // op + paint index + bitmap index + left + top - size_t size = 5 * kUInt32Size; - size_t initialOffset = this->addDraw(DRAW_SPRITE, &size); - SkASSERT(initialOffset+get_paint_offset(DRAW_SPRITE, size) == fWriter.bytesWritten()); - this->addPaintPtr(paint); - this->addBitmap(bitmap); - this->addInt(left); - this->addInt(top); - this->validate(initialOffset, size); -} - void SkPictureRecord::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y, const SkPaint& paint) { // op + paint index + length + 'length' worth of chars + x + y @@ -646,7 +548,6 @@ void SkPictureRecord::onDrawText(const void* text, size_t byteLength, SkScalar x DrawType op = DRAW_TEXT; size_t initialOffset = this->addDraw(op, &size); - SkASSERT(initialOffset+get_paint_offset(op, size) == fWriter.bytesWritten()); this->addPaint(paint); this->addText(text, byteLength); this->addScalar(x); @@ -664,7 +565,6 @@ void SkPictureRecord::onDrawPosText(const void* text, size_t byteLength, const S DrawType op = DRAW_POS_TEXT; size_t initialOffset = this->addDraw(op, &size); - SkASSERT(initialOffset+get_paint_offset(op, size) == fWriter.bytesWritten()); this->addPaint(paint); this->addText(text, byteLength); this->addInt(points); @@ -696,7 +596,6 @@ void SkPictureRecord::onDrawTextOnPath(const void* text, size_t byteLength, cons const SkMatrix& m = matrix ? *matrix : SkMatrix::I(); size_t size = 3 * kUInt32Size + SkAlign4(byteLength) + kUInt32Size + m.writeToMemory(nullptr); size_t initialOffset = this->addDraw(DRAW_TEXT_ON_PATH, &size); - SkASSERT(initialOffset+get_paint_offset(DRAW_TEXT_ON_PATH, size) == fWriter.bytesWritten()); this->addPaint(paint); this->addText(text, byteLength); this->addPath(path); @@ -710,7 +609,6 @@ void SkPictureRecord::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScala // op + paint index + blob index + x/y size_t size = 3 * kUInt32Size + 2 * sizeof(SkScalar); size_t initialOffset = this->addDraw(DRAW_TEXT_BLOB, &size); - SkASSERT(initialOffset + get_paint_offset(DRAW_TEXT_BLOB, size) == fWriter.bytesWritten()); this->addPaint(paint); this->addTextBlob(blob); @@ -733,8 +631,6 @@ void SkPictureRecord::onDrawPicture(const SkPicture* picture, const SkMatrix* ma const SkMatrix& m = matrix ? *matrix : SkMatrix::I(); size += m.writeToMemory(nullptr) + kUInt32Size; // matrix + paint initialOffset = this->addDraw(DRAW_PICTURE_MATRIX_PAINT, &size); - SkASSERT(initialOffset + get_paint_offset(DRAW_PICTURE_MATRIX_PAINT, size) - == fWriter.bytesWritten()); this->addPaintPtr(paint); this->addMatrix(m); this->addPicture(picture); @@ -781,7 +677,6 @@ void SkPictureRecord::onDrawVertices(VertexMode vmode, int vertexCount, } size_t initialOffset = this->addDraw(DRAW_VERTICES, &size); - SkASSERT(initialOffset+get_paint_offset(DRAW_VERTICES, size) == fWriter.bytesWritten()); this->addPaint(paint); this->addInt(flags); this->addInt(vmode); @@ -828,7 +723,6 @@ void SkPictureRecord::onDrawPatch(const SkPoint cubics[12], const SkColor colors } size_t initialOffset = this->addDraw(DRAW_PATCH, &size); - SkASSERT(initialOffset+get_paint_offset(DRAW_PATCH, size) == fWriter.bytesWritten()); this->addPaint(paint); this->addPatch(cubics); this->addInt(flag); @@ -865,7 +759,6 @@ void SkPictureRecord::onDrawAtlas(const SkImage* atlas, const SkRSXform xform[], } size_t initialOffset = this->addDraw(DRAW_ATLAS, &size); - SkASSERT(initialOffset+get_paint_offset(DRAW_ATLAS, size) == fWriter.bytesWritten()); this->addPaintPtr(paint); this->addImage(atlas); this->addInt(flags); diff --git a/gfx/skia/skia/src/core/SkPictureRecord.h b/gfx/skia/skia/src/core/SkPictureRecord.h index 7e21fab112..2e1e62a021 100644 --- a/gfx/skia/skia/src/core/SkPictureRecord.h +++ b/gfx/skia/skia/src/core/SkPictureRecord.h @@ -153,7 +153,7 @@ protected: bool onPeekPixels(SkPixmap*) override { return false; } void willSave() override; - SaveLayerStrategy willSaveLayer(const SkRect*, const SkPaint*, SaveFlags) override; + SaveLayerStrategy getSaveLayerStrategy(const SaveLayerRec&) override; void willRestore() override; void didConcat(const SkMatrix&) override; @@ -192,7 +192,6 @@ protected: const SkPaint*) override; void onDrawBitmapNine(const SkBitmap&, const SkIRect& center, const SkRect& dst, const SkPaint*) override; - void onDrawSprite(const SkBitmap&, int left, int top, const SkPaint*) override; void onDrawVertices(VertexMode vmode, int vertexCount, const SkPoint vertices[], const SkPoint texs[], const SkColor colors[], SkXfermode* xmode, @@ -219,7 +218,7 @@ protected: size_t recordClipPath(int pathID, SkRegion::Op op, bool doAA); size_t recordClipRegion(const SkRegion& region, SkRegion::Op op); void recordSave(); - void recordSaveLayer(const SkRect* bounds, const SkPaint* paint, SaveFlags flags); + void recordSaveLayer(const SaveLayerRec&); void recordRestore(bool fillInSkips = true); private: diff --git a/gfx/skia/skia/src/core/SkPictureShader.cpp b/gfx/skia/skia/src/core/SkPictureShader.cpp index 92e6157343..c803f7d878 100644 --- a/gfx/skia/skia/src/core/SkPictureShader.cpp +++ b/gfx/skia/skia/src/core/SkPictureShader.cpp @@ -290,11 +290,6 @@ void SkPictureShader::PictureShaderContext::shadeSpan(int x, int y, SkPMColor ds fBitmapShaderContext->shadeSpan(x, y, dstC, count); } -void SkPictureShader::PictureShaderContext::shadeSpan16(int x, int y, uint16_t dstC[], int count) { - SkASSERT(fBitmapShaderContext); - fBitmapShaderContext->shadeSpan16(x, y, dstC, count); -} - #ifndef SK_IGNORE_TO_STRING void SkPictureShader::toString(SkString* str) const { static const char* gTileModeName[SkShader::kTileModeCount] = { diff --git a/gfx/skia/skia/src/core/SkPictureShader.h b/gfx/skia/skia/src/core/SkPictureShader.h index 2657bab74c..02fc87f024 100644 --- a/gfx/skia/skia/src/core/SkPictureShader.h +++ b/gfx/skia/skia/src/core/SkPictureShader.h @@ -62,7 +62,6 @@ private: ShadeProc asAShadeProc(void** ctx) override; void shadeSpan(int x, int y, SkPMColor dstC[], int count) override; - void shadeSpan16(int x, int y, uint16_t dstC[], int count) override; private: PictureShaderContext(const SkPictureShader&, const ContextRec&, SkShader* bitmapShader); diff --git a/gfx/skia/skia/src/core/SkPixelRef.cpp b/gfx/skia/skia/src/core/SkPixelRef.cpp index d7117617f6..0825760ccd 100644 --- a/gfx/skia/skia/src/core/SkPixelRef.cpp +++ b/gfx/skia/skia/src/core/SkPixelRef.cpp @@ -307,13 +307,13 @@ void SkPixelRef::restoreMutability() { fMutability = kMutable; } -bool SkPixelRef::readPixels(SkBitmap* dst, const SkIRect* subset) { - return this->onReadPixels(dst, subset); +bool SkPixelRef::readPixels(SkBitmap* dst, SkColorType ct, const SkIRect* subset) { + return this->onReadPixels(dst, ct, subset); } /////////////////////////////////////////////////////////////////////////////////////////////////// -bool SkPixelRef::onReadPixels(SkBitmap* dst, const SkIRect* subset) { +bool SkPixelRef::onReadPixels(SkBitmap* dst, SkColorType, const SkIRect* subset) { return false; } diff --git a/gfx/skia/skia/src/core/SkPixmap.cpp b/gfx/skia/skia/src/core/SkPixmap.cpp index 718be88c57..943287bad3 100644 --- a/gfx/skia/skia/src/core/SkPixmap.cpp +++ b/gfx/skia/skia/src/core/SkPixmap.cpp @@ -163,9 +163,9 @@ bool SkPixmap::erase(SkColor color, const SkIRect& inArea) const { // make rgb premultiplied if (255 != a) { - r = SkAlphaMul(r, a); - g = SkAlphaMul(g, a); - b = SkAlphaMul(b, a); + r = SkMulDiv255Round(r, a); + g = SkMulDiv255Round(g, a); + b = SkMulDiv255Round(b, a); } if (kARGB_4444_SkColorType == this->colorType()) { @@ -186,13 +186,14 @@ bool SkPixmap::erase(SkColor color, const SkIRect& inArea) const { uint32_t* p = this->writable_addr32(area.fLeft, area.fTop); if (255 != a && kPremul_SkAlphaType == this->alphaType()) { - r = SkAlphaMul(r, a); - g = SkAlphaMul(g, a); - b = SkAlphaMul(b, a); + r = SkMulDiv255Round(r, a); + g = SkMulDiv255Round(g, a); + b = SkMulDiv255Round(b, a); } - uint32_t v = kRGBA_8888_SkColorType == this->colorType() ? - SkPackARGB_as_RGBA(a, r, g, b) : SkPackARGB_as_BGRA(a, r, g, b); - + uint32_t v = kRGBA_8888_SkColorType == this->colorType() + ? SkPackARGB_as_RGBA(a, r, g, b) + : SkPackARGB_as_BGRA(a, r, g, b); + while (--height >= 0) { sk_memset32(p, v, width); p = (uint32_t*)((char*)p + rowBytes); @@ -222,9 +223,7 @@ bool SkPixmap::scalePixels(const SkPixmap& dst, SkFilterQuality quality) const { } SkBitmap bitmap; - // we will only ready from this pixmap, but the bitmap setting takes void*, hence the cast - void* readOnlyAddr = const_cast(this->addr()); - if (!bitmap.installPixels(this->info(), readOnlyAddr, this->rowBytes())) { + if (!bitmap.installPixels(*this)) { return false; } bitmap.setIsVolatile(true); // so we don't try to cache it diff --git a/gfx/skia/skia/src/core/SkPx.h b/gfx/skia/skia/src/core/SkPx.h deleted file mode 100644 index 62ec598cd6..0000000000 --- a/gfx/skia/skia/src/core/SkPx.h +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright 2015 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#ifndef SkPx_DEFINED -#define SkPx_DEFINED - -#include "SkTypes.h" -#include "SkColorPriv.h" - -// We'll include one of src/opts/SkPx_{sse,neon,none}.h to define a type SkPx. -// -// SkPx represents up to SkPx::N 8888 pixels. It's agnostic to whether these -// are SkColors or SkPMColors; it only assumes that alpha is the high byte. -static_assert(SK_A32_SHIFT == 24, "For both SkColor and SkPMColor, alpha is always the high byte."); -// -// SkPx::Alpha represents up to SkPx::N 8-bit values, usually coverage or alpha. -// SkPx::Wide represents up to SkPx::N pixels with 16 bits per component. -// -// SkPx supports the following methods: -// static SkPx Dup(uint32_t); -// static SkPx Load(const uint32_t*); -// static SkPx Load(const uint32_t*, int n); // where 0 a -// Wide widenLo() const; // argb -> 0a0r0g0b -// Wide widenHi() const; // argb -> a0r0g0b0 -// Wide widenLoHi() const; // argb -> aarrggbb -// -// SkPx operator+(const SkPx&) const; -// SkPx operator-(const SkPx&) const; -// SkPx saturatedAdd(const SkPx&) const; -// -// Wide operator*(const Alpha&) const; // argb * A -> (a*A)(r*A)(g*A)(b*A) -// -// // Fast approximate (px*a+127)/255. -// // Never off by more than 1, and always correct when px or a is 0 or 255. -// // We use the approximation (px*a+px)/256. -// SkPx approxMulDiv255(const Alpha&) const; -// -// SkPx addAlpha(const Alpha&) const; // argb + A -> (a+A)rgb -// -// SkPx::Alpha supports the following methods: -// static Alpha Dup(uint8_t); -// static Alpha Load(const uint8_t*); -// static Alpha Load(const uint8_t*, int n); // where 0 255-a -// -// SkPx::Wide supports the following methods: -// Wide operator+(const Wide&); -// Wide operator-(const Wide&); -// Wide shl(); -// Wide shr(); -// -// // Return the high byte of each component of (*this + o.widenLo()). -// SkPx addNarrowHi(const SkPx& o); -// -// Methods left unwritten, but certainly to come: -// SkPx SkPx::operator<(const SkPx&) const; -// SkPx SkPx::thenElse(const SkPx& then, const SkPx& else) const; -// Wide Wide::operator<(const Wide&) const; -// Wide Wide::thenElse(const Wide& then, const Wide& else) const; -// -// SkPx Wide::div255() const; // Rounds, think (*this + 127) / 255. -// -// The different implementations of SkPx have complete freedom to choose -// SkPx::N and how they represent SkPx, SkPx::Alpha, and SkPx::Wide. -// -// All observable math must remain identical. - -#if defined(SKNX_NO_SIMD) - #include "../opts/SkPx_none.h" -#else - #if SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE2 - #include "../opts/SkPx_sse.h" - #elif defined(SK_ARM_HAS_NEON) - #include "../opts/SkPx_neon.h" - #else - #include "../opts/SkPx_none.h" - #endif -#endif - -#endif//SkPx_DEFINED diff --git a/gfx/skia/skia/src/core/SkRRect.cpp b/gfx/skia/skia/src/core/SkRRect.cpp index ad62e5bbae..ca4fd56152 100644 --- a/gfx/skia/skia/src/core/SkRRect.cpp +++ b/gfx/skia/skia/src/core/SkRRect.cpp @@ -5,8 +5,10 @@ * found in the LICENSE file. */ +#include #include "SkRRect.h" #include "SkMatrix.h" +#include "SkScaleToSides.h" /////////////////////////////////////////////////////////////////////////////// @@ -109,28 +111,6 @@ void SkRRect::setNinePatch(const SkRect& rect, SkScalar leftRad, SkScalar topRad SkDEBUGCODE(this->validate();) } -/* - * TODO: clean this guy up and possibly add to SkScalar.h - */ -static inline SkScalar SkScalarDecULP(SkScalar value) { -#if SK_SCALAR_IS_FLOAT - return SkBits2Float(SkFloat2Bits(value) - 1); -#else - #error "need impl for doubles" -#endif -} - - /** - * We need all combinations of predicates to be true to have a "safe" radius value. - */ -static SkScalar clamp_radius_check_predicates(SkScalar rad, SkScalar min, SkScalar max) { - SkASSERT(min < max); - if (rad > max - min || min + rad > max || max - rad < min) { - rad = SkScalarDecULP(rad); - } - return rad; -} - // These parameters intentionally double. Apropos crbug.com/463920, if one of the // radii is huge while the other is small, single precision math can completely // miss the fact that a scale is required. @@ -190,29 +170,21 @@ void SkRRect::setRectRadii(const SkRect& rect, const SkVector radii[4]) { // If f < 1, then all corner radii are reduced by multiplying them by f." double scale = 1.0; - scale = compute_min_scale(fRadii[0].fX, fRadii[1].fX, fRect.width(), scale); - scale = compute_min_scale(fRadii[1].fY, fRadii[2].fY, fRect.height(), scale); - scale = compute_min_scale(fRadii[2].fX, fRadii[3].fX, fRect.width(), scale); - scale = compute_min_scale(fRadii[3].fY, fRadii[0].fY, fRect.height(), scale); + // The sides of the rectangle may be larger than a float. + double width = (double)fRect.fRight - (double)fRect.fLeft; + double height = (double)fRect.fBottom - (double)fRect.fTop; + scale = compute_min_scale(fRadii[0].fX, fRadii[1].fX, width, scale); + scale = compute_min_scale(fRadii[1].fY, fRadii[2].fY, height, scale); + scale = compute_min_scale(fRadii[2].fX, fRadii[3].fX, width, scale); + scale = compute_min_scale(fRadii[3].fY, fRadii[0].fY, height, scale); if (scale < 1.0) { - for (int i = 0; i < 4; ++i) { - fRadii[i].fX *= scale; - fRadii[i].fY *= scale; - } + ScaleToSides::AdjustRadii(width, scale, &fRadii[0].fX, &fRadii[1].fX); + ScaleToSides::AdjustRadii(height, scale, &fRadii[1].fY, &fRadii[2].fY); + ScaleToSides::AdjustRadii(width, scale, &fRadii[2].fX, &fRadii[3].fX); + ScaleToSides::AdjustRadii(height, scale, &fRadii[3].fY, &fRadii[0].fY); } - // https://bug.skia.org/3239 -- its possible that we can hit the following inconsistency: - // rad == bounds.bottom - bounds.top - // bounds.bottom - radius < bounds.top - // YIKES - // We need to detect and "fix" this now, otherwise we can have the following wackiness: - // path.addRRect(rrect); - // rrect.rect() != path.getBounds() - for (int i = 0; i < 4; ++i) { - fRadii[i].fX = clamp_radius_check_predicates(fRadii[i].fX, fRect.fLeft, fRect.fRight); - fRadii[i].fY = clamp_radius_check_predicates(fRadii[i].fY, fRect.fTop, fRect.fBottom); - } // At this point we're either oval, simple, or complex (not empty or rect). this->computeType(); diff --git a/gfx/skia/skia/src/core/SkRWBuffer.cpp b/gfx/skia/skia/src/core/SkRWBuffer.cpp index e9147dc684..0784378d99 100644 --- a/gfx/skia/skia/src/core/SkRWBuffer.cpp +++ b/gfx/skia/skia/src/core/SkRWBuffer.cpp @@ -150,6 +150,9 @@ const void* SkROBuffer::Iter::data() const { } size_t SkROBuffer::Iter::size() const { + if (!fBlock) { + return 0; + } return SkTMin(fBlock->fUsed, fRemaining); } diff --git a/gfx/skia/skia/src/core/SkRasterizer.cpp b/gfx/skia/skia/src/core/SkRasterizer.cpp index 87b9caa242..6a3ea10678 100644 --- a/gfx/skia/skia/src/core/SkRasterizer.cpp +++ b/gfx/skia/skia/src/core/SkRasterizer.cpp @@ -23,7 +23,6 @@ bool SkRasterizer::rasterize(const SkPath& fillPath, const SkMatrix& matrix, srcM.fFormat = SkMask::kA8_Format; srcM.fBounds.set(0, 0, 1, 1); - srcM.fImage = nullptr; if (!filter->filterMask(&dstM, srcM, matrix, &margin)) { return false; } diff --git a/gfx/skia/skia/src/core/SkRecord.cpp b/gfx/skia/skia/src/core/SkRecord.cpp index 2e03e993b8..ca0a8cdde6 100644 --- a/gfx/skia/skia/src/core/SkRecord.cpp +++ b/gfx/skia/skia/src/core/SkRecord.cpp @@ -6,7 +6,6 @@ */ #include "SkRecord.h" -#include SkRecord::~SkRecord() { Destroyer destroyer; diff --git a/gfx/skia/skia/src/core/SkRecord.h b/gfx/skia/skia/src/core/SkRecord.h index 8901d62e91..866d1837ad 100644 --- a/gfx/skia/skia/src/core/SkRecord.h +++ b/gfx/skia/skia/src/core/SkRecord.h @@ -138,13 +138,13 @@ private: }; template - SK_WHEN(skstd::is_empty::value, T*) allocCommand() { + SK_WHEN(std::is_empty::value, T*) allocCommand() { static T singleton = {}; return &singleton; } template - SK_WHEN(!skstd::is_empty::value, T*) allocCommand() { return this->alloc(); } + SK_WHEN(!std::is_empty::value, T*) allocCommand() { return this->alloc(); } void grow(); diff --git a/gfx/skia/skia/src/core/SkRecordDraw.cpp b/gfx/skia/skia/src/core/SkRecordDraw.cpp index 4847273f52..5ca9517d3f 100644 --- a/gfx/skia/skia/src/core/SkRecordDraw.cpp +++ b/gfx/skia/skia/src/core/SkRecordDraw.cpp @@ -78,7 +78,7 @@ template <> void Draw::draw(const NoOp&) {} #define DRAW(T, call) template <> void Draw::draw(const T& r) { fCanvas->call; } DRAW(Restore, restore()); DRAW(Save, save()); -DRAW(SaveLayer, saveLayer(r.bounds, r.paint, r.flags)); +DRAW(SaveLayer, saveLayer(SkCanvas::SaveLayerRec(r.bounds, r.paint, r.backdrop, r.saveLayerFlags))); DRAW(SetMatrix, setMatrix(SkMatrix::Concat(fInitialCTM, r.matrix))); DRAW(Concat, concat(r.matrix)); @@ -111,7 +111,6 @@ DRAW(DrawPosText, drawPosText(r.text, r.byteLength, r.pos, r.paint)); DRAW(DrawPosTextH, drawPosTextH(r.text, r.byteLength, r.xpos, r.y, r.paint)); DRAW(DrawRRect, drawRRect(r.rrect, r.paint)); DRAW(DrawRect, drawRect(r.rect, r.paint)); -DRAW(DrawSprite, drawSprite(r.bitmap.shallowCopy(), r.left, r.top, r.paint)); DRAW(DrawText, drawText(r.text, r.byteLength, r.x, r.y, r.paint)); DRAW(DrawTextBlob, drawTextBlob(r.blob, r.x, r.y, r.paint)); DRAW(DrawTextOnPath, drawTextOnPath(r.text, r.byteLength, r.path, &r.matrix, r.paint)); @@ -221,6 +220,7 @@ private: int controlOps; // Number of control ops in this Save block, including the Save. Bounds bounds; // Bounds of everything in the block. const SkPaint* paint; // Unowned. If set, adjusts the bounds of all ops in this block. + SkMatrix ctm; }; // Only Restore, SetMatrix, and Concat change the CTM. @@ -301,6 +301,7 @@ private: sb.bounds = PaintMayAffectTransparentBlack(paint) ? fCurrentClipBounds : Bounds::MakeEmpty(); sb.paint = paint; + sb.ctm = this->fCTM; fSaveStack.push(sb); this->pushControl(); @@ -387,14 +388,6 @@ private: Bounds bounds(const DrawPaint&) const { return fCurrentClipBounds; } Bounds bounds(const NoOp&) const { return Bounds::MakeEmpty(); } // NoOps don't draw. - Bounds bounds(const DrawSprite& op) const { // Ignores the matrix, but respects the clip. - SkRect rect = Bounds::MakeXYWH(op.left, op.top, op.bitmap.width(), op.bitmap.height()); - if (!rect.intersect(fCurrentClipBounds)) { - return Bounds::MakeEmpty(); - } - return rect; - } - Bounds bounds(const DrawRect& op) const { return this->adjustAndMap(op.rect, &op.paint); } Bounds bounds(const DrawOval& op) const { return this->adjustAndMap(op.oval, &op.paint); } Bounds bounds(const DrawRRect& op) const { @@ -563,9 +556,15 @@ private: bool adjustForSaveLayerPaints(SkRect* rect, int savesToIgnore = 0) const { for (int i = fSaveStack.count() - 1 - savesToIgnore; i >= 0; i--) { + SkMatrix inverse; + if (!fSaveStack[i].ctm.invert(&inverse)) { + return false; + } + inverse.mapRect(rect); if (!AdjustForPaint(fSaveStack[i].paint, rect)) { return false; } + fSaveStack[i].ctm.mapRect(rect); } return true; } @@ -693,7 +692,8 @@ private: // Store 'saveLayer ops from enclosing picture' + drawPict op + 'ops from sub-picture' dst.fKeySize = fSaveLayerOpStack.count() + src.fKeySize + 1; dst.fKey = new int[dst.fKeySize]; - memcpy(dst.fKey, fSaveLayerOpStack.begin(), fSaveLayerOpStack.count() * sizeof(int)); + sk_careful_memcpy(dst.fKey, fSaveLayerOpStack.begin(), + fSaveLayerOpStack.count() * sizeof(int)); dst.fKey[fSaveLayerOpStack.count()] = fFillBounds.currentOp(); memcpy(&dst.fKey[fSaveLayerOpStack.count()+1], src.fKey, src.fKeySize * sizeof(int)); } diff --git a/gfx/skia/skia/src/core/SkRecordOpts.cpp b/gfx/skia/skia/src/core/SkRecordOpts.cpp index d1520adf56..0121ea54fc 100644 --- a/gfx/skia/skia/src/core/SkRecordOpts.cpp +++ b/gfx/skia/skia/src/core/SkRecordOpts.cpp @@ -177,6 +177,11 @@ struct SaveLayerDrawRestoreNooper { typedef Pattern, IsDraw, Is> Match; bool onMatch(SkRecord* record, Match* match, int begin, int end) { + if (match->first()->backdrop) { + // can't throw away the layer if we have a backdrop + return false; + } + // A SaveLayer's bounds field is just a hint, so we should be free to ignore it. SkPaint* layerPaint = match->first()->paint; if (nullptr == layerPaint) { @@ -224,6 +229,11 @@ struct SvgOpacityAndFilterLayerMergePass { Is, Is, Is> Match; bool onMatch(SkRecord* record, Match* match, int begin, int end) { + if (match->first()->backdrop) { + // can't throw away the layer if we have a backdrop + return false; + } + SkPaint* opacityPaint = match->first()->paint; if (nullptr == opacityPaint) { // There wasn't really any point to this SaveLayer at all. diff --git a/gfx/skia/skia/src/core/SkRecorder.cpp b/gfx/skia/skia/src/core/SkRecorder.cpp index d90b2c025a..01c28dfc2b 100644 --- a/gfx/skia/skia/src/core/SkRecorder.cpp +++ b/gfx/skia/skia/src/core/SkRecorder.cpp @@ -240,10 +240,6 @@ void SkRecorder::onDrawImageNine(const SkImage* image, const SkIRect& center, APPEND(DrawImageNine, this->copy(paint), image, center, dst); } -void SkRecorder::onDrawSprite(const SkBitmap& bitmap, int left, int top, const SkPaint* paint) { - APPEND(DrawSprite, this->copy(paint), bitmap, left, top); -} - void SkRecorder::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y, const SkPaint& paint) { APPEND(DrawText, @@ -340,10 +336,9 @@ void SkRecorder::willSave() { APPEND(Save); } -SkCanvas::SaveLayerStrategy SkRecorder::willSaveLayer(const SkRect* bounds, - const SkPaint* paint, - SkCanvas::SaveFlags flags) { - APPEND(SaveLayer, this->copy(bounds), this->copy(paint), flags); +SkCanvas::SaveLayerStrategy SkRecorder::getSaveLayerStrategy(const SaveLayerRec& rec) { + APPEND(SaveLayer, + this->copy(rec.fBounds), this->copy(rec.fPaint), rec.fBackdrop, rec.fSaveLayerFlags); return SkCanvas::kNoLayer_SaveLayerStrategy; } diff --git a/gfx/skia/skia/src/core/SkRecorder.h b/gfx/skia/skia/src/core/SkRecorder.h index 92197a0a3c..cd5bc8ad04 100644 --- a/gfx/skia/skia/src/core/SkRecorder.h +++ b/gfx/skia/skia/src/core/SkRecorder.h @@ -54,7 +54,7 @@ public: void forgetRecord(); void willSave() override; - SaveLayerStrategy willSaveLayer(const SkRect*, const SkPaint*, SkCanvas::SaveFlags) override; + SaveLayerStrategy getSaveLayerStrategy(const SaveLayerRec&) override; void willRestore() override {} void didRestore() override; @@ -106,7 +106,6 @@ public: const SkPaint*) override; void onDrawBitmapNine(const SkBitmap&, const SkIRect& center, const SkRect& dst, const SkPaint*) override; - void onDrawSprite(const SkBitmap&, int left, int top, const SkPaint*) override; void onDrawVertices(VertexMode vmode, int vertexCount, const SkPoint vertices[], const SkPoint texs[], const SkColor colors[], SkXfermode* xmode, diff --git a/gfx/skia/skia/src/core/SkRemote.cpp b/gfx/skia/skia/src/core/SkRemote.cpp index 1209be014c..22185cddb0 100644 --- a/gfx/skia/skia/src/core/SkRemote.cpp +++ b/gfx/skia/skia/src/core/SkRemote.cpp @@ -163,18 +163,17 @@ namespace SkRemote { void willSave() override { fEncoder-> save(); } void didRestore() override { fEncoder->restore(); } - SaveLayerStrategy willSaveLayer(const SkRect* bounds, - const SkPaint* paint, - SaveFlags flags) override { + SaveLayerStrategy getSaveLayerStrategy(const SaveLayerRec& rec) override { SkPath path; - if (bounds) { - path.addRect(*bounds); + if (rec.fBounds) { + path.addRect(*rec.fBounds); } const SkPaint defaultPaint; + const SkPaint* paint = rec.fPaint; if (!paint) { paint = &defaultPaint; } - fEncoder->saveLayer(this->id(path), this->commonIDs(*paint), flags); + fEncoder->saveLayer(this->id(path), this->commonIDs(*paint), rec.fSaveLayerFlags); return kNoLayer_SaveLayerStrategy; } @@ -500,11 +499,13 @@ namespace SkRemote { void save() override { fCanvas->save(); } void restore() override { fCanvas->restore(); } - void saveLayer(ID bounds, CommonIDs common, SkCanvas::SaveFlags flags) override { + void saveLayer(ID bounds, CommonIDs common, SkCanvas::SaveLayerFlags flags) override { SkPaint paint; this->applyCommon(common, &paint); SkRect rect; - fCanvas->saveLayer(fPath.find(bounds).isRect(&rect) ? &rect : nullptr, &paint, flags); + + fCanvas->saveLayer({ fPath.find(bounds).isRect(&rect) ? &rect : nullptr, + &paint, flags }); } void setMatrix(ID matrix) override { fCanvas->setMatrix(fMatrix.find(matrix)); } @@ -684,7 +685,7 @@ namespace SkRemote { void save() override { fWrapped-> save(); } void restore() override { fWrapped->restore(); } - void saveLayer(ID bounds, CommonIDs common, SkCanvas::SaveFlags flags) override { + void saveLayer(ID bounds, CommonIDs common, SkCanvas::SaveLayerFlags flags) override { fWrapped->saveLayer(bounds, common, flags); } diff --git a/gfx/skia/skia/src/core/SkRemote.h b/gfx/skia/skia/src/core/SkRemote.h index 924e63e220..a1b1405b21 100644 --- a/gfx/skia/skia/src/core/SkRemote.h +++ b/gfx/skia/skia/src/core/SkRemote.h @@ -91,7 +91,7 @@ namespace SkRemote { virtual void save() = 0; virtual void restore() = 0; - virtual void saveLayer(ID bounds, CommonIDs, SkCanvas::SaveFlags) = 0; + virtual void saveLayer(ID bounds, CommonIDs, uint32_t saveLayerFlags) = 0; virtual void setMatrix(ID matrix) = 0; diff --git a/gfx/skia/skia/src/core/SkScaleToSides.h b/gfx/skia/skia/src/core/SkScaleToSides.h new file mode 100644 index 0000000000..77637a3ac2 --- /dev/null +++ b/gfx/skia/skia/src/core/SkScaleToSides.h @@ -0,0 +1,61 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkScaleToSides_DEFINED +#define SkScaleToSides_DEFINED + +#include +#include "SkScalar.h" +#include "SkTypes.h" + +class ScaleToSides { +public: + // This code assumes that a and b fit in in a float, and therefore the resulting smaller value + // of a and b will fit in a float. The side of the rectangle may be larger than a float. + // Scale must be less than or equal to the ratio limit / (*a + *b). + // This code assumes that NaN and Inf are never passed in. + static void AdjustRadii(double limit, double scale, SkScalar* a, SkScalar* b) { + SkASSERTF(scale < 1.0 && scale > 0.0, "scale: %g", scale); + + *a = (float)((double)*a * scale); + *b = (float)((double)*b * scale); + + // This check is conservative. (double)*a + (double)*b >= (double)(*a + *b) + if ((double)*a + (double)*b > limit) { + float* minRadius = a; + float* maxRadius = b; + + // Force minRadius to be the smaller of the two. + if (*minRadius > *maxRadius) { + SkTSwap(minRadius, maxRadius); + } + + // newMinRadius must be float in order to give the actual value of the radius. + // The newMinRadius will always be smaller than limit. The largest that minRadius can be + // is 1/2 the ratio of minRadius : (minRadius + maxRadius), therefore in the resulting + // division, minRadius can be no larger than 1/2 limit + ULP. + float newMinRadius = *minRadius; + + // Because newMaxRadius is the result of a double to float conversion, it can be larger + // than limit, but only by one ULP. + float newMaxRadius = (float)(limit - newMinRadius); + + // If newMaxRadius forces the total over the limit, then it needs to be + // reduced by one ULP to be less than limit - newMinRadius. + // Note: nexttowardf is a c99 call and should be std::nexttoward, but this is not + // implemented in the ARM compiler. + if ((double)newMaxRadius + (double)newMinRadius > limit) { + newMaxRadius = nexttowardf(newMaxRadius, 0.0); + } + *maxRadius = newMaxRadius; + } + + SkASSERTF(*a >= 0.0f && *b >= 0.0f, "a: %g, b: %g", *a, *b); + SkASSERTF((*a + *b) <= limit, "limit: %g, a: %g, b: %g", limit, *a, *b); + } +}; +#endif // ScaleToSides_DEFINED diff --git a/gfx/skia/skia/src/core/SkScan.h b/gfx/skia/skia/src/core/SkScan.h index ef44873569..7309a1dd8c 100644 --- a/gfx/skia/skia/src/core/SkScan.h +++ b/gfx/skia/skia/src/core/SkScan.h @@ -56,6 +56,10 @@ public: static void AntiHairRect(const SkRect&, const SkRasterClip&, SkBlitter*); static void HairPath(const SkPath&, const SkRasterClip&, SkBlitter*); static void AntiHairPath(const SkPath&, const SkRasterClip&, SkBlitter*); + static void HairSquarePath(const SkPath&, const SkRasterClip&, SkBlitter*); + static void AntiHairSquarePath(const SkPath&, const SkRasterClip&, SkBlitter*); + static void HairRoundPath(const SkPath&, const SkRasterClip&, SkBlitter*); + static void AntiHairRoundPath(const SkPath&, const SkRasterClip&, SkBlitter*); private: friend class SkAAClip; diff --git a/gfx/skia/skia/src/core/SkScan_AntiPath.cpp b/gfx/skia/skia/src/core/SkScan_AntiPath.cpp index 6ea6b8b07e..22bca7a55c 100644 --- a/gfx/skia/skia/src/core/SkScan_AntiPath.cpp +++ b/gfx/skia/skia/src/core/SkScan_AntiPath.cpp @@ -91,11 +91,11 @@ BaseSuperBlitter::BaseSuperBlitter(SkBlitter* realBlit, const SkIRect& ir, const const int right = sectBounds.right(); fLeft = left; - fSuperLeft = left << SHIFT; + fSuperLeft = SkLeftShift(left, SHIFT); fWidth = right - left; fTop = sectBounds.top(); fCurrIY = fTop - 1; - fCurrY = (fTop << SHIFT) - 1; + fCurrY = SkLeftShift(fTop, SHIFT) - 1; SkDEBUGCODE(fCurrX = -1;) } @@ -545,7 +545,7 @@ void MaskSuperBlitter::blitH(int x, int y, int width) { } #endif - x -= (fMask.fBounds.fLeft << SHIFT); + x -= SkLeftShift(fMask.fBounds.fLeft, SHIFT); // hack, until I figure out why my cubics (I think) go beyond the bounds if (x < 0) { @@ -592,7 +592,7 @@ static bool fitsInsideLimit(const SkRect& r, SkScalar max) { static int overflows_short_shift(int value, int shift) { const int s = 16 + shift; - return (value << s >> s) - value; + return (SkLeftShift(value, s) >> s) - value; } /** diff --git a/gfx/skia/skia/src/core/SkScan_Antihair.cpp b/gfx/skia/skia/src/core/SkScan_Antihair.cpp index b14e12bab8..8ee0ba5f98 100644 --- a/gfx/skia/skia/src/core/SkScan_Antihair.cpp +++ b/gfx/skia/skia/src/core/SkScan_Antihair.cpp @@ -250,9 +250,9 @@ public: }; static inline SkFixed fastfixdiv(SkFDot6 a, SkFDot6 b) { - SkASSERT((a << 16 >> 16) == a); + SkASSERT((SkLeftShift(a, 16) >> 16) == a); SkASSERT(b != 0); - return (a << 16) / b; + return SkLeftShift(a, 16) / b; } #define SkBITCOUNT(x) (sizeof(x) << 3) diff --git a/gfx/skia/skia/src/core/SkScan_Hairline.cpp b/gfx/skia/skia/src/core/SkScan_Hairline.cpp index 7a6e3ba187..12553a4a54 100644 --- a/gfx/skia/skia/src/core/SkScan_Hairline.cpp +++ b/gfx/skia/skia/src/core/SkScan_Hairline.cpp @@ -318,8 +318,69 @@ static void hair_cubic(const SkPoint pts[4], const SkRegion* clip, SkBlitter* bl lineproc(tmp, lines + 1, clip, blitter); } -static inline void haircubic(const SkPoint pts[4], const SkRegion* clip, +static SkRect compute_nocheck_cubic_bounds(const SkPoint pts[4]) { + SkASSERT(SkScalarsAreFinite(&pts[0].fX, 8)); + + Sk2s min = Sk2s::Load(&pts[0].fX); + Sk2s max = min; + for (int i = 1; i < 4; ++i) { + Sk2s pair = Sk2s::Load(&pts[i].fX); + min = Sk2s::Min(min, pair); + max = Sk2s::Max(max, pair); + } + return { min.kth<0>(), min.kth<1>(), max.kth<0>(), max.kth<1>() }; +} + +static bool is_inverted(const SkRect& r) { + return r.fLeft > r.fRight || r.fTop > r.fBottom; +} + +// Can't call SkRect::intersects, since it cares about empty, and we don't (since we tracking +// something to be stroked, so empty can still draw something (e.g. horizontal line) +static bool geometric_overlap(const SkRect& a, const SkRect& b) { + SkASSERT(!is_inverted(a) && !is_inverted(b)); + return a.fLeft < b.fRight && b.fLeft < a.fRight && + a.fTop < b.fBottom && b.fTop < a.fBottom; +} + +// Can't call SkRect::contains, since it cares about empty, and we don't (since we tracking +// something to be stroked, so empty can still draw something (e.g. horizontal line) +static bool geometric_contains(const SkRect& outer, const SkRect& inner) { + SkASSERT(!is_inverted(outer) && !is_inverted(inner)); + return inner.fRight <= outer.fRight && inner.fLeft >= outer.fLeft && + inner.fBottom <= outer.fBottom && inner.fTop >= outer.fTop; +} + +//#define SK_SHOW_HAIRCLIP_STATS +#ifdef SK_SHOW_HAIRCLIP_STATS +static int gKillClip, gRejectClip, gClipCount; +#endif + +static inline void haircubic(const SkPoint pts[4], const SkRegion* clip, const SkRect* insetClip, const SkRect* outsetClip, SkBlitter* blitter, int level, SkScan::HairRgnProc lineproc) { + if (insetClip) { + SkASSERT(outsetClip); +#ifdef SK_SHOW_HAIRCLIP_STATS + gClipCount += 1; +#endif + SkRect bounds = compute_nocheck_cubic_bounds(pts); + if (!geometric_overlap(*outsetClip, bounds)) { +#ifdef SK_SHOW_HAIRCLIP_STATS + gRejectClip += 1; +#endif + return; + } else if (geometric_contains(*insetClip, bounds)) { + clip = nullptr; +#ifdef SK_SHOW_HAIRCLIP_STATS + gKillClip += 1; +#endif + } +#ifdef SK_SHOW_HAIRCLIP_STATS + if (0 == gClipCount % 256) + SkDebugf("kill %g reject %g total %d\n", 1.0*gKillClip / gClipCount, 1.0*gRejectClip/gClipCount, gClipCount); +#endif + } + if (quick_cubic_niceness_check(pts)) { hair_cubic(pts, clip, blitter, lineproc); } else { @@ -348,7 +409,59 @@ static int compute_quad_level(const SkPoint pts[3]) { return level; } -static void hair_path(const SkPath& path, const SkRasterClip& rclip, SkBlitter* blitter, +/* Extend the points in the direction of the starting or ending tangent by 1/2 unit to + account for a round or square cap. If there's no distance between the end point and + the control point, use the next control point to create a tangent. If the curve + is degenerate, move the cap out 1/2 unit horizontally. */ +template +void extend_pts(SkPath::Verb prevVerb, SkPath::Verb nextVerb, SkPoint* pts, int ptCount) { + SkASSERT(SkPaint::kSquare_Cap == capStyle || SkPaint::kRound_Cap == capStyle); + // The area of a circle is PI*R*R. For a unit circle, R=1/2, and the cap covers half of that. + const SkScalar capOutset = SkPaint::kSquare_Cap == capStyle ? 0.5f : SK_ScalarPI / 8; + if (SkPath::kMove_Verb == prevVerb) { + SkPoint* first = pts; + SkPoint* ctrl = first; + int controls = ptCount - 1; + SkVector tangent; + do { + tangent = *first - *++ctrl; + } while (tangent.isZero() && --controls > 0); + if (tangent.isZero()) { + tangent.set(1, 0); + controls = ptCount - 1; // If all points are equal, move all but one + } else { + tangent.normalize(); + } + do { // If the end point and control points are equal, loop to move them in tandem. + first->fX += tangent.fX * capOutset; + first->fY += tangent.fY * capOutset; + ++first; + } while (++controls < ptCount); + } + if (SkPath::kMove_Verb == nextVerb || SkPath::kDone_Verb == nextVerb) { + SkPoint* last = &pts[ptCount - 1]; + SkPoint* ctrl = last; + int controls = ptCount - 1; + SkVector tangent; + do { + tangent = *last - *--ctrl; + } while (tangent.isZero() && --controls > 0); + if (tangent.isZero()) { + tangent.set(-1, 0); + controls = ptCount - 1; + } else { + tangent.normalize(); + } + do { + last->fX += tangent.fX * capOutset; + last->fY += tangent.fY * capOutset; + --last; + } while (++controls < ptCount); + } +} + +template +void hair_path(const SkPath& path, const SkRasterClip& rclip, SkBlitter* blitter, SkScan::HairRgnProc lineproc) { if (path.isEmpty()) { return; @@ -356,10 +469,13 @@ static void hair_path(const SkPath& path, const SkRasterClip& rclip, SkBlitter* SkAAClipBlitterWrapper wrap; const SkRegion* clip = nullptr; + SkRect insetStorage, outsetStorage; + const SkRect* insetClip = nullptr; + const SkRect* outsetClip = nullptr; { - const SkIRect ibounds = path.getBounds().roundOut().makeOutset(1, 1); - + const int capOut = SkPaint::kButt_Cap == capStyle ? 1 : 2; + const SkIRect ibounds = path.getBounds().roundOut().makeOutset(capOut, capOut); if (rclip.quickReject(ibounds)) { return; } @@ -371,25 +487,69 @@ static void hair_path(const SkPath& path, const SkRasterClip& rclip, SkBlitter* blitter = wrap.getBlitter(); clip = &wrap.getRgn(); } + + /* + * We now cache two scalar rects, to use for culling per-segment (e.g. cubic). + * Since we're hairlining, the "bounds" of the control points isn't necessairly the + * limit of where a segment can draw (it might draw up to 1 pixel beyond in aa-hairs). + * + * Compute the pt-bounds per segment is easy, so we do that, and then inversely adjust + * the culling bounds so we can just do a straight compare per segment. + * + * insetClip is use for quick-accept (i.e. the segment is not clipped), so we inset + * it from the clip-bounds (since segment bounds can be off by 1). + * + * outsetClip is used for quick-reject (i.e. the segment is entirely outside), so we + * outset it from the clip-bounds. + */ + insetStorage.set(clip->getBounds()); + outsetStorage = insetStorage.makeOutset(1, 1); + insetStorage.inset(1, 1); + if (is_inverted(insetStorage)) { + /* + * our bounds checks assume the rects are never inverted. If insetting has + * created that, we assume that the area is too small to safely perform a + * quick-accept, so we just mark the rect as empty (so the quick-accept check + * will always fail. + */ + insetStorage.setEmpty(); // just so we don't pass an inverted rect + } + insetClip = &insetStorage; + outsetClip = &outsetStorage; } } - SkPath::Iter iter(path, false); - SkPoint pts[4]; - SkPath::Verb verb; - SkAutoConicToQuads converter; + SkPath::RawIter iter(path); + SkPoint pts[4], firstPt, lastPt; + SkPath::Verb verb, prevVerb; + SkAutoConicToQuads converter; - while ((verb = iter.next(pts, false)) != SkPath::kDone_Verb) { + if (SkPaint::kButt_Cap != capStyle) { + prevVerb = SkPath::kDone_Verb; + } + while ((verb = iter.next(pts)) != SkPath::kDone_Verb) { switch (verb) { case SkPath::kMove_Verb: + firstPt = lastPt = pts[0]; break; case SkPath::kLine_Verb: + if (SkPaint::kButt_Cap != capStyle) { + extend_pts(prevVerb, iter.peek(), pts, 2); + } lineproc(pts, 2, clip, blitter); + lastPt = pts[1]; break; case SkPath::kQuad_Verb: + if (SkPaint::kButt_Cap != capStyle) { + extend_pts(prevVerb, iter.peek(), pts, 3); + } hairquad(pts, clip, blitter, compute_quad_level(pts), lineproc); + lastPt = pts[2]; break; case SkPath::kConic_Verb: { + if (SkPaint::kButt_Cap != capStyle) { + extend_pts(prevVerb, iter.peek(), pts, 3); + } // how close should the quads be to the original conic? const SkScalar tol = SK_Scalar1 / 4; const SkPoint* quadPts = converter.computeQuads(pts, @@ -399,25 +559,56 @@ static void hair_path(const SkPath& path, const SkRasterClip& rclip, SkBlitter* hairquad(quadPts, clip, blitter, level, lineproc); quadPts += 2; } + lastPt = pts[2]; break; } case SkPath::kCubic_Verb: { - haircubic(pts, clip, blitter, kMaxCubicSubdivideLevel, lineproc); + if (SkPaint::kButt_Cap != capStyle) { + extend_pts(prevVerb, iter.peek(), pts, 4); + } + haircubic(pts, clip, insetClip, outsetClip, blitter, kMaxCubicSubdivideLevel, lineproc); + lastPt = pts[3]; } break; case SkPath::kClose_Verb: + pts[0] = lastPt; + pts[1] = firstPt; + if (SkPaint::kButt_Cap != capStyle && prevVerb == SkPath::kMove_Verb) { + // cap moveTo/close to match svg expectations for degenerate segments + extend_pts(prevVerb, iter.peek(), pts, 2); + } + lineproc(pts, 2, clip, blitter); break; case SkPath::kDone_Verb: break; } + if (SkPaint::kButt_Cap != capStyle) { + prevVerb = verb; + } } } void SkScan::HairPath(const SkPath& path, const SkRasterClip& clip, SkBlitter* blitter) { - hair_path(path, clip, blitter, SkScan::HairLineRgn); + hair_path(path, clip, blitter, SkScan::HairLineRgn); } void SkScan::AntiHairPath(const SkPath& path, const SkRasterClip& clip, SkBlitter* blitter) { - hair_path(path, clip, blitter, SkScan::AntiHairLineRgn); + hair_path(path, clip, blitter, SkScan::AntiHairLineRgn); +} + +void SkScan::HairSquarePath(const SkPath& path, const SkRasterClip& clip, SkBlitter* blitter) { + hair_path(path, clip, blitter, SkScan::HairLineRgn); +} + +void SkScan::AntiHairSquarePath(const SkPath& path, const SkRasterClip& clip, SkBlitter* blitter) { + hair_path(path, clip, blitter, SkScan::AntiHairLineRgn); +} + +void SkScan::HairRoundPath(const SkPath& path, const SkRasterClip& clip, SkBlitter* blitter) { + hair_path(path, clip, blitter, SkScan::HairLineRgn); +} + +void SkScan::AntiHairRoundPath(const SkPath& path, const SkRasterClip& clip, SkBlitter* blitter) { + hair_path(path, clip, blitter, SkScan::AntiHairLineRgn); } /////////////////////////////////////////////////////////////////////////////// diff --git a/gfx/skia/skia/src/core/SkScan_Path.cpp b/gfx/skia/skia/src/core/SkScan_Path.cpp index fc79fc8537..804148db36 100644 --- a/gfx/skia/skia/src/core/SkScan_Path.cpp +++ b/gfx/skia/skia/src/core/SkScan_Path.cpp @@ -450,8 +450,8 @@ void sk_fill_path(const SkPath& path, const SkIRect* clipRect, SkBlitter* blitte // now edge is the head of the sorted linklist - start_y <<= shiftEdgesUp; - stop_y <<= shiftEdgesUp; + start_y = SkLeftShift(start_y, shiftEdgesUp); + stop_y = SkLeftShift(stop_y, shiftEdgesUp); if (clipRect && start_y < clipRect->fTop) { start_y = clipRect->fTop; } @@ -559,6 +559,42 @@ static bool clip_to_limit(const SkRegion& orig, SkRegion* reduced) { return true; } +/** + * Variant of SkScalarRoundToInt, identical to SkDScalarRoundToInt except when the input fraction + * is 0.5. In this case only, round the value down. This is used to round the top and left + * of a rectangle, and corresponds to the way the scan converter treats the top and left edges. + */ +static inline int round_down_to_int(SkScalar x) { + double xx = x; + xx += 0.5; + double floorXX = floor(xx); + return (int)floorXX - (xx == floorXX); +} + +/** + * Variant of SkRect::round() that explicitly performs the rounding step (i.e. floor(x + 0.5)) + * using double instead of SkScalar (float). It does this by calling SkDScalarRoundToInt(), + * which may be slower than calling SkScalarRountToInt(), but gives slightly more accurate + * results. Also rounds top and left using double, flooring when the fraction is exactly 0.5f. + * + * e.g. + * SkScalar left = 0.5f; + * int ileft = SkScalarRoundToInt(left); + * SkASSERT(0 == ileft); // <--- fails + * int ileft = round_down_to_int(left); + * SkASSERT(0 == ileft); // <--- succeeds + * SkScalar right = 0.49999997f; + * int iright = SkScalarRoundToInt(right); + * SkASSERT(0 == iright); // <--- fails + * iright = SkDScalarRoundToInt(right); + * SkASSERT(0 == iright); // <--- succeeds + */ +static void round_asymmetric_to_int(const SkRect& src, SkIRect* dst) { + SkASSERT(dst); + dst->set(round_down_to_int(src.fLeft), round_down_to_int(src.fTop), + SkDScalarRoundToInt(src.fRight), SkDScalarRoundToInt(src.fBottom)); +} + void SkScan::FillPath(const SkPath& path, const SkRegion& origClip, SkBlitter* blitter) { if (origClip.isEmpty()) { @@ -578,11 +614,11 @@ void SkScan::FillPath(const SkPath& path, const SkRegion& origClip, // don't reference "origClip" any more, just use clipPtr SkIRect ir; - // We deliberately call dround() instead of round(), since we can't afford to generate a - // bounds that is tighter than the corresponding SkEdges. The edge code basically converts - // the floats to fixed, and then "rounds". If we called round() instead of dround() here, - // we could generate the wrong ir for values like 0.4999997. - path.getBounds().dround(&ir); + // We deliberately call round_asymmetric_to_int() instead of round(), since we can't afford + // to generate a bounds that is tighter than the corresponding SkEdges. The edge code basically + // converts the floats to fixed, and then "rounds". If we called round() instead of + // round_asymmetric_to_int() here, we could generate the wrong ir for values like 0.4999997. + round_asymmetric_to_int(path.getBounds(), &ir); if (ir.isEmpty()) { if (path.isInverseFillType()) { blitter->blitRegion(*clipPtr); diff --git a/gfx/skia/skia/src/core/SkShader.cpp b/gfx/skia/skia/src/core/SkShader.cpp index 0ec1433a90..bd3876a69b 100644 --- a/gfx/skia/skia/src/core/SkShader.cpp +++ b/gfx/skia/skia/src/core/SkShader.cpp @@ -119,15 +119,6 @@ SkShader::Context::ShadeProc SkShader::Context::asAShadeProc(void** ctx) { #include "SkColorPriv.h" -void SkShader::Context::shadeSpan16(int x, int y, uint16_t span16[], int count) { - SkASSERT(span16); - SkASSERT(count > 0); - SkASSERT(this->canCallShadeSpan16()); - - // basically, if we get here, the subclass screwed up - SkDEBUGFAIL("kHasSpan16 flag is set, but shadeSpan16() not implemented"); -} - #define kTempColorQuadCount 6 // balance between speed (larger) and saving stack-space #define kTempColorCount (kTempColorQuadCount << 2) @@ -266,10 +257,6 @@ uint32_t SkColorShader::ColorShaderContext::getFlags() const { return fFlags; } -uint8_t SkColorShader::ColorShaderContext::getSpan16Alpha() const { - return SkGetPackedA32(fPMColor); -} - SkShader::Context* SkColorShader::onCreateContext(const ContextRec& rec, void* storage) const { return new (storage) ColorShaderContext(*this, rec); } @@ -285,9 +272,6 @@ SkColorShader::ColorShaderContext::ColorShaderContext(const SkColorShader& shade unsigned g = SkColorGetG(color); unsigned b = SkColorGetB(color); - // we want this before we apply any alpha - fColor16 = SkPack888ToRGB16(r, g, b); - if (a != 255) { r = SkMulDiv255Round(r, a); g = SkMulDiv255Round(g, a); @@ -298,9 +282,6 @@ SkColorShader::ColorShaderContext::ColorShaderContext(const SkColorShader& shade fFlags = kConstInY32_Flag; if (255 == a) { fFlags |= kOpaqueAlpha_Flag; - if (rec.fPaint->isDither() == false) { - fFlags |= kHasSpan16_Flag; - } } } @@ -308,10 +289,6 @@ void SkColorShader::ColorShaderContext::shadeSpan(int x, int y, SkPMColor span[] sk_memset32(span, fPMColor, count); } -void SkColorShader::ColorShaderContext::shadeSpan16(int x, int y, uint16_t span[], int count) { - sk_memset16(span, fColor16, count); -} - void SkColorShader::ColorShaderContext::shadeSpanAlpha(int x, int y, uint8_t alpha[], int count) { memset(alpha, SkGetPackedA32(fPMColor), count); } diff --git a/gfx/skia/skia/src/core/SkStream.cpp b/gfx/skia/skia/src/core/SkStream.cpp index 05867d91bf..ef4c6baae4 100644 --- a/gfx/skia/skia/src/core/SkStream.cpp +++ b/gfx/skia/skia/src/core/SkStream.cpp @@ -367,18 +367,14 @@ size_t SkMemoryStream::read(void* buffer, size_t size) { return size; } -bool SkMemoryStream::peek(void* buffer, size_t size) const { +size_t SkMemoryStream::peek(void* buffer, size_t size) const { SkASSERT(buffer != nullptr); - const size_t position = fOffset; - if (size > fData->size() - position) { - // The stream is not large enough to satisfy this request. - return false; - } + + const size_t currentOffset = fOffset; SkMemoryStream* nonConstThis = const_cast(this); - SkDEBUGCODE(const size_t bytesRead =) nonConstThis->read(buffer, size); - SkASSERT(bytesRead == size); - nonConstThis->fOffset = position; - return true; + const size_t bytesRead = nonConstThis->read(buffer, size); + nonConstThis->fOffset = currentOffset; + return bytesRead; } bool SkMemoryStream::isAtEnd() const { @@ -467,6 +463,14 @@ void SkFILEWStream::flush() } } +void SkFILEWStream::fsync() +{ + flush(); + if (fFILE) { + sk_fsync(fFILE); + } +} + //////////////////////////////////////////////////////////////////////// SkMemoryWStream::SkMemoryWStream(void* buffer, size_t size) @@ -725,25 +729,26 @@ public: return fOffset == fSize; } - bool peek(void* buff, size_t size) const override { + size_t peek(void* buff, size_t bytesToPeek) const override { SkASSERT(buff != nullptr); - if (fOffset + size > fSize) { - return false; - } + + bytesToPeek = SkTMin(bytesToPeek, fSize - fOffset); + + size_t bytesLeftToPeek = bytesToPeek; char* buffer = static_cast(buff); const SkDynamicMemoryWStream::Block* current = fCurrent; size_t currentOffset = fCurrentOffset; - while (size) { + while (bytesLeftToPeek) { SkASSERT(current); size_t bytesFromCurrent = - SkTMin(current->written() - currentOffset, size); + SkTMin(current->written() - currentOffset, bytesLeftToPeek); memcpy(buffer, current->start() + currentOffset, bytesFromCurrent); - size -= bytesFromCurrent; + bytesLeftToPeek -= bytesFromCurrent; buffer += bytesFromCurrent; current = current->fNext; currentOffset = 0; } - return true; + return bytesToPeek; } bool rewind() override { diff --git a/gfx/skia/skia/src/core/SkString.cpp b/gfx/skia/skia/src/core/SkString.cpp index e730753fca..64193c47e9 100644 --- a/gfx/skia/skia/src/core/SkString.cpp +++ b/gfx/skia/skia/src/core/SkString.cpp @@ -624,16 +624,35 @@ SkString SkStringPrintf(const char* format, ...) { return formattedOutput; } -void SkStrSplit(const char* str, const char* delimiters, SkTArray* out) { - const char* end = str + strlen(str); - while (str != end) { - // Find a token. - const size_t len = strcspn(str, delimiters); - out->push_back().set(str, len); - str += len; +void SkStrSplit(const char* str, const char* delimiters, SkStrSplitMode splitMode, + SkTArray* out) { + if (splitMode == kCoalesce_SkStrSplitMode) { // Skip any delimiters. str += strspn(str, delimiters); } + if (!*str) { + return; + } + + while (true) { + // Find a token. + const size_t len = strcspn(str, delimiters); + if (splitMode == kStrict_SkStrSplitMode || len > 0) { + out->push_back().set(str, len); + str += len; + } + + if (!*str) { + return; + } + if (splitMode == kCoalesce_SkStrSplitMode) { + // Skip any delimiters. + str += strspn(str, delimiters); + } else { + // Skip one delimiter. + str += 1; + } + } } #undef VSNPRINTF diff --git a/gfx/skia/skia/src/core/SkStroke.cpp b/gfx/skia/skia/src/core/SkStroke.cpp index 65267d11fd..5a43075284 100644 --- a/gfx/skia/skia/src/core/SkStroke.cpp +++ b/gfx/skia/skia/src/core/SkStroke.cpp @@ -126,7 +126,7 @@ public: SkPoint moveToPt() const { return fFirstPt; } void moveTo(const SkPoint&); - void lineTo(const SkPoint&); + void lineTo(const SkPoint&, const SkPath::Iter* iter = nullptr); void quadTo(const SkPoint&, const SkPoint&); void conicTo(const SkPoint&, const SkPoint&, SkScalar weight); void cubicTo(const SkPoint&, const SkPoint&, const SkPoint&); @@ -187,6 +187,7 @@ private: int fRecursionDepth; // track stack depth to abort if numerics run amok bool fFoundTangents; // do less work until tangents meet (cubic) + bool fJoinCompleted; // previous join was not degenerate void addDegenerateLine(const SkQuadConstruct* ); static ReductionType CheckConicLinear(const SkConic& , SkPoint* reduction); @@ -273,6 +274,7 @@ bool SkPathStroker::preJoinTo(const SkPoint& currPt, SkVector* normal, void SkPathStroker::postJoinTo(const SkPoint& currPt, const SkVector& normal, const SkVector& unitNormal) { + fJoinCompleted = true; fPrevPt = currPt; fPrevUnitNormal = unitNormal; fPrevNormal = normal; @@ -359,6 +361,7 @@ void SkPathStroker::moveTo(const SkPoint& pt) { } fSegmentCount = 0; fFirstPt = fPrevPt = pt; + fJoinCompleted = false; } void SkPathStroker::line_to(const SkPoint& currPt, const SkVector& normal) { @@ -366,11 +369,46 @@ void SkPathStroker::line_to(const SkPoint& currPt, const SkVector& normal) { fInner.lineTo(currPt.fX - normal.fX, currPt.fY - normal.fY); } -void SkPathStroker::lineTo(const SkPoint& currPt) { +static bool has_valid_tangent(const SkPath::Iter* iter) { + SkPath::Iter copy = *iter; + SkPath::Verb verb; + SkPoint pts[4]; + while ((verb = copy.next(pts))) { + switch (verb) { + case SkPath::kMove_Verb: + return false; + case SkPath::kLine_Verb: + if (pts[0] == pts[1]) { + continue; + } + return true; + case SkPath::kQuad_Verb: + case SkPath::kConic_Verb: + if (pts[0] == pts[1] && pts[0] == pts[2]) { + continue; + } + return true; + case SkPath::kCubic_Verb: + if (pts[0] == pts[1] && pts[0] == pts[2] && pts[0] == pts[3]) { + continue; + } + return true; + case SkPath::kClose_Verb: + case SkPath::kDone_Verb: + return false; + } + } + return false; +} + +void SkPathStroker::lineTo(const SkPoint& currPt, const SkPath::Iter* iter) { if (SkStrokerPriv::CapFactory(SkPaint::kButt_Cap) == fCapper && fPrevPt.equalsWithinTolerance(currPt, SK_ScalarNearlyZero * fInvResScale)) { return; } + if (fPrevPt == currPt && (fJoinCompleted || (iter && has_valid_tangent(iter)))) { + return; + } SkVector normal, unitNormal; if (!this->preJoinTo(currPt, &normal, &unitNormal, true)) { @@ -1339,7 +1377,7 @@ void SkStroke::strokePath(const SkPath& src, SkPath* dst) const { stroker.moveTo(pts[0]); break; case SkPath::kLine_Verb: - stroker.lineTo(pts[1]); + stroker.lineTo(pts[1], &iter); lastSegment = SkPath::kLine_Verb; break; case SkPath::kQuad_Verb: diff --git a/gfx/skia/skia/src/core/SkTLList.h b/gfx/skia/skia/src/core/SkTLList.h index 211a441413..48e81bdac1 100644 --- a/gfx/skia/skia/src/core/SkTLList.h +++ b/gfx/skia/skia/src/core/SkTLList.h @@ -10,7 +10,7 @@ #include "SkTInternalLList.h" #include "SkTypes.h" -#include "SkUtility.h" +#include /** Doubly-linked list of objects. The objects' lifetimes are controlled by the list. I.e. the the list creates the objects and they are deleted upon removal. This class block-allocates @@ -72,7 +72,7 @@ public: Node* node = this->createNode(); fList.addToHead(node); this->validate(); - return new (node->fObj) T(skstd::forward(args)...); + return new (node->fObj) T(std::forward(args)...); } /** Adds a new element to the list at the tail. */ @@ -81,7 +81,7 @@ public: Node* node = this->createNode(); fList.addToTail(node); this->validate(); - return new (node->fObj) T(skstd::forward(args)...); + return new (node->fObj) T(std::forward(args)...); } /** Adds a new element to the list before the location indicated by the iterator. If the @@ -91,7 +91,7 @@ public: Node* node = this->createNode(); fList.addBefore(node, location.getNode()); this->validate(); - return new (node->fObj) T(skstd::forward(args)...); + return new (node->fObj) T(std::forward(args)...); } /** Adds a new element to the list after the location indicated by the iterator. If the @@ -101,7 +101,7 @@ public: Node* node = this->createNode(); fList.addAfter(node, location.getNode()); this->validate(); - return new (node->fObj) T(skstd::forward(args)...); + return new (node->fObj) T(std::forward(args)...); } /** Convenience methods for getting an iterator initialized to the head/tail of the list. */ diff --git a/gfx/skia/skia/src/core/SkTaskGroup.cpp b/gfx/skia/skia/src/core/SkTaskGroup.cpp index 863195cfd3..e4d4bee9a2 100644 --- a/gfx/skia/skia/src/core/SkTaskGroup.cpp +++ b/gfx/skia/skia/src/core/SkTaskGroup.cpp @@ -9,6 +9,7 @@ #include "SkRunnable.h" #include "SkSemaphore.h" #include "SkSpinlock.h" +#include "SkTArray.h" #include "SkTDArray.h" #include "SkTaskGroup.h" #include "SkThreadUtils.h" @@ -43,23 +44,22 @@ public: if (!gGlobal) { // If we have no threads, run synchronously. return task->run(); } - gGlobal->add(&CallRunnable, task, pending); + gGlobal->add([task]() { task->run(); }, pending); } - static void Add(void (*fn)(void*), void* arg, SkAtomic* pending) { + static void Add(std::function fn, SkAtomic* pending) { if (!gGlobal) { - return fn(arg); + return fn(); } - gGlobal->add(fn, arg, pending); + gGlobal->add(fn, pending); } - static void Batch(void (*fn)(void*), void* args, int N, size_t stride, - SkAtomic* pending) { + static void Batch(int N, std::function fn, SkAtomic* pending) { if (!gGlobal) { - for (int i = 0; i < N; i++) { fn((char*)args + i*stride); } + for (int i = 0; i < N; i++) { fn(i); } return; } - gGlobal->batch(fn, args, N, stride, pending); + gGlobal->batch(N, fn, pending); } static void Wait(SkAtomic* pending) { @@ -76,16 +76,17 @@ public: // so we never call fWorkAvailable.wait(), which could sleep us if there's no work. // This means fWorkAvailable is only an upper bound on fWork.count(). AutoLock lock(&gGlobal->fWorkLock); - if (gGlobal->fWork.isEmpty()) { + if (gGlobal->fWork.empty()) { // Someone has picked up all the work (including ours). How nice of them! // (They may still be working on it, so we can't assert *pending == 0 here.) continue; } - gGlobal->fWork.pop(&work); + work = gGlobal->fWork.back(); + gGlobal->fWork.pop_back(); } // This Work isn't necessarily part of our SkTaskGroup of interest, but that's fine. // We threads gotta stick together. We're always making forward progress. - work.fn(work.arg); + work.fn(); work.pending->fetch_add(-1, sk_memory_order_release); // Pairs with load above. } } @@ -101,8 +102,7 @@ private: static void CallRunnable(void* arg) { static_cast(arg)->run(); } struct Work { - void (*fn)(void*); // A function to call, - void* arg; // its argument, + std::function fn; // A function to call SkAtomic* pending; // then decrement pending afterwards. }; @@ -117,39 +117,40 @@ private: } ~ThreadPool() { - SkASSERT(fWork.isEmpty()); // All SkTaskGroups should be destroyed by now. + SkASSERT(fWork.empty()); // All SkTaskGroups should be destroyed by now. // Send a poison pill to each thread. SkAtomic dummy(0); for (int i = 0; i < fThreads.count(); i++) { - this->add(nullptr, nullptr, &dummy); + this->add(nullptr, &dummy); } // Wait for them all to swallow the pill and die. for (int i = 0; i < fThreads.count(); i++) { fThreads[i]->join(); } - SkASSERT(fWork.isEmpty()); // Can't hurt to double check. + SkASSERT(fWork.empty()); // Can't hurt to double check. fThreads.deleteAll(); } - void add(void (*fn)(void*), void* arg, SkAtomic* pending) { - Work work = { fn, arg, pending }; + void add(std::function fn, SkAtomic* pending) { + Work work = { fn, pending }; pending->fetch_add(+1, sk_memory_order_relaxed); // No barrier needed. { AutoLock lock(&fWorkLock); - fWork.push(work); + fWork.push_back(work); } fWorkAvailable.signal(1); } - void batch(void (*fn)(void*), void* arg, int N, size_t stride, SkAtomic* pending) { + void batch(int N, std::function fn, SkAtomic* pending) { pending->fetch_add(+N, sk_memory_order_relaxed); // No barrier needed. { AutoLock lock(&fWorkLock); - Work* batch = fWork.append(N); for (int i = 0; i < N; i++) { - Work work = { fn, (char*)arg + i*stride, pending }; - batch[i] = work; + Work work; + work.fn = [i, fn]() { fn(i); }; + work.pending = pending; + fWork.push_back(work); } } fWorkAvailable.signal(N); @@ -163,24 +164,25 @@ private: pool->fWorkAvailable.wait(); { AutoLock lock(&pool->fWorkLock); - if (pool->fWork.isEmpty()) { + if (pool->fWork.empty()) { // Someone in Wait() stole our work (fWorkAvailable is an upper bound). // Well, that's fine, back to sleep for us. continue; } - pool->fWork.pop(&work); + work = pool->fWork.back(); + pool->fWork.pop_back(); } if (!work.fn) { return; // Poison pill. Time... to die. } - work.fn(work.arg); + work.fn(); work.pending->fetch_add(-1, sk_memory_order_release); // Pairs with load in Wait(). } } // fWorkLock must be held when reading or modifying fWork. SkSpinlock fWorkLock; - SkTDArray fWork; + SkTArray fWork; // A thread-safe upper bound for fWork.count(). // @@ -196,7 +198,6 @@ private: static ThreadPool* gGlobal; friend struct SkTaskGroup::Enabler; - friend int ::sk_parallel_for_thread_count(); }; ThreadPool* ThreadPool::gGlobal = nullptr; @@ -215,14 +216,8 @@ SkTaskGroup::SkTaskGroup() : fPending(0) {} void SkTaskGroup::wait() { ThreadPool::Wait(&fPending); } void SkTaskGroup::add(SkRunnable* task) { ThreadPool::Add(task, &fPending); } -void SkTaskGroup::add(void (*fn)(void*), void* arg) { ThreadPool::Add(fn, arg, &fPending); } -void SkTaskGroup::batch (void (*fn)(void*), void* args, int N, size_t stride) { - ThreadPool::Batch(fn, args, N, stride, &fPending); +void SkTaskGroup::add(std::function fn) { ThreadPool::Add(fn, &fPending); } +void SkTaskGroup::batch(int N, std::function fn) { + ThreadPool::Batch(N, fn, &fPending); } -int sk_parallel_for_thread_count() { - if (ThreadPool::gGlobal != nullptr) { - return ThreadPool::gGlobal->fThreads.count(); - } - return 0; -} diff --git a/gfx/skia/skia/src/core/SkTaskGroup.h b/gfx/skia/skia/src/core/SkTaskGroup.h index f68c528a1f..802ee0603e 100644 --- a/gfx/skia/skia/src/core/SkTaskGroup.h +++ b/gfx/skia/skia/src/core/SkTaskGroup.h @@ -29,70 +29,20 @@ public: // Neither add() method takes owership of any of its parameters. void add(SkRunnable*); - template - void add(void (*fn)(T*), T* arg) { this->add((void_fn)fn, (void*)arg); } + void add(std::function fn); // Add a batch of N tasks, all calling fn with different arguments. - // Equivalent to a loop over add(fn, arg), but with perhaps less synchronization overhead. - template - void batch(void (*fn)(T*), T* args, int N) { this->batch((void_fn)fn, args, N, sizeof(T)); } + void batch(int N, std::function fn); // Block until all Tasks previously add()ed to this SkTaskGroup have run. // You may safely reuse this SkTaskGroup after wait() returns. void wait(); private: - typedef void(*void_fn)(void*); - - void add (void_fn, void* arg); - void batch(void_fn, void* args, int N, size_t stride); - SkAtomic fPending; }; // Returns best estimate of number of CPU cores available to use. int sk_num_cores(); -int sk_parallel_for_thread_count(); - -// Call f(i) for i in [0, end). -template -void sk_parallel_for(int end, const Func& f) { - if (end <= 0) { return; } - - struct Chunk { - const Func* f; - int start, end; - }; - - // TODO(mtklein): this chunking strategy could probably use some tuning. - int max_chunks = sk_num_cores() * 2, - stride = (end + max_chunks - 1 ) / max_chunks, - nchunks = (end + stride - 1 ) / stride; - SkASSERT(nchunks <= max_chunks); - -#if defined(GOOGLE3) - // Stack frame size is limited in GOOGLE3. - SkAutoSTMalloc<512, Chunk> chunks(nchunks); -#else - // With the chunking strategy above this won't malloc until we have a machine with >512 cores. - SkAutoSTMalloc<1024, Chunk> chunks(nchunks); -#endif - - for (int i = 0; i < nchunks; i++) { - Chunk& c = chunks[i]; - c.f = &f; - c.start = i * stride; - c.end = SkTMin(c.start + stride, end); - SkASSERT(c.start < c.end); // Nothing will break if start >= end, but it's a wasted chunk. - } - - void(*run_chunk)(Chunk*) = [](Chunk* c) { - for (int i = c->start; i < c->end; i++) { - (*c->f)(i); - } - }; - SkTaskGroup().batch(run_chunk, chunks.get(), nchunks); -} - #endif//SkTaskGroup_DEFINED diff --git a/gfx/skia/skia/src/core/SkTime.cpp b/gfx/skia/skia/src/core/SkTime.cpp index 2f66057878..a454061a89 100644 --- a/gfx/skia/skia/src/core/SkTime.cpp +++ b/gfx/skia/skia/src/core/SkTime.cpp @@ -8,6 +8,7 @@ #include "SkOncePtr.h" #include "SkString.h" #include "SkTime.h" +#include "SkTypes.h" void SkTime::DateTime::toISO8601(SkString* dst) const { if (dst) { @@ -24,6 +25,46 @@ void SkTime::DateTime::toISO8601(SkString* dst) const { } } + +#ifdef SK_BUILD_FOR_WIN32 + +#include "windows.h" +void SkTime::GetDateTime(DateTime* dt) { + if (dt) { + SYSTEMTIME st; + GetSystemTime(&st); + dt->fTimeZoneMinutes = 0; + dt->fYear = st.wYear; + dt->fMonth = SkToU8(st.wMonth); + dt->fDayOfWeek = SkToU8(st.wDayOfWeek); + dt->fDay = SkToU8(st.wDay); + dt->fHour = SkToU8(st.wHour); + dt->fMinute = SkToU8(st.wMinute); + dt->fSecond = SkToU8(st.wSecond); + } +} + +#else // SK_BUILD_FOR_WIN32 + +#include +void SkTime::GetDateTime(DateTime* dt) { + if (dt) { + time_t m_time; + time(&m_time); + struct tm* tstruct; + tstruct = gmtime(&m_time); + dt->fTimeZoneMinutes = 0; + dt->fYear = tstruct->tm_year + 1900; + dt->fMonth = SkToU8(tstruct->tm_mon + 1); + dt->fDayOfWeek = SkToU8(tstruct->tm_wday); + dt->fDay = SkToU8(tstruct->tm_mday); + dt->fHour = SkToU8(tstruct->tm_hour); + dt->fMinute = SkToU8(tstruct->tm_min); + dt->fSecond = SkToU8(tstruct->tm_sec); + } +} +#endif // SK_BUILD_FOR_WIN32 + #if defined(_MSC_VER) // TODO: try std::chrono again with MSVC 2015? #include diff --git a/gfx/skia/skia/src/core/SkUtils.cpp b/gfx/skia/skia/src/core/SkUtils.cpp index b3f698b4e7..f706cb9f25 100644 --- a/gfx/skia/skia/src/core/SkUtils.cpp +++ b/gfx/skia/skia/src/core/SkUtils.cpp @@ -74,11 +74,11 @@ SkUnichar SkUTF8_ToUnichar(const char utf8[]) { if (hic < 0) { uint32_t mask = (uint32_t)~0x3F; - hic <<= 1; + hic = SkLeftShift(hic, 1); do { c = (c << 6) | (*++p & 0x3F); mask <<= 5; - } while ((hic <<= 1) < 0); + } while ((hic = SkLeftShift(hic, 1)) < 0); c &= ~mask; } return c; @@ -95,11 +95,11 @@ SkUnichar SkUTF8_NextUnichar(const char** ptr) { if (hic < 0) { uint32_t mask = (uint32_t)~0x3F; - hic <<= 1; + hic = SkLeftShift(hic, 1); do { c = (c << 6) | (*++p & 0x3F); mask <<= 5; - } while ((hic <<= 1) < 0); + } while ((hic = SkLeftShift(hic, 1)) < 0); c &= ~mask; } *ptr = (char*)p + 1; diff --git a/gfx/skia/skia/src/core/SkValue.h b/gfx/skia/skia/src/core/SkValue.h new file mode 100644 index 0000000000..4a53a38e3d --- /dev/null +++ b/gfx/skia/skia/src/core/SkValue.h @@ -0,0 +1,81 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkValue_DEFINED +#define SkValue_DEFINED + +#include "SkTypes.h" +#include "SkTemplates.h" + +class SkValue { +public: + enum Type : uint32_t { + // 0-255 are reserved for built-in SkValue types. + Null, + Byte , S16 , U16 , S32 , U32 , S64 , U64 , F32 , F64 , + Bytes, S16s, U16s, S32s, U32s, S64s, U64s, F32s, F64s, + Array, + + // 256-2147483647 may be used by Skia for public Object types. + + + // 2147483648+ won't be used by Skia. They're open for client-specific use, testing, etc. + }; + + enum Key : uint32_t { + // Each Object type may define its own namespace of Key values, + // so there are no pre-defined Keys here. + // + // This is just a reminder that they must fit in a uint32_t, + // and that their namespace is distinct from other uint32_ts (e.g. Type). + }; + + SkValue(); + SkValue(const SkValue&); + SkValue(SkValue&&); + + SkValue& operator=(const SkValue&); + SkValue& operator=(SkValue&&); + + ~SkValue(); + + static SkValue FromS32(int32_t); + static SkValue FromU32(uint32_t); + static SkValue FromF32(float); + static SkValue FromBytes(const void*, size_t); // Copies. + static SkValue Object(Type); + + Type type() const; + + // These remaining methods may assert they're called on a value of the appropriate type. + + int32_t s32() const; + uint32_t u32() const; + float f32() const; + + const void* bytes() const; + size_t count() const; + + void set(Key, SkValue); + const SkValue* get(Key) const; + void foreach(std::function) const; + +private: + class Bytes; + class Object; + + Type fType; + union { + int32_t fS32; + uint32_t fU32; + float fF32; + class Bytes* fBytes; + class Object* fObject; + }; +}; + +#endif//SkValue_DEFINED diff --git a/gfx/skia/skia/src/core/SkVarAlloc.cpp b/gfx/skia/skia/src/core/SkVarAlloc.cpp index 149f0515b0..1fbd382c19 100644 --- a/gfx/skia/skia/src/core/SkVarAlloc.cpp +++ b/gfx/skia/skia/src/core/SkVarAlloc.cpp @@ -7,13 +7,6 @@ #include "SkVarAlloc.h" -// We use non-standard malloc diagnostic methods to make sure our allocations are sized well. -#if defined(SK_BUILD_FOR_MAC) - #include -#elif defined(SK_BUILD_FOR_UNIX) || defined(SK_BUILD_FOR_WIN32) -// #include -#endif - struct SkVarAlloc::Block { Block* prev; char* data() { return (char*)(this + 1); } @@ -60,11 +53,4 @@ void SkVarAlloc::makeSpace(size_t bytes) { fBlock = Block::Alloc(fBlock, alloc); fByte = fBlock->data(); fRemaining = alloc - sizeof(Block); - -#if defined(SK_BUILD_FOR_MAC) - SkASSERT(alloc == malloc_good_size(alloc)); -#elif defined(SK_BUILD_FOR_UNIX) && !defined(__UCLIBC__) - // TODO(mtklein): tune so we can assert something like this - //SkASSERT(alloc == malloc_usable_size(fBlock)); -#endif } diff --git a/gfx/skia/skia/src/core/SkWriteBuffer.cpp b/gfx/skia/skia/src/core/SkWriteBuffer.cpp index 1dfe0b3972..f8b3195403 100644 --- a/gfx/skia/skia/src/core/SkWriteBuffer.cpp +++ b/gfx/skia/skia/src/core/SkWriteBuffer.cpp @@ -200,11 +200,8 @@ void SkWriteBuffer::writeBitmap(const SkBitmap& bitmap) { // see if the caller wants to manually encode SkAutoPixmapUnlock result; if (fPixelSerializer && bitmap.requestLock(&result)) { - const SkPixmap& pmap = result.pixmap(); SkASSERT(nullptr == fBitmapHeap); - SkAutoDataUnref data(fPixelSerializer->encodePixels(pmap.info(), - pmap.addr(), - pmap.rowBytes())); + SkAutoDataUnref data(fPixelSerializer->encode(result.pixmap())); if (data.get() != nullptr) { // if we have to "encode" the bitmap, then we assume there is no // offset to share, since we are effectively creating a new pixelref diff --git a/gfx/skia/skia/src/device/xps/SkXPSDevice.cpp b/gfx/skia/skia/src/device/xps/SkXPSDevice.cpp index 86c729c9e4..48233bb554 100644 --- a/gfx/skia/skia/src/device/xps/SkXPSDevice.cpp +++ b/gfx/skia/skia/src/device/xps/SkXPSDevice.cpp @@ -1628,12 +1628,8 @@ void SkXPSDevice::drawPath(const SkDraw& d, //[Mask -> Mask] SkMask filteredMask; - if (filter && - filter->filterMask(&filteredMask, *mask, *d.fMatrix, nullptr)) { - + if (filter && filter->filterMask(&filteredMask, *mask, *d.fMatrix, nullptr)) { mask = &filteredMask; - } else { - filteredMask.fImage = nullptr; } SkAutoMaskFreeImage filteredAmi(filteredMask.fImage); @@ -1675,13 +1671,8 @@ void SkXPSDevice::drawPath(const SkDraw& d, //[Mask -> Mask] SkMask filteredMask; - if (filter->filterMask(&filteredMask, - rasteredMask, - matrix, - nullptr)) { + if (filter->filterMask(&filteredMask, rasteredMask, matrix, nullptr)) { mask = &filteredMask; - } else { - filteredMask.fImage = nullptr; } SkAutoMaskFreeImage filteredAmi(filteredMask.fImage); diff --git a/gfx/skia/skia/src/doc/SkDocument_PDF.cpp b/gfx/skia/skia/src/doc/SkDocument_PDF.cpp index ff7a038b6b..fb560ea1e3 100644 --- a/gfx/skia/skia/src/doc/SkDocument_PDF.cpp +++ b/gfx/skia/skia/src/doc/SkDocument_PDF.cpp @@ -322,9 +322,12 @@ class SkDocument_PDF : public SkDocument { public: SkDocument_PDF(SkWStream* stream, void (*doneProc)(SkWStream*, bool), - SkScalar rasterDpi) + SkScalar rasterDpi, + SkPixelSerializer* jpegEncoder) : SkDocument(stream, doneProc) - , fRasterDpi(rasterDpi) {} + , fRasterDpi(rasterDpi) { + fCanon.fPixelSerializer.reset(SkSafeRef(jpegEncoder)); + } virtual ~SkDocument_PDF() { // subclasses must call close() in their destructors @@ -386,7 +389,15 @@ private: /////////////////////////////////////////////////////////////////////////////// SkDocument* SkDocument::CreatePDF(SkWStream* stream, SkScalar dpi) { - return stream ? new SkDocument_PDF(stream, nullptr, dpi) : nullptr; + return stream ? new SkDocument_PDF(stream, nullptr, dpi, nullptr) : nullptr; +} + +SkDocument* SkDocument::CreatePDF(SkWStream* stream, + SkScalar dpi, + SkPixelSerializer* jpegEncoder) { + return stream + ? new SkDocument_PDF(stream, nullptr, dpi, jpegEncoder) + : nullptr; } SkDocument* SkDocument::CreatePDF(const char path[], SkScalar dpi) { @@ -396,5 +407,5 @@ SkDocument* SkDocument::CreatePDF(const char path[], SkScalar dpi) { return nullptr; } auto delete_wstream = [](SkWStream* stream, bool) { delete stream; }; - return new SkDocument_PDF(stream, delete_wstream, dpi); + return new SkDocument_PDF(stream, delete_wstream, dpi, nullptr); } diff --git a/gfx/skia/skia/src/effects/GrCircleBlurFragmentProcessor.cpp b/gfx/skia/skia/src/effects/GrCircleBlurFragmentProcessor.cpp index 8c9d204928..83faa86fa2 100644 --- a/gfx/skia/skia/src/effects/GrCircleBlurFragmentProcessor.cpp +++ b/gfx/skia/skia/src/effects/GrCircleBlurFragmentProcessor.cpp @@ -11,12 +11,13 @@ #if SK_SUPPORT_GPU #include "GrContext.h" +#include "GrInvariantOutput.h" #include "GrTextureProvider.h" #include "glsl/GrGLSLFragmentProcessor.h" #include "glsl/GrGLSLFragmentShaderBuilder.h" -#include "glsl/GrGLSLProgramBuilder.h" #include "glsl/GrGLSLProgramDataManager.h" +#include "glsl/GrGLSLUniformHandler.h" class GrGLCircleBlurFragmentProcessor : public GrGLSLFragmentProcessor { public: @@ -39,12 +40,12 @@ void GrGLCircleBlurFragmentProcessor::emitCode(EmitArgs& args) { // The data is formatted as: // x,y - the center of the circle // z - the distance at which the intensity starts falling off (e.g., the start of the table) - // w - the size of the profile texture - fDataUniform = args.fBuilder->addUniform(GrGLSLProgramBuilder::kFragment_Visibility, - kVec4f_GrSLType, - kDefault_GrSLPrecision, - "data", - &dataName); + // w - the inverse of the profile texture size + fDataUniform = args.fUniformHandler->addUniform(GrGLSLUniformHandler::kFragment_Visibility, + kVec4f_GrSLType, + kDefault_GrSLPrecision, + "data", + &dataName); GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder; const char *fragmentPos = fragBuilder->fragmentPosition(); @@ -55,8 +56,13 @@ void GrGLCircleBlurFragmentProcessor::emitCode(EmitArgs& args) { fragBuilder->codeAppendf("vec4 src=vec4(1);"); } - fragBuilder->codeAppendf("vec2 vec = %s.xy - %s.xy;", fragmentPos, dataName); - fragBuilder->codeAppendf("float dist = (length(vec) - %s.z + 0.5) / %s.w;", dataName, dataName); + // We just want to compute "length(vec) - %s.z + 0.5) * %s.w" but need to rearrange + // for precision + fragBuilder->codeAppendf("vec2 vec = vec2( (%s.x - %s.x) * %s.w , (%s.y - %s.y) * %s.w );", + fragmentPos, dataName, dataName, + fragmentPos, dataName, dataName); + fragBuilder->codeAppendf("float dist = length(vec) + ( 0.5 - %s.z ) * %s.w;", + dataName, dataName); fragBuilder->codeAppendf("float intensity = "); fragBuilder->appendTextureLookup(args.fSamplers[0], "vec2(dist, 0.5)"); @@ -73,9 +79,9 @@ void GrGLCircleBlurFragmentProcessor::onSetData(const GrGLSLProgramDataManager& // The data is formatted as: // x,y - the center of the circle // z - the distance at which the intensity starts falling off (e.g., the start of the table) - // w - the size of the profile texture + // w - the inverse of the profile texture size pdman.set4f(fDataUniform, circle.centerX(), circle.centerY(), cbfp.offset(), - SkIntToScalar(cbfp.profileSize())); + 1.0f / cbfp.profileSize()); } /////////////////////////////////////////////////////////////////////////////// @@ -180,7 +186,7 @@ static inline void compute_profile_offset_and_size(float halfWH, float sigma, // The circle is bigger than the Gaussian. In this case we know the interior of the // blurred circle is solid. *offset = halfWH - 3 * sigma; // This location maps to 0.5f in the weights texture. - // It should always be 255. + // It should always be 255. *size = SkScalarCeilToInt(6*sigma); } else { // The Gaussian is bigger than the circle. diff --git a/gfx/skia/skia/src/effects/SkAlphaThresholdFilter.cpp b/gfx/skia/skia/src/effects/SkAlphaThresholdFilter.cpp index e6f1f088cb..b6be944220 100644 --- a/gfx/skia/skia/src/effects/SkAlphaThresholdFilter.cpp +++ b/gfx/skia/skia/src/effects/SkAlphaThresholdFilter.cpp @@ -22,6 +22,7 @@ public: SK_TO_STRING_OVERRIDE() SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkAlphaThresholdFilterImpl) + friend void SkAlphaThresholdFilter::InitializeFlattenables(); protected: void flatten(SkWriteBuffer&) const override; @@ -40,6 +41,11 @@ private: typedef SkImageFilter INHERITED; }; +SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkAlphaThresholdFilter) + SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkAlphaThresholdFilterImpl) +SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END + + SkImageFilter* SkAlphaThresholdFilter::Create(const SkRegion& region, SkScalar innerThreshold, SkScalar outerThreshold, @@ -59,8 +65,8 @@ SkImageFilter* SkAlphaThresholdFilter::Create(const SkRegion& region, #include "glsl/GrGLSLFragmentProcessor.h" #include "glsl/GrGLSLFragmentShaderBuilder.h" -#include "glsl/GrGLSLProgramBuilder.h" #include "glsl/GrGLSLProgramDataManager.h" +#include "glsl/GrGLSLUniformHandler.h" class AlphaThresholdEffect : public GrFragmentProcessor { @@ -139,12 +145,13 @@ private: }; void GrGLAlphaThresholdEffect::emitCode(EmitArgs& args) { - fInnerThresholdVar = args.fBuilder->addUniform(GrGLSLProgramBuilder::kFragment_Visibility, - kFloat_GrSLType, kDefault_GrSLPrecision, - "inner_threshold"); - fOuterThresholdVar = args.fBuilder->addUniform(GrGLSLProgramBuilder::kFragment_Visibility, - kFloat_GrSLType, kDefault_GrSLPrecision, - "outer_threshold"); + GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; + fInnerThresholdVar = uniformHandler->addUniform(GrGLSLUniformHandler::kFragment_Visibility, + kFloat_GrSLType, kDefault_GrSLPrecision, + "inner_threshold"); + fOuterThresholdVar = uniformHandler->addUniform(GrGLSLUniformHandler::kFragment_Visibility, + kFloat_GrSLType, kDefault_GrSLPrecision, + "outer_threshold"); GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder; SkString coords2D = fragBuilder->ensureFSCoords2D(args.fCoords, 0); @@ -160,9 +167,9 @@ void GrGLAlphaThresholdEffect::emitCode(EmitArgs& args) { fragBuilder->codeAppend(";\n"); fragBuilder->codeAppendf("\t\tfloat inner_thresh = %s;\n", - args.fBuilder->getUniformCStr(fInnerThresholdVar)); + uniformHandler->getUniformCStr(fInnerThresholdVar)); fragBuilder->codeAppendf("\t\tfloat outer_thresh = %s;\n", - args.fBuilder->getUniformCStr(fOuterThresholdVar)); + uniformHandler->getUniformCStr(fOuterThresholdVar)); fragBuilder->codeAppend("\t\tfloat mask = mask_color.a;\n"); fragBuilder->codeAppend("vec4 color = input_color;\n"); diff --git a/gfx/skia/skia/src/effects/SkArithmeticMode_gpu.cpp b/gfx/skia/skia/src/effects/SkArithmeticMode_gpu.cpp index a3793243ee..ce8e426d41 100644 --- a/gfx/skia/skia/src/effects/SkArithmeticMode_gpu.cpp +++ b/gfx/skia/skia/src/effects/SkArithmeticMode_gpu.cpp @@ -15,8 +15,8 @@ #include "GrTexture.h" #include "glsl/GrGLSLFragmentProcessor.h" #include "glsl/GrGLSLFragmentShaderBuilder.h" -#include "glsl/GrGLSLProgramBuilder.h" #include "glsl/GrGLSLProgramDataManager.h" +#include "glsl/GrGLSLUniformHandler.h" #include "glsl/GrGLSLXferProcessor.h" static const bool gUseUnpremul = false; @@ -65,10 +65,10 @@ public: SkString dstColor("dstColor"); this->emitChild(0, nullptr, &dstColor, args); - fKUni = args.fBuilder->addUniform(GrGLSLProgramBuilder::kFragment_Visibility, - kVec4f_GrSLType, kDefault_GrSLPrecision, - "k"); - const char* kUni = args.fBuilder->getUniformCStr(fKUni); + fKUni = args.fUniformHandler->addUniform(GrGLSLUniformHandler::kFragment_Visibility, + kVec4f_GrSLType, kDefault_GrSLPrecision, + "k"); + const char* kUni = args.fUniformHandler->getUniformCStr(fKUni); add_arithmetic_code(fragBuilder, args.fInputColor, @@ -210,18 +210,18 @@ public: } private: - void emitBlendCodeForDstRead(GrGLSLXPBuilder* pb, - GrGLSLXPFragmentBuilder* fragBuilder, + void emitBlendCodeForDstRead(GrGLSLXPFragmentBuilder* fragBuilder, + GrGLSLUniformHandler* uniformHandler, const char* srcColor, const char* srcCoverage, const char* dstColor, const char* outColor, const char* outColorSecondary, const GrXferProcessor& proc) override { - fKUni = pb->addUniform(GrGLSLProgramBuilder::kFragment_Visibility, - kVec4f_GrSLType, kDefault_GrSLPrecision, - "k"); - const char* kUni = pb->getUniformCStr(fKUni); + fKUni = uniformHandler->addUniform(GrGLSLUniformHandler::kFragment_Visibility, + kVec4f_GrSLType, kDefault_GrSLPrecision, + "k"); + const char* kUni = uniformHandler->getUniformCStr(fKUni); add_arithmetic_code(fragBuilder, srcColor, dstColor, outColor, kUni, fEnforcePMColor); diff --git a/gfx/skia/skia/src/effects/SkBlurImageFilter.cpp b/gfx/skia/skia/src/effects/SkBlurImageFilter.cpp index 928793de62..a1d6a2f80e 100644 --- a/gfx/skia/skia/src/effects/SkBlurImageFilter.cpp +++ b/gfx/skia/skia/src/effects/SkBlurImageFilter.cpp @@ -15,6 +15,7 @@ #include "SkWriteBuffer.h" #if SK_SUPPORT_GPU #include "GrContext.h" +#include "SkGr.h" #endif // This rather arbitrary-looking value results in a maximum box blur kernel size @@ -24,7 +25,7 @@ // raster paths. #define MAX_SIGMA SkIntToScalar(532) -static SkVector mapSigma(const SkSize& localSigma, const SkMatrix& ctm) { +static SkVector map_sigma(const SkSize& localSigma, const SkMatrix& ctm) { SkVector sigma = SkVector::Make(localSigma.width(), localSigma.height()); ctm.mapVectors(&sigma, 1); sigma.fX = SkMinScalar(SkScalarAbs(sigma.fX), MAX_SIGMA); @@ -82,9 +83,30 @@ bool SkBlurImageFilter::onFilterImage(Proxy* proxy, } SkIRect srcBounds, dstBounds; - if (!this->applyCropRect(ctx, src, srcOffset, &dstBounds, &srcBounds)) { + if (!this->applyCropRect(this->mapContext(ctx), src, srcOffset, &dstBounds, &srcBounds)) { return false; } + if (!srcBounds.intersect(dstBounds)) { + return false; + } + + SkVector sigma = map_sigma(fSigma, ctx.ctm()); + + int kernelSizeX, kernelSizeX3, lowOffsetX, highOffsetX; + int kernelSizeY, kernelSizeY3, lowOffsetY, highOffsetY; + getBox3Params(sigma.x(), &kernelSizeX, &kernelSizeX3, &lowOffsetX, &highOffsetX); + getBox3Params(sigma.y(), &kernelSizeY, &kernelSizeY3, &lowOffsetY, &highOffsetY); + + if (kernelSizeX < 0 || kernelSizeY < 0) { + return false; + } + + if (kernelSizeX == 0 && kernelSizeY == 0) { + src.extractSubset(dst, srcBounds); + offset->fX = srcBounds.x(); + offset->fY = srcBounds.y(); + return true; + } SkAutoLockPixels alp(src); if (!src.getPixels()) { @@ -98,24 +120,6 @@ bool SkBlurImageFilter::onFilterImage(Proxy* proxy, *dst = device->accessBitmap(false); SkAutoLockPixels alp_dst(*dst); - SkVector sigma = mapSigma(fSigma, ctx.ctm()); - - int kernelSizeX, kernelSizeX3, lowOffsetX, highOffsetX; - int kernelSizeY, kernelSizeY3, lowOffsetY, highOffsetY; - getBox3Params(sigma.x(), &kernelSizeX, &kernelSizeX3, &lowOffsetX, &highOffsetX); - getBox3Params(sigma.y(), &kernelSizeY, &kernelSizeY3, &lowOffsetY, &highOffsetY); - - if (kernelSizeX < 0 || kernelSizeY < 0) { - return false; - } - - if (kernelSizeX == 0 && kernelSizeY == 0) { - src.copyTo(dst, dst->colorType()); - offset->fX = dstBounds.x() + srcOffset.x(); - offset->fY = dstBounds.y() + srcOffset.y(); - return true; - } - SkAutoTUnref tempDevice(proxy->createDevice(dst->width(), dst->height())); if (!tempDevice) { return false; @@ -184,17 +188,12 @@ void SkBlurImageFilter::computeFastBounds(const SkRect& src, SkRect* dst) const SkScalarMul(fSigma.height(), SkIntToScalar(3))); } -bool SkBlurImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm, - SkIRect* dst) const { - SkIRect bounds = src; - SkVector sigma = mapSigma(fSigma, ctm); - bounds.outset(SkScalarCeilToInt(SkScalarMul(sigma.x(), SkIntToScalar(3))), - SkScalarCeilToInt(SkScalarMul(sigma.y(), SkIntToScalar(3)))); - if (this->getInput(0) && !this->getInput(0)->filterBounds(bounds, ctm, &bounds)) { - return false; - } - *dst = bounds; - return true; +void SkBlurImageFilter::onFilterNodeBounds(const SkIRect& src, const SkMatrix& ctm, + SkIRect* dst, MapDirection) const { + *dst = src; + SkVector sigma = map_sigma(fSigma, ctm); + dst->outset(SkScalarCeilToInt(SkScalarMul(sigma.x(), SkIntToScalar(3))), + SkScalarCeilToInt(SkScalarMul(sigma.y(), SkIntToScalar(3)))); } bool SkBlurImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, const Context& ctx, @@ -206,29 +205,36 @@ bool SkBlurImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, const return false; } SkIRect srcBounds, dstBounds; - if (!this->applyCropRect(ctx, input, srcOffset, &dstBounds, &srcBounds)) { + if (!this->applyCropRect(this->mapContext(ctx), input, srcOffset, &dstBounds, &srcBounds)) { return false; } - GrTexture* source = input.getTexture(); - SkVector sigma = mapSigma(fSigma, ctx.ctm()); + if (!srcBounds.intersect(dstBounds)) { + return false; + } + SkVector sigma = map_sigma(fSigma, ctx.ctm()); + if (sigma.x() == 0 && sigma.y() == 0) { + input.extractSubset(result, srcBounds); + offset->fX = srcBounds.x(); + offset->fY = srcBounds.y(); + return true; + } offset->fX = dstBounds.fLeft; offset->fY = dstBounds.fTop; srcBounds.offset(-srcOffset); dstBounds.offset(-srcOffset); SkRect srcBoundsF(SkRect::Make(srcBounds)); - auto constraint = GrTextureProvider::FromImageFilter(ctx.sizeConstraint()); - SkAutoTUnref tex(SkGpuBlurUtils::GaussianBlur(source->getContext(), - source, + GrTexture* inputTexture = input.getTexture(); + SkAutoTUnref tex(SkGpuBlurUtils::GaussianBlur(inputTexture->getContext(), + inputTexture, false, SkRect::Make(dstBounds), &srcBoundsF, sigma.x(), - sigma.y(), - constraint)); + sigma.y())); if (!tex) { return false; } - WrapTexture(tex, dstBounds.width(), dstBounds.height(), result); + GrWrapTextureInBitmap(tex, dstBounds.width(), dstBounds.height(), false, result); return true; #else SkDEBUGFAIL("Should not call in GPU-less build"); diff --git a/gfx/skia/skia/src/effects/SkBlurMask.h b/gfx/skia/skia/src/effects/SkBlurMask.h index b6c37fb461..25f890e263 100644 --- a/gfx/skia/skia/src/effects/SkBlurMask.h +++ b/gfx/skia/skia/src/effects/SkBlurMask.h @@ -15,14 +15,14 @@ class SkBlurMask { public: - static bool BlurRect(SkScalar sigma, SkMask *dst, const SkRect &src, SkBlurStyle, - SkIPoint *margin = nullptr, - SkMask::CreateMode createMode = - SkMask::kComputeBoundsAndRenderImage_CreateMode); - static bool BlurRRect(SkScalar sigma, SkMask *dst, const SkRRect &src, SkBlurStyle, - SkIPoint *margin = nullptr, - SkMask::CreateMode createMode = - SkMask::kComputeBoundsAndRenderImage_CreateMode); + static bool SK_WARN_UNUSED_RESULT BlurRect(SkScalar sigma, SkMask *dst, const SkRect &src, + SkBlurStyle, SkIPoint *margin = nullptr, + SkMask::CreateMode createMode = + SkMask::kComputeBoundsAndRenderImage_CreateMode); + static bool SK_WARN_UNUSED_RESULT BlurRRect(SkScalar sigma, SkMask *dst, const SkRRect &src, + SkBlurStyle, SkIPoint *margin = nullptr, + SkMask::CreateMode createMode = + SkMask::kComputeBoundsAndRenderImage_CreateMode); // forceQuality will prevent BoxBlur from falling back to the low quality approach when sigma // is very small -- this can be used predict the margin bump ahead of time without completely @@ -30,14 +30,16 @@ public: // but also being able to predict precisely at what pixels the blurred profile of e.g. a // rectangle will lie. - static bool BoxBlur(SkMask* dst, const SkMask& src, - SkScalar sigma, SkBlurStyle style, SkBlurQuality quality, - SkIPoint* margin = nullptr, bool force_quality=false); + static bool SK_WARN_UNUSED_RESULT BoxBlur(SkMask* dst, const SkMask& src, + SkScalar sigma, SkBlurStyle style, SkBlurQuality, + SkIPoint* margin = nullptr, + bool forceQuality = false); // the "ground truth" blur does a gaussian convolution; it's slow // but useful for comparison purposes. - static bool BlurGroundTruth(SkScalar sigma, SkMask* dst, const SkMask& src, SkBlurStyle, - SkIPoint* margin = nullptr); + static bool SK_WARN_UNUSED_RESULT BlurGroundTruth(SkScalar sigma, SkMask* dst, + const SkMask& src, + SkBlurStyle, SkIPoint* margin = nullptr); // If radius > 0, return the corresponding sigma, else return 0 static SkScalar ConvertRadiusToSigma(SkScalar radius); diff --git a/gfx/skia/skia/src/effects/SkBlurMaskFilter.cpp b/gfx/skia/skia/src/effects/SkBlurMaskFilter.cpp index 8dd82c5ac3..edd2fa4f69 100644 --- a/gfx/skia/skia/src/effects/SkBlurMaskFilter.cpp +++ b/gfx/skia/skia/src/effects/SkBlurMaskFilter.cpp @@ -24,14 +24,13 @@ #include "GrTexture.h" #include "GrFragmentProcessor.h" #include "GrInvariantOutput.h" -#include "SkGrPixelRef.h" #include "SkDraw.h" #include "effects/GrSimpleTextureEffect.h" #include "glsl/GrGLSLFragmentProcessor.h" #include "glsl/GrGLSLFragmentShaderBuilder.h" -#include "glsl/GrGLSLProgramBuilder.h" #include "glsl/GrGLSLProgramDataManager.h" #include "glsl/GrGLSLTextureSampler.h" +#include "glsl/GrGLSLUniformHandler.h" #endif SkScalar SkBlurMaskFilter::ConvertRadiusToSigma(SkScalar radius) { @@ -353,7 +352,6 @@ SkBlurMaskFilterImpl::filterRRectToNine(const SkRRect& rrect, const SkMatrix& ma SkIPoint margin; SkMask srcM, dstM; srcM.fBounds = rrect.rect().roundOut(); - srcM.fImage = nullptr; srcM.fFormat = SkMask::kA8_Format; srcM.fRowBytes = 0; @@ -473,7 +471,6 @@ SkBlurMaskFilterImpl::filterRectsToNine(const SkRect rects[], int count, SkIPoint margin; SkMask srcM, dstM; srcM.fBounds = rects[0].roundOut(); - srcM.fImage = nullptr; srcM.fFormat = SkMask::kA8_Format; srcM.fRowBytes = 0; @@ -720,21 +717,22 @@ void GrGLRectBlurEffect::GenKey(GrSLPrecision precision, GrProcessorKeyBuilder* void GrGLRectBlurEffect::emitCode(EmitArgs& args) { + GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; const char *rectName; const char *profileSizeName; const char* precisionString = GrGLSLShaderVar::PrecisionString(args.fGLSLCaps, fPrecision); - fProxyRectUniform = args.fBuilder->addUniform(GrGLSLProgramBuilder::kFragment_Visibility, - kVec4f_GrSLType, - fPrecision, - "proxyRect", - &rectName); - fProfileSizeUniform = args.fBuilder->addUniform(GrGLSLProgramBuilder::kFragment_Visibility, - kFloat_GrSLType, - kDefault_GrSLPrecision, - "profileSize", - &profileSizeName); + fProxyRectUniform = uniformHandler->addUniform(GrGLSLUniformHandler::kFragment_Visibility, + kVec4f_GrSLType, + fPrecision, + "proxyRect", + &rectName); + fProfileSizeUniform = uniformHandler->addUniform(GrGLSLUniformHandler::kFragment_Visibility, + kFloat_GrSLType, + kDefault_GrSLPrecision, + "profileSize", + &profileSizeName); GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder; const char *fragmentPos = fragBuilder->fragmentPosition(); @@ -988,8 +986,10 @@ const GrFragmentProcessor* GrRRectBlurEffect::Create(GrTextureProvider* texProvi SkMask::kJustRenderImage_CreateMode, SkPaint::kFill_Style); SkMask blurredMask; - SkBlurMask::BoxBlur(&blurredMask, mask, sigma, kNormal_SkBlurStyle, kHigh_SkBlurQuality, - nullptr, true); + if (!SkBlurMask::BoxBlur(&blurredMask, mask, sigma, kNormal_SkBlurStyle, + kHigh_SkBlurQuality, nullptr, true)) { + return nullptr; + } unsigned int texSide = smallRectSide + 2*blurRadius; GrSurfaceDesc texDesc; @@ -1063,24 +1063,25 @@ void GrGLRRectBlurEffect::emitCode(EmitArgs& args) { const char *cornerRadiusName; const char *blurRadiusName; + GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; // The proxy rect has left, top, right, and bottom edges correspond to // components x, y, z, and w, respectively. - fProxyRectUniform = args.fBuilder->addUniform(GrGLSLProgramBuilder::kFragment_Visibility, - kVec4f_GrSLType, - kDefault_GrSLPrecision, - "proxyRect", - &rectName); - fCornerRadiusUniform = args.fBuilder->addUniform(GrGLSLProgramBuilder::kFragment_Visibility, - kFloat_GrSLType, - kDefault_GrSLPrecision, - "cornerRadius", - &cornerRadiusName); - fBlurRadiusUniform = args.fBuilder->addUniform(GrGLSLProgramBuilder::kFragment_Visibility, - kFloat_GrSLType, + fProxyRectUniform = uniformHandler->addUniform(GrGLSLUniformHandler::kFragment_Visibility, + kVec4f_GrSLType, kDefault_GrSLPrecision, - "blurRadius", - &blurRadiusName); + "proxyRect", + &rectName); + fCornerRadiusUniform = uniformHandler->addUniform(GrGLSLUniformHandler::kFragment_Visibility, + kFloat_GrSLType, + kDefault_GrSLPrecision, + "cornerRadius", + &cornerRadiusName); + fBlurRadiusUniform = uniformHandler->addUniform(GrGLSLUniformHandler::kFragment_Visibility, + kFloat_GrSLType, + kDefault_GrSLPrecision, + "blurRadius", + &blurRadiusName); GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder; const char* fragmentPos = fragBuilder->fragmentPosition(); @@ -1239,8 +1240,7 @@ bool SkBlurMaskFilterImpl::filterMaskGPU(GrTexture* src, // gaussianBlur. Otherwise, we need to save it for later compositing. bool isNormalBlur = (kNormal_SkBlurStyle == fBlurStyle); *result = SkGpuBlurUtils::GaussianBlur(context, src, isNormalBlur && canOverwriteSrc, - clipRect, nullptr, xformedSigma, xformedSigma, - GrTextureProvider::kApprox_SizeConstraint); + clipRect, nullptr, xformedSigma, xformedSigma); if (nullptr == *result) { return false; } diff --git a/gfx/skia/skia/src/effects/SkColorCubeFilter.cpp b/gfx/skia/skia/src/effects/SkColorCubeFilter.cpp index a6f8e78e6e..5f1f10d53f 100644 --- a/gfx/skia/skia/src/effects/SkColorCubeFilter.cpp +++ b/gfx/skia/skia/src/effects/SkColorCubeFilter.cpp @@ -20,8 +20,8 @@ #include "SkGr.h" #include "glsl/GrGLSLFragmentProcessor.h" #include "glsl/GrGLSLFragmentShaderBuilder.h" -#include "glsl/GrGLSLProgramBuilder.h" #include "glsl/GrGLSLProgramDataManager.h" +#include "glsl/GrGLSLUniformHandler.h" #endif /////////////////////////////////////////////////////////////////////////////// @@ -99,7 +99,7 @@ void SkColorCubeFilter::ColorCubeProcesingCache::initProcessingLuts( // We need 256 SkScalar * 2 for fColorToFactors and 256 SkScalar // for fColorToScalar, so a total of 768 SkScalar. cache->fLutStorage.reset(512 * sizeof(int) + 768 * sizeof(SkScalar)); - uint8_t* storage = (uint8_t*)cache->fLutStorage.get(); + uint8_t* storage = cache->fLutStorage.get(); cache->fColorToIndex[0] = (int*)storage; cache->fColorToIndex[1] = cache->fColorToIndex[0] + 256; cache->fColorToFactors[0] = (SkScalar*)(storage + (512 * sizeof(int))); @@ -212,7 +212,7 @@ private: /////////////////////////////////////////////////////////////////////////////// GrColorCubeEffect::GrColorCubeEffect(GrTexture* colorCube) - : fColorCubeAccess(colorCube, "bgra", GrTextureParams::kBilerp_FilterMode) { + : fColorCubeAccess(colorCube, GrTextureParams::kBilerp_FilterMode) { this->initClassID(); this->addTextureAccess(&fColorCubeAccess); } @@ -246,14 +246,15 @@ void GrColorCubeEffect::GLSLProcessor::emitCode(EmitArgs& args) { args.fInputColor = "vec4(1)"; } - fColorCubeSizeUni = args.fBuilder->addUniform(GrGLSLProgramBuilder::kFragment_Visibility, - kFloat_GrSLType, kDefault_GrSLPrecision, - "Size"); - const char* colorCubeSizeUni = args.fBuilder->getUniformCStr(fColorCubeSizeUni); - fColorCubeInvSizeUni = args.fBuilder->addUniform(GrGLSLProgramBuilder::kFragment_Visibility, - kFloat_GrSLType, kDefault_GrSLPrecision, - "InvSize"); - const char* colorCubeInvSizeUni = args.fBuilder->getUniformCStr(fColorCubeInvSizeUni); + GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; + fColorCubeSizeUni = uniformHandler->addUniform(GrGLSLUniformHandler::kFragment_Visibility, + kFloat_GrSLType, kDefault_GrSLPrecision, + "Size"); + const char* colorCubeSizeUni = uniformHandler->getUniformCStr(fColorCubeSizeUni); + fColorCubeInvSizeUni = uniformHandler->addUniform(GrGLSLUniformHandler::kFragment_Visibility, + kFloat_GrSLType, kDefault_GrSLPrecision, + "InvSize"); + const char* colorCubeInvSizeUni = uniformHandler->getUniformCStr(fColorCubeInvSizeUni); const char* nonZeroAlpha = "nonZeroAlpha"; const char* unPMColor = "unPMColor"; @@ -286,11 +287,11 @@ void GrColorCubeEffect::GLSLProcessor::emitCode(EmitArgs& args) { // Apply the cube. fragBuilder->codeAppendf("%s = vec4(mix(", args.fOutputColor); fragBuilder->appendTextureLookup(args.fSamplers[0], cCoords1); - fragBuilder->codeAppend(".rgb, "); + fragBuilder->codeAppend(".bgr, "); fragBuilder->appendTextureLookup(args.fSamplers[0], cCoords2); // Premultiply color by alpha. Note that the input alpha is not modified by this shader. - fragBuilder->codeAppendf(".rgb, fract(%s.b)) * vec3(%s), %s.a);\n", + fragBuilder->codeAppendf(".bgr, fract(%s.b)) * vec3(%s), %s.a);\n", cubeIdx, nonZeroAlpha, args.fInputColor); } diff --git a/gfx/skia/skia/src/effects/SkColorFilterImageFilter.cpp b/gfx/skia/skia/src/effects/SkColorFilterImageFilter.cpp index 8d394aa17b..b0e47505a7 100644 --- a/gfx/skia/skia/src/effects/SkColorFilterImageFilter.cpp +++ b/gfx/skia/skia/src/effects/SkColorFilterImageFilter.cpp @@ -99,8 +99,11 @@ bool SkColorFilterImageFilter::onIsColorFilterNode(SkColorFilter** filter) const return false; } -bool SkColorFilterImageFilter::affectsTransparentBlack() const { - return fColorFilter->affectsTransparentBlack(); +bool SkColorFilterImageFilter::canComputeFastBounds() const { + if (fColorFilter->affectsTransparentBlack()) { + return false; + } + return INHERITED::canComputeFastBounds(); } #ifndef SK_IGNORE_TO_STRING diff --git a/gfx/skia/skia/src/effects/SkColorMatrixFilter.cpp b/gfx/skia/skia/src/effects/SkColorMatrixFilter.cpp index 6e1c8ddb81..ce6ca5538b 100644 --- a/gfx/skia/skia/src/effects/SkColorMatrixFilter.cpp +++ b/gfx/skia/skia/src/effects/SkColorMatrixFilter.cpp @@ -33,199 +33,26 @@ static void transpose_to_pmorder(float dst[20], const float src[20]) { } } -static int32_t rowmul4(const int32_t array[], unsigned r, unsigned g, - unsigned b, unsigned a) { - return array[0] * r + array[1] * g + array[2] * b + array[3] * a + array[4]; -} - -static int32_t rowmul3(const int32_t array[], unsigned r, unsigned g, - unsigned b) { - return array[0] * r + array[1] * g + array[2] * b + array[4]; -} - -static void General(const SkColorMatrixFilter::State& state, - unsigned r, unsigned g, unsigned b, unsigned a, - int32_t* SK_RESTRICT result) { - const int32_t* SK_RESTRICT array = state.fArray; - const int shift = state.fShift; - - result[0] = rowmul4(&array[0], r, g, b, a) >> shift; - result[1] = rowmul4(&array[5], r, g, b, a) >> shift; - result[2] = rowmul4(&array[10], r, g, b, a) >> shift; - result[3] = rowmul4(&array[15], r, g, b, a) >> shift; -} - -static void General16(const SkColorMatrixFilter::State& state, - unsigned r, unsigned g, unsigned b, unsigned a, - int32_t* SK_RESTRICT result) { - const int32_t* SK_RESTRICT array = state.fArray; - - result[0] = rowmul4(&array[0], r, g, b, a) >> 16; - result[1] = rowmul4(&array[5], r, g, b, a) >> 16; - result[2] = rowmul4(&array[10], r, g, b, a) >> 16; - result[3] = rowmul4(&array[15], r, g, b, a) >> 16; -} - -static void AffineAdd(const SkColorMatrixFilter::State& state, - unsigned r, unsigned g, unsigned b, unsigned a, - int32_t* SK_RESTRICT result) { - const int32_t* SK_RESTRICT array = state.fArray; - const int shift = state.fShift; - - result[0] = rowmul3(&array[0], r, g, b) >> shift; - result[1] = rowmul3(&array[5], r, g, b) >> shift; - result[2] = rowmul3(&array[10], r, g, b) >> shift; - result[3] = a; -} - -static void AffineAdd16(const SkColorMatrixFilter::State& state, - unsigned r, unsigned g, unsigned b, unsigned a, - int32_t* SK_RESTRICT result) { - const int32_t* SK_RESTRICT array = state.fArray; - - result[0] = rowmul3(&array[0], r, g, b) >> 16; - result[1] = rowmul3(&array[5], r, g, b) >> 16; - result[2] = rowmul3(&array[10], r, g, b) >> 16; - result[3] = a; -} - -static void ScaleAdd(const SkColorMatrixFilter::State& state, - unsigned r, unsigned g, unsigned b, unsigned a, - int32_t* SK_RESTRICT result) { - const int32_t* SK_RESTRICT array = state.fArray; - const int shift = state.fShift; - - // cast to (int) to keep the expression signed for the shift - result[0] = (array[SkColorMatrix::kR_Scale] * (int)r + array[4]) >> shift; - result[1] = (array[SkColorMatrix::kG_Scale] * (int)g + array[9]) >> shift; - result[2] = (array[SkColorMatrix::kB_Scale] * (int)b + array[14]) >> shift; - result[3] = a; -} - -static void ScaleAdd16(const SkColorMatrixFilter::State& state, - unsigned r, unsigned g, unsigned b, unsigned a, - int32_t* SK_RESTRICT result) { - const int32_t* SK_RESTRICT array = state.fArray; - - // cast to (int) to keep the expression signed for the shift - result[0] = (array[SkColorMatrix::kR_Scale] * (int)r + array[4]) >> 16; - result[1] = (array[SkColorMatrix::kG_Scale] * (int)g + array[9]) >> 16; - result[2] = (array[SkColorMatrix::kB_Scale] * (int)b + array[14]) >> 16; - result[3] = a; -} - -static void Add(const SkColorMatrixFilter::State& state, - unsigned r, unsigned g, unsigned b, unsigned a, - int32_t* SK_RESTRICT result) { - const int32_t* SK_RESTRICT array = state.fArray; - const int shift = state.fShift; - - result[0] = r + (array[SkColorMatrix::kR_Trans] >> shift); - result[1] = g + (array[SkColorMatrix::kG_Trans] >> shift); - result[2] = b + (array[SkColorMatrix::kB_Trans] >> shift); - result[3] = a; -} - -static void Add16(const SkColorMatrixFilter::State& state, - unsigned r, unsigned g, unsigned b, unsigned a, - int32_t* SK_RESTRICT result) { - const int32_t* SK_RESTRICT array = state.fArray; - - result[0] = r + (array[SkColorMatrix::kR_Trans] >> 16); - result[1] = g + (array[SkColorMatrix::kG_Trans] >> 16); - result[2] = b + (array[SkColorMatrix::kB_Trans] >> 16); - result[3] = a; -} - // src is [20] but some compilers won't accept __restrict__ on anything // but an raw pointer or reference void SkColorMatrixFilter::initState(const SkScalar* SK_RESTRICT src) { transpose_to_pmorder(fTranspose, src); - int32_t* array = fState.fArray; - SkFixed max = 0; - for (int i = 0; i < 20; i++) { - SkFixed value = SkScalarToFixed(src[i]); - array[i] = value; - value = SkAbs32(value); - max = SkMax32(max, value); - } - - /* All of fArray[] values must fit in 23 bits, to safely allow me to - multiply them by 8bit unsigned values, and get a signed answer without - overflow. This means clz needs to be 9 or bigger - */ - int bits = SkCLZ(max); - int32_t one = SK_Fixed1; - - fState.fShift = 16; // we are starting out as fixed 16.16 - if (bits < 9) { - bits = 9 - bits; - fState.fShift -= bits; - for (int i = 0; i < 20; i++) { - array[i] >>= bits; - } - one >>= bits; - } + const float* array = fMatrix.fMat; // check if we have to munge Alpha - int32_t changesAlpha = (array[15] | array[16] | array[17] | - (array[18] - one) | array[19]); - int32_t usesAlpha = (array[3] | array[8] | array[13]); - bool shiftIs16 = (16 == fState.fShift); + bool changesAlpha = (array[15] || array[16] || array[17] || (array[18] - 1) || array[19]); + bool usesAlpha = (array[3] || array[8] || array[13]); - if (changesAlpha | usesAlpha) { - fProc = shiftIs16 ? General16 : General; + if (changesAlpha || usesAlpha) { fFlags = changesAlpha ? 0 : SkColorFilter::kAlphaUnchanged_Flag; } else { fFlags = SkColorFilter::kAlphaUnchanged_Flag; - - int32_t needsScale = (array[SkColorMatrix::kR_Scale] - one) | - (array[SkColorMatrix::kG_Scale] - one) | - (array[SkColorMatrix::kB_Scale] - one); - - int32_t needs3x3 = array[1] | array[2] | // red off-axis - array[5] | array[7] | // green off-axis - array[10] | array[11]; // blue off-axis - - if (needs3x3) { - fProc = shiftIs16 ? AffineAdd16 : AffineAdd; - } else if (needsScale) { - fProc = shiftIs16 ? ScaleAdd16 : ScaleAdd; - } else if (array[SkColorMatrix::kR_Trans] | - array[SkColorMatrix::kG_Trans] | - array[SkColorMatrix::kB_Trans]) { - fProc = shiftIs16 ? Add16 : Add; - } else { - fProc = nullptr; // identity - } - } - - /* preround our add values so we get a rounded shift. We do this after we - analyze the array, so we don't miss the case where the caller has zeros - which could make us accidentally take the General or Add case. - */ - if (fProc) { - int32_t add = 1 << (fState.fShift - 1); - array[4] += add; - array[9] += add; - array[14] += add; - array[19] += add; } } /////////////////////////////////////////////////////////////////////////////// -static int32_t pin(int32_t value, int32_t max) { - if (value < 0) { - value = 0; - } - if (value > max) { - value = max; - } - return value; -} - SkColorMatrixFilter::SkColorMatrixFilter(const SkColorMatrix& cm) : fMatrix(cm) { this->initState(cm.fMat); } @@ -258,95 +85,45 @@ static Sk4f clamp_0_1(const Sk4f& x) { static SkPMColor round(const Sk4f& x) { SkPMColor c; - (x * Sk4f(255) + Sk4f(0.5f)).toBytes((uint8_t*)&c); + SkNx_cast(x * Sk4f(255) + Sk4f(0.5f)).store((uint8_t*)&c); return c; } void SkColorMatrixFilter::filterSpan(const SkPMColor src[], int count, SkPMColor dst[]) const { - Proc proc = fProc; - if (nullptr == proc) { - if (src != dst) { - memcpy(dst, src, count * sizeof(SkPMColor)); + // c0-c3 are already in [0,1]. + const Sk4f c0 = Sk4f::Load(fTranspose + 0); + const Sk4f c1 = Sk4f::Load(fTranspose + 4); + const Sk4f c2 = Sk4f::Load(fTranspose + 8); + const Sk4f c3 = Sk4f::Load(fTranspose + 12); + // c4 (the translate vector) is in [0, 255]. Bring it back to [0,1]. + const Sk4f c4 = Sk4f::Load(fTranspose + 16)*Sk4f(1.0f/255); + + // todo: we could cache this in the constructor... + SkPMColor matrix_translate_pmcolor = round(premul(clamp_0_1(c4))); + + for (int i = 0; i < count; i++) { + const SkPMColor src_c = src[i]; + if (0 == src_c) { + dst[i] = matrix_translate_pmcolor; + continue; } - return; - } -#ifdef SK_SUPPORT_LEGACY_INT_COLORMATRIX - const bool use_floats = false; -#else - const bool use_floats = true; -#endif + Sk4f srcf = SkNx_cast(Sk4b::Load((const uint8_t*)&src_c)) * Sk4f(1.0f/255); - if (use_floats) { - // c0-c3 are already in [0,1]. - const Sk4f c0 = Sk4f::Load(fTranspose + 0); - const Sk4f c1 = Sk4f::Load(fTranspose + 4); - const Sk4f c2 = Sk4f::Load(fTranspose + 8); - const Sk4f c3 = Sk4f::Load(fTranspose + 12); - // c4 (the translate vector) is in [0, 255]. Bring it back to [0,1]. - const Sk4f c4 = Sk4f::Load(fTranspose + 16)*Sk4f(1.0f/255); - - // todo: we could cache this in the constructor... - SkPMColor matrix_translate_pmcolor = round(premul(clamp_0_1(c4))); - - for (int i = 0; i < count; i++) { - const SkPMColor src_c = src[i]; - if (0 == src_c) { - dst[i] = matrix_translate_pmcolor; - continue; - } - - Sk4f srcf = Sk4f::FromBytes((const uint8_t*)&src_c) * Sk4f(1.0f/255); - - if (0xFF != SkGetPackedA32(src_c)) { - srcf = unpremul(srcf); - } - - Sk4f r4 = SkNx_dup(srcf); - Sk4f g4 = SkNx_dup(srcf); - Sk4f b4 = SkNx_dup(srcf); - Sk4f a4 = SkNx_dup(srcf); - - // apply matrix - Sk4f dst4 = c0 * r4 + c1 * g4 + c2 * b4 + c3 * a4 + c4; - - // clamp, re-premul, and write - dst[i] = round(premul(clamp_0_1(dst4))); + if (0xFF != SkGetPackedA32(src_c)) { + srcf = unpremul(srcf); } - } else { - const State& state = fState; - int32_t result[4]; - const SkUnPreMultiply::Scale* table = SkUnPreMultiply::GetScaleTable(); - for (int i = 0; i < count; i++) { - SkPMColor c = src[i]; + Sk4f r4 = SkNx_dup(srcf); + Sk4f g4 = SkNx_dup(srcf); + Sk4f b4 = SkNx_dup(srcf); + Sk4f a4 = SkNx_dup(srcf); - unsigned r = SkGetPackedR32(c); - unsigned g = SkGetPackedG32(c); - unsigned b = SkGetPackedB32(c); - unsigned a = SkGetPackedA32(c); + // apply matrix + Sk4f dst4 = c0 * r4 + c1 * g4 + c2 * b4 + c3 * a4 + c4; - // need our components to be un-premultiplied - if (255 != a) { - SkUnPreMultiply::Scale scale = table[a]; - r = SkUnPreMultiply::ApplyScale(scale, r); - g = SkUnPreMultiply::ApplyScale(scale, g); - b = SkUnPreMultiply::ApplyScale(scale, b); - - SkASSERT(r <= 255); - SkASSERT(g <= 255); - SkASSERT(b <= 255); - } - - proc(state, r, g, b, a, result); - - r = pin(result[0], SK_R32_MASK); - g = pin(result[1], SK_G32_MASK); - b = pin(result[2], SK_B32_MASK); - a = pin(result[3], SK_A32_MASK); - // re-prepremultiply if needed - dst[i] = SkPremultiplyARGBInline(a, r, g, b); - } + // clamp, re-premul, and write + dst[i] = round(premul(clamp_0_1(dst4))); } } @@ -387,8 +164,8 @@ SkColorFilter* SkColorMatrixFilter::newComposed(const SkColorFilter* innerFilter #include "GrInvariantOutput.h" #include "glsl/GrGLSLFragmentProcessor.h" #include "glsl/GrGLSLFragmentShaderBuilder.h" -#include "glsl/GrGLSLProgramBuilder.h" #include "glsl/GrGLSLProgramDataManager.h" +#include "glsl/GrGLSLUniformHandler.h" class ColorMatrixEffect : public GrFragmentProcessor { public: @@ -408,12 +185,13 @@ public: GLSLProcessor(const GrProcessor&) {} virtual void emitCode(EmitArgs& args) override { - fMatrixHandle = args.fBuilder->addUniform(GrGLSLProgramBuilder::kFragment_Visibility, - kMat44f_GrSLType, kDefault_GrSLPrecision, - "ColorMatrix"); - fVectorHandle = args.fBuilder->addUniform(GrGLSLProgramBuilder::kFragment_Visibility, - kVec4f_GrSLType, kDefault_GrSLPrecision, - "ColorMatrixVector"); + GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; + fMatrixHandle = uniformHandler->addUniform(GrGLSLUniformHandler::kFragment_Visibility, + kMat44f_GrSLType, kDefault_GrSLPrecision, + "ColorMatrix"); + fVectorHandle = uniformHandler->addUniform(GrGLSLUniformHandler::kFragment_Visibility, + kVec4f_GrSLType, kDefault_GrSLPrecision, + "ColorMatrixVector"); if (nullptr == args.fInputColor) { // could optimize this case, but we aren't for now. @@ -426,9 +204,9 @@ public: args.fInputColor); fragBuilder->codeAppendf("\t%s = %s * vec4(%s.rgb / nonZeroAlpha, nonZeroAlpha) + %s;\n", args.fOutputColor, - args.fBuilder->getUniformCStr(fMatrixHandle), + uniformHandler->getUniformCStr(fMatrixHandle), args.fInputColor, - args.fBuilder->getUniformCStr(fVectorHandle)); + uniformHandler->getUniformCStr(fVectorHandle)); fragBuilder->codeAppendf("\t%s = clamp(%s, 0.0, 1.0);\n", args.fOutputColor, args.fOutputColor); fragBuilder->codeAppendf("\t%s.rgb *= %s.a;\n", args.fOutputColor, args.fOutputColor); @@ -560,3 +338,27 @@ void SkColorMatrixFilter::toString(SkString* str) const { str->append(")"); } #endif + +/////////////////////////////////////////////////////////////////////////////// + +static SkScalar byte_to_scale(U8CPU byte) { + if (0xFF == byte) { + // want to get this exact + return 1; + } else { + return byte * 0.00392156862745f; + } +} + +SkColorFilter* SkColorMatrixFilter::CreateLightingFilter(SkColor mul, SkColor add) { + SkColorMatrix matrix; + matrix.setScale(byte_to_scale(SkColorGetR(mul)), + byte_to_scale(SkColorGetG(mul)), + byte_to_scale(SkColorGetB(mul)), + 1); + matrix.postTranslate(SkIntToScalar(SkColorGetR(add)), + SkIntToScalar(SkColorGetG(add)), + SkIntToScalar(SkColorGetB(add)), + 0); + return SkColorMatrixFilter::Create(matrix); +} diff --git a/gfx/skia/skia/src/effects/SkComposeImageFilter.cpp b/gfx/skia/skia/src/effects/SkComposeImageFilter.cpp index 1be03a3370..a922d30d1e 100644 --- a/gfx/skia/skia/src/effects/SkComposeImageFilter.cpp +++ b/gfx/skia/skia/src/effects/SkComposeImageFilter.cpp @@ -35,8 +35,10 @@ bool SkComposeImageFilter::onFilterImage(Proxy* proxy, SkMatrix outerMatrix(ctx.ctm()); outerMatrix.postTranslate(SkIntToScalar(-innerOffset.x()), SkIntToScalar(-innerOffset.y())); - Context outerContext(outerMatrix, ctx.clipBounds(), ctx.cache(), ctx.sizeConstraint()); - if (!this->filterInput(0, proxy, tmp, outerContext, result, &outerOffset, false)) { + SkIRect clipBounds = ctx.clipBounds(); + clipBounds.offset(-innerOffset.x(), -innerOffset.y()); + Context outerContext(outerMatrix, clipBounds, ctx.cache()); + if (!this->filterInput(0, proxy, tmp, outerContext, result, &outerOffset)) { return false; } diff --git a/gfx/skia/skia/src/effects/SkDisplacementMapEffect.cpp b/gfx/skia/skia/src/effects/SkDisplacementMapEffect.cpp index fe918581c4..30f980a5c6 100644 --- a/gfx/skia/skia/src/effects/SkDisplacementMapEffect.cpp +++ b/gfx/skia/skia/src/effects/SkDisplacementMapEffect.cpp @@ -16,11 +16,12 @@ #include "GrDrawContext.h" #include "GrCoordTransform.h" #include "GrInvariantOutput.h" +#include "SkGr.h" #include "effects/GrTextureDomain.h" #include "glsl/GrGLSLFragmentProcessor.h" #include "glsl/GrGLSLFragmentShaderBuilder.h" -#include "glsl/GrGLSLProgramBuilder.h" #include "glsl/GrGLSLProgramDataManager.h" +#include "glsl/GrGLSLUniformHandler.h" #endif namespace { @@ -164,12 +165,12 @@ bool channel_selector_type_is_valid(SkDisplacementMapEffect::ChannelSelectorType /////////////////////////////////////////////////////////////////////////////// -SkDisplacementMapEffect* SkDisplacementMapEffect::Create(ChannelSelectorType xChannelSelector, - ChannelSelectorType yChannelSelector, - SkScalar scale, - SkImageFilter* displacement, - SkImageFilter* color, - const CropRect* cropRect) { +SkImageFilter* SkDisplacementMapEffect::Create(ChannelSelectorType xChannelSelector, + ChannelSelectorType yChannelSelector, + SkScalar scale, + SkImageFilter* displacement, + SkImageFilter* color, + const CropRect* cropRect) { if (!channel_selector_type_is_valid(xChannelSelector) || !channel_selector_type_is_valid(yChannelSelector)) { return nullptr; @@ -268,16 +269,22 @@ void SkDisplacementMapEffect::computeFastBounds(const SkRect& src, SkRect* dst) } else { *dst = src; } - dst->outset(fScale * SK_ScalarHalf, fScale * SK_ScalarHalf); + dst->outset(SkScalarAbs(fScale) * SK_ScalarHalf, SkScalarAbs(fScale) * SK_ScalarHalf); +} + +void SkDisplacementMapEffect::onFilterNodeBounds(const SkIRect& src, const SkMatrix& ctm, + SkIRect* dst, MapDirection) const { + *dst = src; + SkVector scale = SkVector::Make(fScale, fScale); + ctm.mapVectors(&scale, 1); + dst->outset(SkScalarCeilToInt(SkScalarAbs(scale.fX) * SK_ScalarHalf), + SkScalarCeilToInt(SkScalarAbs(scale.fY) * SK_ScalarHalf)); } bool SkDisplacementMapEffect::onFilterBounds(const SkIRect& src, const SkMatrix& ctm, SkIRect* dst) const { - SkIRect bounds = src; - SkVector scale = SkVector::Make(fScale, fScale); - ctm.mapVectors(&scale, 1); - bounds.outset(SkScalarCeilToInt(scale.fX * SK_ScalarHalf), - SkScalarCeilToInt(scale.fY * SK_ScalarHalf)); + SkIRect bounds; + this->onFilterNodeBounds(src, ctm, &bounds, kReverse_MapDirection); if (this->getColorInput()) { return this->getColorInput()->filterBounds(bounds, ctm, dst); } @@ -422,8 +429,7 @@ bool SkDisplacementMapEffect::filterImageGPU(Proxy* proxy, const SkBitmap& src, desc.fHeight = bounds.height(); desc.fConfig = kSkia8888_GrPixelConfig; - auto constraint = GrTextureProvider::FromImageFilter(ctx.sizeConstraint()); - SkAutoTUnref dst(context->textureProvider()->createTexture(desc, constraint)); + SkAutoTUnref dst(context->textureProvider()->createApproxTexture(desc)); if (!dst) { return false; @@ -460,7 +466,7 @@ bool SkDisplacementMapEffect::filterImageGPU(Proxy* proxy, const SkBitmap& src, drawContext->drawRect(GrClip::WideOpen(), paint, matrix, SkRect::Make(colorBounds)); offset->fX = bounds.left(); offset->fY = bounds.top(); - WrapTexture(dst, bounds.width(), bounds.height(), result); + GrWrapTextureInBitmap(dst, bounds.width(), bounds.height(), false, result); return true; } @@ -549,9 +555,9 @@ GrGLDisplacementMapEffect::~GrGLDisplacementMapEffect() { void GrGLDisplacementMapEffect::emitCode(EmitArgs& args) { const GrTextureDomain& domain = args.fFp.cast().domain(); - fScaleUni = args.fBuilder->addUniform(GrGLSLProgramBuilder::kFragment_Visibility, - kVec2f_GrSLType, kDefault_GrSLPrecision, "Scale"); - const char* scaleUni = args.fBuilder->getUniformCStr(fScaleUni); + fScaleUni = args.fUniformHandler->addUniform(GrGLSLUniformHandler::kFragment_Visibility, + kVec2f_GrSLType, kDefault_GrSLPrecision, "Scale"); + const char* scaleUni = args.fUniformHandler->getUniformCStr(fScaleUni); const char* dColor = "dColor"; const char* cCoords = "cCoords"; const char* nearZero = "1e-6"; // Since 6.10352e−5 is the smallest half float, use @@ -610,6 +616,7 @@ void GrGLDisplacementMapEffect::emitCode(EmitArgs& args) { fragBuilder->codeAppend("-vec2(0.5));\t\t"); fGLDomain.sampleTexture(fragBuilder, + args.fUniformHandler, args.fGLSLCaps, domain, args.fOutputColor, diff --git a/gfx/skia/skia/src/effects/SkDropShadowImageFilter.cpp b/gfx/skia/skia/src/effects/SkDropShadowImageFilter.cpp index 7519d5313d..eb05cf09ec 100644 --- a/gfx/skia/skia/src/effects/SkDropShadowImageFilter.cpp +++ b/gfx/skia/skia/src/effects/SkDropShadowImageFilter.cpp @@ -116,25 +116,23 @@ void SkDropShadowImageFilter::computeFastBounds(const SkRect& src, SkRect* dst) } } -bool SkDropShadowImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm, - SkIRect* dst) const { - SkIRect bounds = src; +void SkDropShadowImageFilter::onFilterNodeBounds(const SkIRect& src, const SkMatrix& ctm, + SkIRect* dst, MapDirection direction) const { + *dst = src; SkVector offsetVec = SkVector::Make(fDx, fDy); + if (kReverse_MapDirection == direction) { + offsetVec.negate(); + } ctm.mapVectors(&offsetVec, 1); - bounds.offset(-SkScalarCeilToInt(offsetVec.x()), - -SkScalarCeilToInt(offsetVec.y())); + dst->offset(SkScalarCeilToInt(offsetVec.x()), + SkScalarCeilToInt(offsetVec.y())); SkVector sigma = SkVector::Make(fSigmaX, fSigmaY); ctm.mapVectors(&sigma, 1); - bounds.outset(SkScalarCeilToInt(SkScalarMul(sigma.x(), SkIntToScalar(3))), - SkScalarCeilToInt(SkScalarMul(sigma.y(), SkIntToScalar(3)))); + dst->outset(SkScalarCeilToInt(SkScalarMul(sigma.x(), SkIntToScalar(3))), + SkScalarCeilToInt(SkScalarMul(sigma.y(), SkIntToScalar(3)))); if (fShadowMode == kDrawShadowAndForeground_ShadowMode) { - bounds.join(src); + dst->join(src); } - if (getInput(0) && !getInput(0)->filterBounds(bounds, ctm, &bounds)) { - return false; - } - *dst = bounds; - return true; } #ifndef SK_IGNORE_TO_STRING diff --git a/gfx/skia/skia/src/effects/SkEmbossMaskFilter.cpp b/gfx/skia/skia/src/effects/SkEmbossMaskFilter.cpp index 2607e255d9..64afa49447 100644 --- a/gfx/skia/skia/src/effects/SkEmbossMaskFilter.cpp +++ b/gfx/skia/skia/src/effects/SkEmbossMaskFilter.cpp @@ -13,7 +13,7 @@ #include "SkWriteBuffer.h" #include "SkString.h" -SkEmbossMaskFilter* SkEmbossMaskFilter::Create(SkScalar blurSigma, const Light& light) { +SkMaskFilter* SkEmbossMaskFilter::Create(SkScalar blurSigma, const Light& light) { return new SkEmbossMaskFilter(blurSigma, light); } diff --git a/gfx/skia/skia/src/effects/SkGpuBlurUtils.cpp b/gfx/skia/skia/src/effects/SkGpuBlurUtils.cpp index 8ab05dd862..77a8501877 100644 --- a/gfx/skia/skia/src/effects/SkGpuBlurUtils.cpp +++ b/gfx/skia/skia/src/effects/SkGpuBlurUtils.cpp @@ -60,21 +60,21 @@ static void convolve_gaussian_1d(GrDrawContext* drawContext, texture, direction, radius, sigma, useBounds, bounds)); paint.addColorFragmentProcessor(conv); paint.setPorterDuffXPFactory(SkXfermode::kSrc_Mode); - SkMatrix localMatrix = SkMatrix::MakeTrans(srcOffset.x(), srcOffset.y()); + SkMatrix localMatrix = SkMatrix::MakeTrans(-srcOffset.x(), -srcOffset.y()); drawContext->fillRectWithLocalMatrix(clip, paint, SkMatrix::I(), dstRect, localMatrix); } static void convolve_gaussian_2d(GrDrawContext* drawContext, const GrClip& clip, - const SkRect& srcRect, + const SkRect& dstRect, + const SkPoint& srcOffset, GrTexture* texture, int radiusX, int radiusY, SkScalar sigmaX, SkScalar sigmaY, const SkRect* srcBounds) { - SkRect dstRect = SkRect::MakeWH(srcRect.width(), srcRect.height()); - SkMatrix localMatrix = SkMatrix::MakeTrans(srcRect.x(), srcRect.y()); + SkMatrix localMatrix = SkMatrix::MakeTrans(-srcOffset.x(), -srcOffset.y()); SkISize size = SkISize::Make(2 * radiusX + 1, 2 * radiusY + 1); SkIPoint kernelOffset = SkIPoint::Make(radiusX, radiusY); GrPaint paint; @@ -148,15 +148,15 @@ static void convolve_gaussian(GrDrawContext* drawContext, } if (midRect.isEmpty()) { // Blur radius covers srcBounds; use bounds over entire draw - convolve_gaussian_1d(drawContext, clip, dstRect, -srcOffset, texture, + convolve_gaussian_1d(drawContext, clip, dstRect, srcOffset, texture, direction, radius, sigma, true, bounds); } else { // Draw right and left margins with bounds; middle without. - convolve_gaussian_1d(drawContext, clip, leftRect, -srcOffset, texture, + convolve_gaussian_1d(drawContext, clip, leftRect, srcOffset, texture, direction, radius, sigma, true, bounds); - convolve_gaussian_1d(drawContext, clip, rightRect, -srcOffset, texture, + convolve_gaussian_1d(drawContext, clip, rightRect, srcOffset, texture, direction, radius, sigma, true, bounds); - convolve_gaussian_1d(drawContext, clip, midRect, -srcOffset, texture, + convolve_gaussian_1d(drawContext, clip, midRect, srcOffset, texture, direction, radius, sigma, false, bounds); } } @@ -167,8 +167,7 @@ GrTexture* GaussianBlur(GrContext* context, const SkRect& dstBounds, const SkRect* srcBounds, float sigmaX, - float sigmaY, - GrTextureProvider::SizeConstraint constraint) { + float sigmaY) { SkASSERT(context); SkIRect clearRect; int scaleFactorX, radiusX; @@ -211,12 +210,12 @@ GrTexture* GaussianBlur(GrContext* context, GrTexture* tempTexture; SkAutoTUnref temp1, temp2; - temp1.reset(context->textureProvider()->createTexture(desc, constraint)); + temp1.reset(context->textureProvider()->createApproxTexture(desc)); dstTexture = temp1.get(); if (canClobberSrc) { tempTexture = srcTexture; } else { - temp2.reset(context->textureProvider()->createTexture(desc, constraint)); + temp2.reset(context->textureProvider()->createApproxTexture(desc)); tempTexture = temp2.get(); } @@ -269,6 +268,7 @@ GrTexture* GaussianBlur(GrContext* context, // For really small blurs (certainly no wider than 5x5 on desktop gpus) it is faster to just // launch a single non separable kernel vs two launches + srcRect = localDstBounds; if (sigmaX > 0.0f && sigmaY > 0.0f && (2 * radiusX + 1) * (2 * radiusY + 1) <= MAX_KERNEL_SIZE) { // We shouldn't be scaling because this is a small size blur @@ -279,7 +279,7 @@ GrTexture* GaussianBlur(GrContext* context, if (!dstDrawContext) { return nullptr; } - convolve_gaussian_2d(dstDrawContext, clip, srcRect, + convolve_gaussian_2d(dstDrawContext, clip, srcRect, srcOffset, srcTexture, radiusX, radiusY, sigmaX, sigmaY, srcBounds); srcDrawContext.swap(dstDrawContext); @@ -288,7 +288,6 @@ GrTexture* GaussianBlur(GrContext* context, SkTSwap(dstTexture, tempTexture); } else { - srcRect = localDstBounds; scale_rect(&srcRect, 1.0f / scaleFactorX, 1.0f / scaleFactorY); srcRect.roundOut(&srcRect); const SkIRect srcIRect = srcRect.roundOut(); diff --git a/gfx/skia/skia/src/effects/SkGpuBlurUtils.h b/gfx/skia/skia/src/effects/SkGpuBlurUtils.h index 84e3252cc2..013d11b517 100644 --- a/gfx/skia/skia/src/effects/SkGpuBlurUtils.h +++ b/gfx/skia/skia/src/effects/SkGpuBlurUtils.h @@ -40,8 +40,7 @@ namespace SkGpuBlurUtils { const SkRect& dstBounds, const SkRect* srcBounds, float sigmaX, - float sigmaY, - GrTextureProvider::SizeConstraint); + float sigmaY); #endif }; diff --git a/gfx/skia/skia/src/effects/SkLightingImageFilter.cpp b/gfx/skia/skia/src/effects/SkLightingImageFilter.cpp index 9b6a20f0ef..f701b4c894 100644 --- a/gfx/skia/skia/src/effects/SkLightingImageFilter.cpp +++ b/gfx/skia/skia/src/effects/SkLightingImageFilter.cpp @@ -20,11 +20,12 @@ #include "GrFragmentProcessor.h" #include "GrInvariantOutput.h" #include "GrPaint.h" +#include "SkGr.h" #include "effects/GrSingleTextureEffect.h" #include "glsl/GrGLSLFragmentProcessor.h" #include "glsl/GrGLSLFragmentShaderBuilder.h" -#include "glsl/GrGLSLProgramBuilder.h" #include "glsl/GrGLSLProgramDataManager.h" +#include "glsl/GrGLSLUniformHandler.h" class GrGLDiffuseLightingEffect; class GrGLSpecularLightingEffect; @@ -383,8 +384,7 @@ bool SkLightingImageFilterInternal::filterImageGPU(Proxy* proxy, desc.fHeight = bounds.height(); desc.fConfig = kRGBA_8888_GrPixelConfig; - auto constraint = GrTextureProvider::FromImageFilter(ctx.sizeConstraint()); - SkAutoTUnref dst(context->textureProvider()->createTexture(desc, constraint)); + SkAutoTUnref dst(context->textureProvider()->createApproxTexture(desc)); if (!dst) { return false; } @@ -425,7 +425,7 @@ bool SkLightingImageFilterInternal::filterImageGPU(Proxy* proxy, this->drawRect(drawContext, srcTexture, matrix, clip, bottom, kBottom_BoundaryMode, bounds); this->drawRect(drawContext, srcTexture, matrix, clip, bottomRight, kBottomRight_BoundaryMode, bounds); - WrapTexture(dst, bounds.width(), bounds.height(), result); + GrWrapTextureInBitmap(dst, bounds.width(), bounds.height(), false, result); return true; } #endif @@ -599,7 +599,7 @@ public: * This is called by GrGLLightingEffect::emitCode() before either of the two virtual functions * below. It adds a vec3f uniform visible in the FS that represents the constant light color. */ - void emitLightColorUniform(GrGLSLFPBuilder*); + void emitLightColorUniform(GrGLSLUniformHandler*); /** * These two functions are called from GrGLLightingEffect's emitCode() function. @@ -609,8 +609,10 @@ public: * the FS. The default of emitLightColor appends the name of the constant light color uniform * and so this function only needs to be overridden if the light color varies spatially. */ - virtual void emitSurfaceToLight(GrGLSLFPBuilder*, GrGLSLFragmentBuilder*, const char* z) = 0; - virtual void emitLightColor(GrGLSLFPBuilder*, + virtual void emitSurfaceToLight(GrGLSLUniformHandler*, + GrGLSLFragmentBuilder*, + const char* z) = 0; + virtual void emitLightColor(GrGLSLUniformHandler*, GrGLSLFragmentBuilder*, const char *surfaceToLight); @@ -637,7 +639,7 @@ class GrGLDistantLight : public GrGLLight { public: virtual ~GrGLDistantLight() {} void setData(const GrGLSLProgramDataManager&, const SkImageFilterLight* light) const override; - void emitSurfaceToLight(GrGLSLFPBuilder*, GrGLSLFragmentBuilder*, const char* z) override; + void emitSurfaceToLight(GrGLSLUniformHandler*, GrGLSLFragmentBuilder*, const char* z) override; private: typedef GrGLLight INHERITED; @@ -650,7 +652,7 @@ class GrGLPointLight : public GrGLLight { public: virtual ~GrGLPointLight() {} void setData(const GrGLSLProgramDataManager&, const SkImageFilterLight* light) const override; - void emitSurfaceToLight(GrGLSLFPBuilder*, GrGLSLFragmentBuilder*, const char* z) override; + void emitSurfaceToLight(GrGLSLUniformHandler*, GrGLSLFragmentBuilder*, const char* z) override; private: typedef GrGLLight INHERITED; @@ -663,8 +665,8 @@ class GrGLSpotLight : public GrGLLight { public: virtual ~GrGLSpotLight() {} void setData(const GrGLSLProgramDataManager&, const SkImageFilterLight* light) const override; - void emitSurfaceToLight(GrGLSLFPBuilder*, GrGLSLFragmentBuilder*, const char* z) override; - void emitLightColor(GrGLSLFPBuilder*, + void emitSurfaceToLight(GrGLSLUniformHandler*, GrGLSLFragmentBuilder*, const char* z) override; + void emitLightColor(GrGLSLUniformHandler*, GrGLSLFragmentBuilder*, const char *surfaceToLight) override; @@ -1528,7 +1530,9 @@ protected: */ void onSetData(const GrGLSLProgramDataManager&, const GrProcessor&) override; - virtual void emitLightFunc(GrGLSLFPBuilder*, GrGLSLFragmentBuilder*, SkString* funcName) = 0; + virtual void emitLightFunc(GrGLSLUniformHandler*, + GrGLSLFragmentBuilder*, + SkString* funcName) = 0; private: typedef GrGLSLFragmentProcessor INHERITED; @@ -1544,7 +1548,7 @@ private: class GrGLDiffuseLightingEffect : public GrGLLightingEffect { public: GrGLDiffuseLightingEffect(const GrProcessor&); - void emitLightFunc(GrGLSLFPBuilder*, GrGLSLFragmentBuilder*, SkString* funcName) override; + void emitLightFunc(GrGLSLUniformHandler*, GrGLSLFragmentBuilder*, SkString* funcName) override; protected: void onSetData(const GrGLSLProgramDataManager&, const GrProcessor&) override; @@ -1560,7 +1564,7 @@ private: class GrGLSpecularLightingEffect : public GrGLLightingEffect { public: GrGLSpecularLightingEffect(const GrProcessor&); - void emitLightFunc(GrGLSLFPBuilder*, GrGLSLFragmentBuilder*, SkString* funcName) override; + void emitLightFunc(GrGLSLUniformHandler*, GrGLSLFragmentBuilder*, SkString* funcName) override; protected: void onSetData(const GrGLSLProgramDataManager&, const GrProcessor&) override; @@ -1657,16 +1661,17 @@ GrGLLightingEffect::~GrGLLightingEffect() { } void GrGLLightingEffect::emitCode(EmitArgs& args) { - fImageIncrementUni = args.fBuilder->addUniform(GrGLSLProgramBuilder::kFragment_Visibility, - kVec2f_GrSLType, kDefault_GrSLPrecision, - "ImageIncrement"); - fSurfaceScaleUni = args.fBuilder->addUniform(GrGLSLProgramBuilder::kFragment_Visibility, - kFloat_GrSLType, kDefault_GrSLPrecision, - "SurfaceScale"); - fLight->emitLightColorUniform(args.fBuilder); + GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; + fImageIncrementUni = uniformHandler->addUniform(GrGLSLUniformHandler::kFragment_Visibility, + kVec2f_GrSLType, kDefault_GrSLPrecision, + "ImageIncrement"); + fSurfaceScaleUni = uniformHandler->addUniform(GrGLSLUniformHandler::kFragment_Visibility, + kFloat_GrSLType, kDefault_GrSLPrecision, + "SurfaceScale"); + fLight->emitLightColorUniform(uniformHandler); GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder; SkString lightFunc; - this->emitLightFunc(args.fBuilder, fragBuilder, &lightFunc); + this->emitLightFunc(uniformHandler, fragBuilder, &lightFunc); static const GrGLSLShaderVar gSobelArgs[] = { GrGLSLShaderVar("a", kFloat_GrSLType), GrGLSLShaderVar("b", kFloat_GrSLType), @@ -1716,8 +1721,8 @@ void GrGLLightingEffect::emitCode(EmitArgs& args) { fragBuilder->codeAppendf("\t\tvec2 coord = %s;\n", coords2D.c_str()); fragBuilder->codeAppend("\t\tfloat m[9];\n"); - const char* imgInc = args.fBuilder->getUniformCStr(fImageIncrementUni); - const char* surfScale = args.fBuilder->getUniformCStr(fSurfaceScaleUni); + const char* imgInc = uniformHandler->getUniformCStr(fImageIncrementUni); + const char* surfScale = uniformHandler->getUniformCStr(fSurfaceScaleUni); int index = 0; for (int dy = 1; dy >= -1; dy--) { @@ -1732,11 +1737,11 @@ void GrGLLightingEffect::emitCode(EmitArgs& args) { fragBuilder->codeAppend("\t\tvec3 surfaceToLight = "); SkString arg; arg.appendf("%s * m[4]", surfScale); - fLight->emitSurfaceToLight(args.fBuilder, fragBuilder, arg.c_str()); + fLight->emitSurfaceToLight(uniformHandler, fragBuilder, arg.c_str()); fragBuilder->codeAppend(";\n"); fragBuilder->codeAppendf("\t\t%s = %s(%s(m, %s), surfaceToLight, ", args.fOutputColor, lightFunc.c_str(), normalName.c_str(), surfScale); - fLight->emitLightColor(args.fBuilder, fragBuilder, "surfaceToLight"); + fLight->emitLightColor(uniformHandler, fragBuilder, "surfaceToLight"); fragBuilder->codeAppend(");\n"); SkString modulate; GrGLSLMulVarBy4f(&modulate, args.fOutputColor, args.fInputColor); @@ -1769,11 +1774,11 @@ GrGLDiffuseLightingEffect::GrGLDiffuseLightingEffect(const GrProcessor& proc) : INHERITED(proc) { } -void GrGLDiffuseLightingEffect::emitLightFunc(GrGLSLFPBuilder* builder, +void GrGLDiffuseLightingEffect::emitLightFunc(GrGLSLUniformHandler* uniformHandler, GrGLSLFragmentBuilder* fragBuilder, SkString* funcName) { const char* kd; - fKDUni = builder->addUniform(GrGLSLProgramBuilder::kFragment_Visibility, + fKDUni = uniformHandler->addUniform(GrGLSLUniformHandler::kFragment_Visibility, kFloat_GrSLType, kDefault_GrSLPrecision, "KD", &kd); @@ -1853,19 +1858,19 @@ GrGLSpecularLightingEffect::GrGLSpecularLightingEffect(const GrProcessor& proc) : INHERITED(proc) { } -void GrGLSpecularLightingEffect::emitLightFunc(GrGLSLFPBuilder* builder, +void GrGLSpecularLightingEffect::emitLightFunc(GrGLSLUniformHandler* uniformHandler, GrGLSLFragmentBuilder* fragBuilder, SkString* funcName) { const char* ks; const char* shininess; - fKSUni = builder->addUniform(GrGLSLProgramBuilder::kFragment_Visibility, - kFloat_GrSLType, kDefault_GrSLPrecision, "KS", &ks); - fShininessUni = builder->addUniform(GrGLSLProgramBuilder::kFragment_Visibility, - kFloat_GrSLType, - kDefault_GrSLPrecision, - "Shininess", - &shininess); + fKSUni = uniformHandler->addUniform(GrGLSLUniformHandler::kFragment_Visibility, + kFloat_GrSLType, kDefault_GrSLPrecision, "KS", &ks); + fShininessUni = uniformHandler->addUniform(GrGLSLUniformHandler::kFragment_Visibility, + kFloat_GrSLType, + kDefault_GrSLPrecision, + "Shininess", + &shininess); static const GrGLSLShaderVar gLightArgs[] = { GrGLSLShaderVar("normal", kVec3f_GrSLType), @@ -1894,16 +1899,16 @@ void GrGLSpecularLightingEffect::onSetData(const GrGLSLProgramDataManager& pdman } /////////////////////////////////////////////////////////////////////////////// -void GrGLLight::emitLightColorUniform(GrGLSLFPBuilder* builder) { - fColorUni = builder->addUniform(GrGLSLProgramBuilder::kFragment_Visibility, - kVec3f_GrSLType, kDefault_GrSLPrecision, - "LightColor"); +void GrGLLight::emitLightColorUniform(GrGLSLUniformHandler* uniformHandler) { + fColorUni = uniformHandler->addUniform(GrGLSLUniformHandler::kFragment_Visibility, + kVec3f_GrSLType, kDefault_GrSLPrecision, + "LightColor"); } -void GrGLLight::emitLightColor(GrGLSLFPBuilder* builder, +void GrGLLight::emitLightColor(GrGLSLUniformHandler* uniformHandler, GrGLSLFragmentBuilder* fragBuilder, const char *surfaceToLight) { - fragBuilder->codeAppend(builder->getUniformCStr(this->lightColorUni())); + fragBuilder->codeAppend(uniformHandler->getUniformCStr(this->lightColorUni())); } void GrGLLight::setData(const GrGLSLProgramDataManager& pdman, @@ -1922,13 +1927,13 @@ void GrGLDistantLight::setData(const GrGLSLProgramDataManager& pdman, setUniformNormal3(pdman, fDirectionUni, distantLight->direction()); } -void GrGLDistantLight::emitSurfaceToLight(GrGLSLFPBuilder* builder, +void GrGLDistantLight::emitSurfaceToLight(GrGLSLUniformHandler* uniformHandler, GrGLSLFragmentBuilder* fragBuilder, const char* z) { const char* dir; - fDirectionUni = builder->addUniform(GrGLSLProgramBuilder::kFragment_Visibility, - kVec3f_GrSLType, kDefault_GrSLPrecision, - "LightDirection", &dir); + fDirectionUni = uniformHandler->addUniform(GrGLSLUniformHandler::kFragment_Visibility, + kVec3f_GrSLType, kDefault_GrSLPrecision, + "LightDirection", &dir); fragBuilder->codeAppend(dir); } @@ -1942,13 +1947,13 @@ void GrGLPointLight::setData(const GrGLSLProgramDataManager& pdman, setUniformPoint3(pdman, fLocationUni, pointLight->location()); } -void GrGLPointLight::emitSurfaceToLight(GrGLSLFPBuilder* builder, +void GrGLPointLight::emitSurfaceToLight(GrGLSLUniformHandler* uniformHandler, GrGLSLFragmentBuilder* fragBuilder, const char* z) { const char* loc; - fLocationUni = builder->addUniform(GrGLSLProgramBuilder::kFragment_Visibility, - kVec3f_GrSLType, kDefault_GrSLPrecision, - "LightLocation", &loc); + fLocationUni = uniformHandler->addUniform(GrGLSLUniformHandler::kFragment_Visibility, + kVec3f_GrSLType, kDefault_GrSLPrecision, + "LightLocation", &loc); fragBuilder->codeAppendf("normalize(%s - vec3(%s.xy, %s))", loc, fragBuilder->fragmentPosition(), z); } @@ -1968,43 +1973,43 @@ void GrGLSpotLight::setData(const GrGLSLProgramDataManager& pdman, setUniformNormal3(pdman, fSUni, spotLight->s()); } -void GrGLSpotLight::emitSurfaceToLight(GrGLSLFPBuilder* builder, +void GrGLSpotLight::emitSurfaceToLight(GrGLSLUniformHandler* uniformHandler, GrGLSLFragmentBuilder* fragBuilder, const char* z) { const char* location; - fLocationUni = builder->addUniform(GrGLSLProgramBuilder::kFragment_Visibility, - kVec3f_GrSLType, kDefault_GrSLPrecision, - "LightLocation", &location); + fLocationUni = uniformHandler->addUniform(GrGLSLUniformHandler::kFragment_Visibility, + kVec3f_GrSLType, kDefault_GrSLPrecision, + "LightLocation", &location); fragBuilder->codeAppendf("normalize(%s - vec3(%s.xy, %s))", location, fragBuilder->fragmentPosition(), z); } -void GrGLSpotLight::emitLightColor(GrGLSLFPBuilder* builder, +void GrGLSpotLight::emitLightColor(GrGLSLUniformHandler* uniformHandler, GrGLSLFragmentBuilder* fragBuilder, const char *surfaceToLight) { - const char* color = builder->getUniformCStr(this->lightColorUni()); // created by parent class. + const char* color = uniformHandler->getUniformCStr(this->lightColorUni()); // created by parent class. const char* exponent; const char* cosInner; const char* cosOuter; const char* coneScale; const char* s; - fExponentUni = builder->addUniform(GrGLSLProgramBuilder::kFragment_Visibility, - kFloat_GrSLType, kDefault_GrSLPrecision, - "Exponent", &exponent); - fCosInnerConeAngleUni = builder->addUniform(GrGLSLProgramBuilder::kFragment_Visibility, - kFloat_GrSLType, kDefault_GrSLPrecision, - "CosInnerConeAngle", &cosInner); - fCosOuterConeAngleUni = builder->addUniform(GrGLSLProgramBuilder::kFragment_Visibility, - kFloat_GrSLType, kDefault_GrSLPrecision, - "CosOuterConeAngle", &cosOuter); - fConeScaleUni = builder->addUniform(GrGLSLProgramBuilder::kFragment_Visibility, - kFloat_GrSLType, kDefault_GrSLPrecision, - "ConeScale", &coneScale); - fSUni = builder->addUniform(GrGLSLProgramBuilder::kFragment_Visibility, - kVec3f_GrSLType, kDefault_GrSLPrecision, "S", &s); + fExponentUni = uniformHandler->addUniform(GrGLSLUniformHandler::kFragment_Visibility, + kFloat_GrSLType, kDefault_GrSLPrecision, + "Exponent", &exponent); + fCosInnerConeAngleUni = uniformHandler->addUniform(GrGLSLUniformHandler::kFragment_Visibility, + kFloat_GrSLType, kDefault_GrSLPrecision, + "CosInnerConeAngle", &cosInner); + fCosOuterConeAngleUni = uniformHandler->addUniform(GrGLSLUniformHandler::kFragment_Visibility, + kFloat_GrSLType, kDefault_GrSLPrecision, + "CosOuterConeAngle", &cosOuter); + fConeScaleUni = uniformHandler->addUniform(GrGLSLUniformHandler::kFragment_Visibility, + kFloat_GrSLType, kDefault_GrSLPrecision, + "ConeScale", &coneScale); + fSUni = uniformHandler->addUniform(GrGLSLUniformHandler::kFragment_Visibility, + kVec3f_GrSLType, kDefault_GrSLPrecision, "S", &s); static const GrGLSLShaderVar gLightColorArgs[] = { GrGLSLShaderVar("surfaceToLight", kVec3f_GrSLType) diff --git a/gfx/skia/skia/src/effects/SkLumaColorFilter.cpp b/gfx/skia/skia/src/effects/SkLumaColorFilter.cpp index a131b0f434..acdebf8418 100644 --- a/gfx/skia/skia/src/effects/SkLumaColorFilter.cpp +++ b/gfx/skia/skia/src/effects/SkLumaColorFilter.cpp @@ -15,7 +15,6 @@ #include "GrInvariantOutput.h" #include "glsl/GrGLSLFragmentProcessor.h" #include "glsl/GrGLSLFragmentShaderBuilder.h" -#include "glsl/GrGLSLProgramBuilder.h" #endif void SkLumaColorFilter::filterSpan(const SkPMColor src[], int count, diff --git a/gfx/skia/skia/src/effects/SkMagnifierImageFilter.cpp b/gfx/skia/skia/src/effects/SkMagnifierImageFilter.cpp index 911294444a..45f630a085 100644 --- a/gfx/skia/skia/src/effects/SkMagnifierImageFilter.cpp +++ b/gfx/skia/skia/src/effects/SkMagnifierImageFilter.cpp @@ -19,8 +19,8 @@ #include "effects/GrSingleTextureEffect.h" #include "glsl/GrGLSLFragmentProcessor.h" #include "glsl/GrGLSLFragmentShaderBuilder.h" -#include "glsl/GrGLSLProgramBuilder.h" #include "glsl/GrGLSLProgramDataManager.h" +#include "glsl/GrGLSLUniformHandler.h" class GrMagnifierEffect : public GrSingleTextureEffect { @@ -120,31 +120,32 @@ GrGLMagnifierEffect::GrGLMagnifierEffect(const GrProcessor&) { } void GrGLMagnifierEffect::emitCode(EmitArgs& args) { - fOffsetVar = args.fBuilder->addUniform( - GrGLSLProgramBuilder::kFragment_Visibility, - kVec2f_GrSLType, kDefault_GrSLPrecision, "Offset"); - fInvZoomVar = args.fBuilder->addUniform( - GrGLSLProgramBuilder::kFragment_Visibility, - kVec2f_GrSLType, kDefault_GrSLPrecision, "InvZoom"); - fInvInsetVar = args.fBuilder->addUniform( - GrGLSLProgramBuilder::kFragment_Visibility, - kVec2f_GrSLType, kDefault_GrSLPrecision, "InvInset"); - fBoundsVar = args.fBuilder->addUniform( - GrGLSLProgramBuilder::kFragment_Visibility, - kVec4f_GrSLType, kDefault_GrSLPrecision, "Bounds"); + GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; + fOffsetVar = uniformHandler->addUniform(GrGLSLUniformHandler::kFragment_Visibility, + kVec2f_GrSLType, kDefault_GrSLPrecision, + "Offset"); + fInvZoomVar = uniformHandler->addUniform(GrGLSLUniformHandler::kFragment_Visibility, + kVec2f_GrSLType, kDefault_GrSLPrecision, + "InvZoom"); + fInvInsetVar = uniformHandler->addUniform(GrGLSLUniformHandler::kFragment_Visibility, + kVec2f_GrSLType, kDefault_GrSLPrecision, + "InvInset"); + fBoundsVar = uniformHandler->addUniform(GrGLSLUniformHandler::kFragment_Visibility, + kVec4f_GrSLType, kDefault_GrSLPrecision, + "Bounds"); GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder; SkString coords2D = fragBuilder->ensureFSCoords2D(args.fCoords, 0); fragBuilder->codeAppendf("\t\tvec2 coord = %s;\n", coords2D.c_str()); fragBuilder->codeAppendf("\t\tvec2 zoom_coord = %s + %s * %s;\n", - args.fBuilder->getUniformCStr(fOffsetVar), + uniformHandler->getUniformCStr(fOffsetVar), coords2D.c_str(), - args.fBuilder->getUniformCStr(fInvZoomVar)); - const char* bounds = args.fBuilder->getUniformCStr(fBoundsVar); + uniformHandler->getUniformCStr(fInvZoomVar)); + const char* bounds = uniformHandler->getUniformCStr(fBoundsVar); fragBuilder->codeAppendf("\t\tvec2 delta = (coord - %s.xy) * %s.zw;\n", bounds, bounds); fragBuilder->codeAppendf("\t\tdelta = min(delta, vec2(1.0, 1.0) - delta);\n"); fragBuilder->codeAppendf("\t\tdelta = delta * %s;\n", - args.fBuilder->getUniformCStr(fInvInsetVar)); + uniformHandler->getUniformCStr(fInvInsetVar)); fragBuilder->codeAppend("\t\tfloat weight = 0.0;\n"); fragBuilder->codeAppend("\t\tif (delta.s < 2.0 && delta.t < 2.0) {\n"); diff --git a/gfx/skia/skia/src/effects/SkMatrixConvolutionImageFilter.cpp b/gfx/skia/skia/src/effects/SkMatrixConvolutionImageFilter.cpp index e58eec10f4..a80ba45fdb 100644 --- a/gfx/skia/skia/src/effects/SkMatrixConvolutionImageFilter.cpp +++ b/gfx/skia/skia/src/effects/SkMatrixConvolutionImageFilter.cpp @@ -47,7 +47,7 @@ SkMatrixConvolutionImageFilter::SkMatrixConvolutionImageFilter( SkASSERT(kernelOffset.fY >= 0 && kernelOffset.fY < kernelSize.fHeight); } -SkMatrixConvolutionImageFilter* SkMatrixConvolutionImageFilter::Create( +SkImageFilter* SkMatrixConvolutionImageFilter::Create( const SkISize& kernelSize, const SkScalar* kernel, SkScalar gain, @@ -280,7 +280,7 @@ bool SkMatrixConvolutionImageFilter::onFilterImage(Proxy* proxy, } SkIRect bounds; - if (!this->applyCropRect(ctx, proxy, src, &srcOffset, &bounds, &src)) { + if (!this->applyCropRect(this->mapContext(ctx), proxy, src, &srcOffset, &bounds, &src)) { return false; } @@ -322,17 +322,23 @@ bool SkMatrixConvolutionImageFilter::onFilterImage(Proxy* proxy, return true; } -bool SkMatrixConvolutionImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm, - SkIRect* dst) const { - SkIRect bounds = src; - bounds.fRight += fKernelSize.width() - 1; - bounds.fBottom += fKernelSize.height() - 1; - bounds.offset(-fKernelOffset); - if (getInput(0) && !getInput(0)->filterBounds(bounds, ctm, &bounds)) { - return false; +void SkMatrixConvolutionImageFilter::onFilterNodeBounds(const SkIRect& src, const SkMatrix& ctm, + SkIRect* dst, MapDirection direction) const { + *dst = src; + int w = fKernelSize.width() - 1, h = fKernelSize.height() - 1; + dst->fRight += w; + dst->fBottom += h; + if (kReverse_MapDirection == direction) { + dst->offset(-fKernelOffset); + } else { + dst->offset(fKernelOffset - SkIPoint::Make(w, h)); } - *dst = bounds; - return true; +} + +bool SkMatrixConvolutionImageFilter::canComputeFastBounds() const { + // Because the kernel is applied in device-space, we have no idea what + // pixels it will affect in object-space. + return false; } #if SK_SUPPORT_GPU diff --git a/gfx/skia/skia/src/effects/SkMorphologyImageFilter.cpp b/gfx/skia/skia/src/effects/SkMorphologyImageFilter.cpp index 484ed57f37..3f6bc947cf 100644 --- a/gfx/skia/skia/src/effects/SkMorphologyImageFilter.cpp +++ b/gfx/skia/skia/src/effects/SkMorphologyImageFilter.cpp @@ -18,11 +18,12 @@ #include "GrDrawContext.h" #include "GrInvariantOutput.h" #include "GrTexture.h" +#include "SkGr.h" #include "effects/Gr1DKernelEffect.h" #include "glsl/GrGLSLFragmentProcessor.h" #include "glsl/GrGLSLFragmentShaderBuilder.h" -#include "glsl/GrGLSLProgramBuilder.h" #include "glsl/GrGLSLProgramDataManager.h" +#include "glsl/GrGLSLUniformHandler.h" #endif SkMorphologyImageFilter::SkMorphologyImageFilter(int radiusX, @@ -70,7 +71,7 @@ bool SkMorphologyImageFilter::filterImageGeneric(SkMorphologyImageFilter::Proc p } SkIRect bounds; - if (!this->applyCropRect(ctx, proxy, src, &srcOffset, &bounds, &src)) { + if (!this->applyCropRect(this->mapContext(ctx), proxy, src, &srcOffset, &bounds, &src)) { return false; } @@ -149,18 +150,13 @@ void SkMorphologyImageFilter::computeFastBounds(const SkRect& src, SkRect* dst) dst->outset(SkIntToScalar(fRadius.width()), SkIntToScalar(fRadius.height())); } -bool SkMorphologyImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm, - SkIRect* dst) const { - SkIRect bounds = src; +void SkMorphologyImageFilter::onFilterNodeBounds(const SkIRect& src, const SkMatrix& ctm, + SkIRect* dst, MapDirection) const { + *dst = src; SkVector radius = SkVector::Make(SkIntToScalar(this->radius().width()), SkIntToScalar(this->radius().height())); ctm.mapVectors(&radius, 1); - bounds.outset(SkScalarCeilToInt(radius.x()), SkScalarCeilToInt(radius.y())); - if (this->getInput(0) && !this->getInput(0)->filterBounds(bounds, ctm, &bounds)) { - return false; - } - *dst = bounds; - return true; + dst->outset(SkScalarCeilToInt(radius.x()), SkScalarCeilToInt(radius.y())); } SkFlattenable* SkErodeImageFilter::CreateProc(SkReadBuffer& buffer) { @@ -288,14 +284,15 @@ GrGLMorphologyEffect::GrGLMorphologyEffect(const GrProcessor& proc) { } void GrGLMorphologyEffect::emitCode(EmitArgs& args) { - fPixelSizeUni = args.fBuilder->addUniform(GrGLSLProgramBuilder::kFragment_Visibility, - kFloat_GrSLType, kDefault_GrSLPrecision, - "PixelSize"); - const char* pixelSizeInc = args.fBuilder->getUniformCStr(fPixelSizeUni); - fRangeUni = args.fBuilder->addUniform(GrGLSLProgramBuilder::kFragment_Visibility, - kVec2f_GrSLType, kDefault_GrSLPrecision, - "Range"); - const char* range = args.fBuilder->getUniformCStr(fRangeUni); + GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; + fPixelSizeUni = uniformHandler->addUniform(GrGLSLUniformHandler::kFragment_Visibility, + kFloat_GrSLType, kDefault_GrSLPrecision, + "PixelSize"); + const char* pixelSizeInc = uniformHandler->getUniformCStr(fPixelSizeUni); + fRangeUni = uniformHandler->addUniform(GrGLSLUniformHandler::kFragment_Visibility, + kVec2f_GrSLType, kDefault_GrSLPrecision, + "Range"); + const char* range = uniformHandler->getUniformCStr(fRangeUni); GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder; SkString coords2D = fragBuilder->ensureFSCoords2D(args.fCoords, 0); @@ -552,8 +549,7 @@ bool apply_morphology(const SkBitmap& input, const SkIRect& rect, GrMorphologyEffect::MorphologyType morphType, SkISize radius, - SkBitmap* dst, - GrTextureProvider::SizeConstraint constraint) { + SkBitmap* dst) { SkAutoTUnref srcTexture(SkRef(input.getTexture())); SkASSERT(srcTexture); GrContext* context = srcTexture->getContext(); @@ -571,14 +567,7 @@ bool apply_morphology(const SkBitmap& input, SkIRect srcRect = rect; if (radius.fWidth > 0) { - GrTextureProvider::SizeConstraint horiConstraint = constraint; - if (radius.fHeight > 0) { - // Optimization: we will fall through and allocate the "real" texture after this one - // so ours can be approximate (likely faster to allocate) - horiConstraint = GrTextureProvider::kApprox_SizeConstraint; - } - - GrTexture* scratch = context->textureProvider()->createTexture(desc, horiConstraint); + GrTexture* scratch = context->textureProvider()->createApproxTexture(desc); if (nullptr == scratch) { return false; } @@ -602,7 +591,7 @@ bool apply_morphology(const SkBitmap& input, srcRect = dstRect; } if (radius.fHeight > 0) { - GrTexture* scratch = context->textureProvider()->createTexture(desc, constraint); + GrTexture* scratch = context->textureProvider()->createApproxTexture(desc); if (nullptr == scratch) { return false; } @@ -618,7 +607,7 @@ bool apply_morphology(const SkBitmap& input, srcTexture.reset(scratch); } - SkImageFilter::WrapTexture(srcTexture, rect.width(), rect.height(), dst); + GrWrapTextureInBitmap(srcTexture, rect.width(), rect.height(), false, dst); return true; } @@ -636,7 +625,7 @@ bool SkMorphologyImageFilter::filterImageGPUGeneric(bool dilate, return false; } SkIRect bounds; - if (!this->applyCropRect(ctx, proxy, input, &srcOffset, &bounds, &input)) { + if (!this->applyCropRect(this->mapContext(ctx), proxy, input, &srcOffset, &bounds, &input)) { return false; } SkVector radius = SkVector::Make(SkIntToScalar(this->radius().width()), @@ -660,8 +649,7 @@ bool SkMorphologyImageFilter::filterImageGPUGeneric(bool dilate, GrMorphologyEffect::MorphologyType type = dilate ? GrMorphologyEffect::kDilate_MorphologyType : GrMorphologyEffect::kErode_MorphologyType; - if (!apply_morphology(input, srcBounds, type, SkISize::Make(width, height), result, - GrTextureProvider::FromImageFilter(ctx.sizeConstraint()))) { + if (!apply_morphology(input, srcBounds, type, SkISize::Make(width, height), result)) { return false; } offset->fX = bounds.left(); diff --git a/gfx/skia/skia/src/effects/SkOffsetImageFilter.cpp b/gfx/skia/skia/src/effects/SkOffsetImageFilter.cpp index 9da026b279..e39c8d2506 100644 --- a/gfx/skia/skia/src/effects/SkOffsetImageFilter.cpp +++ b/gfx/skia/skia/src/effects/SkOffsetImageFilter.cpp @@ -20,11 +20,7 @@ bool SkOffsetImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& source, SkIPoint* offset) const { SkBitmap src = source; SkIPoint srcOffset = SkIPoint::Make(0, 0); -#ifdef SK_DISABLE_OFFSETIMAGEFILTER_OPTIMIZATION - if (false) { -#else if (!cropRectIsSet()) { -#endif if (!this->filterInput(0, proxy, source, ctx, &src, &srcOffset)) { return false; } @@ -70,24 +66,28 @@ void SkOffsetImageFilter::computeFastBounds(const SkRect& src, SkRect* dst) cons } else { *dst = src; } +#ifdef SK_SUPPORT_SRC_BOUNDS_BLOAT_FOR_IMAGEFILTERS SkRect copy = *dst; +#endif dst->offset(fOffset.fX, fOffset.fY); +#ifdef SK_SUPPORT_SRC_BOUNDS_BLOAT_FOR_IMAGEFILTERS dst->join(copy); +#endif } -bool SkOffsetImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm, - SkIRect* dst) const { +void SkOffsetImageFilter::onFilterNodeBounds(const SkIRect& src, const SkMatrix& ctm, + SkIRect* dst, MapDirection direction) const { SkVector vec; ctm.mapVectors(&vec, &fOffset, 1); - - SkIRect bounds = src; - bounds.offset(-SkScalarCeilToInt(vec.fX), -SkScalarCeilToInt(vec.fY)); - bounds.join(src); - if (getInput(0)) { - return getInput(0)->filterBounds(bounds, ctm, dst); + if (kReverse_MapDirection == direction) { + vec.negate(); } - *dst = bounds; - return true; + + *dst = src; + dst->offset(SkScalarCeilToInt(vec.fX), SkScalarCeilToInt(vec.fY)); +#ifdef SK_SUPPORT_SRC_BOUNDS_BLOAT_FOR_IMAGEFILTERS + dst->join(src); +#endif } SkFlattenable* SkOffsetImageFilter::CreateProc(SkReadBuffer& buffer) { diff --git a/gfx/skia/skia/src/effects/SkRectShaderImageFilter.cpp b/gfx/skia/skia/src/effects/SkPaintImageFilter.cpp similarity index 50% rename from gfx/skia/skia/src/effects/SkRectShaderImageFilter.cpp rename to gfx/skia/skia/src/effects/SkPaintImageFilter.cpp index 14837d02b1..d141f34dfb 100644 --- a/gfx/skia/skia/src/effects/SkRectShaderImageFilter.cpp +++ b/gfx/skia/skia/src/effects/SkPaintImageFilter.cpp @@ -1,54 +1,39 @@ /* - * Copyright 2013 Google Inc. + * Copyright 2016 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ -#include "SkRectShaderImageFilter.h" +#include "SkPaintImageFilter.h" #include "SkBitmap.h" #include "SkCanvas.h" #include "SkDevice.h" #include "SkReadBuffer.h" #include "SkWriteBuffer.h" -#include "SkShader.h" -SkImageFilter* SkRectShaderImageFilter::Create(SkShader* s, const SkRect& rect) { - SkASSERT(s); - uint32_t flags = CropRect::kHasAll_CropEdge; - if (rect.width() == 0 || rect.height() == 0) { - flags = 0x0; - } - CropRect cropRect(rect, flags); - return s ? new SkRectShaderImageFilter(s, &cropRect) : nullptr; +SkImageFilter* SkPaintImageFilter::Create(const SkPaint& paint, const CropRect* cropRect) { + return new SkPaintImageFilter(paint, cropRect); } -SkImageFilter* SkRectShaderImageFilter::Create(SkShader* s, const CropRect* cropRect) { - SkASSERT(s); - return s ? new SkRectShaderImageFilter(s, cropRect) : nullptr; -} - -SkRectShaderImageFilter::SkRectShaderImageFilter(SkShader* s, const CropRect* cropRect) +SkPaintImageFilter::SkPaintImageFilter(const SkPaint& paint, const CropRect* cropRect) : INHERITED(0, nullptr, cropRect) - , fShader(SkRef(s)) { + , fPaint(paint) { } -SkFlattenable* SkRectShaderImageFilter::CreateProc(SkReadBuffer& buffer) { +SkFlattenable* SkPaintImageFilter::CreateProc(SkReadBuffer& buffer) { SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 0); - SkAutoTUnref shader(buffer.readShader()); - return Create(shader.get(), &common.cropRect()); + SkPaint paint; + buffer.readPaint(&paint); + return Create(paint, &common.cropRect()); } -void SkRectShaderImageFilter::flatten(SkWriteBuffer& buffer) const { +void SkPaintImageFilter::flatten(SkWriteBuffer& buffer) const { this->INHERITED::flatten(buffer); - buffer.writeFlattenable(fShader); + buffer.writePaint(fPaint); } -SkRectShaderImageFilter::~SkRectShaderImageFilter() { - fShader->unref(); -} - -bool SkRectShaderImageFilter::onFilterImage(Proxy* proxy, +bool SkPaintImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& source, const Context& ctx, SkBitmap* result, @@ -65,13 +50,15 @@ bool SkRectShaderImageFilter::onFilterImage(Proxy* proxy, } SkCanvas canvas(device.get()); - SkPaint paint; SkMatrix matrix(ctx.ctm()); matrix.postTranslate(SkIntToScalar(-bounds.left()), SkIntToScalar(-bounds.top())); - SkSafeUnref(paint.setShader(SkShader::CreateLocalMatrixShader(fShader, matrix))); - SkRect rect = SkRect::MakeWH(SkIntToScalar(bounds.width()), SkIntToScalar(bounds.height())); - canvas.drawRect(rect, paint); + SkMatrix inverse; + if (matrix.invert(&inverse)) { + inverse.mapRect(&rect); + } + canvas.setMatrix(matrix); + canvas.drawRect(rect, fPaint); *result = device.get()->accessBitmap(false); offset->fX = bounds.fLeft; @@ -79,13 +66,16 @@ bool SkRectShaderImageFilter::onFilterImage(Proxy* proxy, return true; } -bool SkRectShaderImageFilter::affectsTransparentBlack() const { - return true; +bool SkPaintImageFilter::canComputeFastBounds() const { + // http:skbug.com/4627: "make computeFastBounds and onFilterBounds() CropRect-aware" + // computeFastBounds() doesn't currently take the crop rect into account, + // so we can't compute it. If a full crop rect is set, we should return true here. + return false; } #ifndef SK_IGNORE_TO_STRING -void SkRectShaderImageFilter::toString(SkString* str) const { - str->appendf("SkRectShaderImageFilter: ("); +void SkPaintImageFilter::toString(SkString* str) const { + str->appendf("SkPaintImageFilter: ("); str->append(")"); } #endif diff --git a/gfx/skia/skia/src/effects/SkPerlinNoiseShader.cpp b/gfx/skia/skia/src/effects/SkPerlinNoiseShader.cpp index 916cf78f0d..60e96017b2 100644 --- a/gfx/skia/skia/src/effects/SkPerlinNoiseShader.cpp +++ b/gfx/skia/skia/src/effects/SkPerlinNoiseShader.cpp @@ -5,7 +5,6 @@ * found in the LICENSE file. */ -#include "SkDither.h" #include "SkPerlinNoiseShader.h" #include "SkColorFilter.h" #include "SkReadBuffer.h" @@ -22,8 +21,8 @@ #include "effects/GrConstColorProcessor.h" #include "glsl/GrGLSLFragmentProcessor.h" #include "glsl/GrGLSLFragmentShaderBuilder.h" -#include "glsl/GrGLSLProgramBuilder.h" #include "glsl/GrGLSLProgramDataManager.h" +#include "glsl/GrGLSLUniformHandler.h" #endif static const int kBlockSize = 256; @@ -467,19 +466,6 @@ void SkPerlinNoiseShader::PerlinNoiseShaderContext::shadeSpan( } } -void SkPerlinNoiseShader::PerlinNoiseShaderContext::shadeSpan16( - int x, int y, uint16_t result[], int count) { - SkPoint point = SkPoint::Make(SkIntToScalar(x), SkIntToScalar(y)); - StitchData stitchData; - DITHER_565_SCAN(y); - for (int i = 0; i < count; ++i) { - unsigned dither = DITHER_VALUE(x); - result[i] = SkDitherRGB32To565(shade(point, stitchData), dither); - DITHER_INC_X(x); - point.fX += SK_Scalar1; - } -} - ///////////////////////////////////////////////////////////////////// #if SK_SUPPORT_GPU @@ -621,19 +607,20 @@ GrGLPerlinNoise::GrGLPerlinNoise(const GrProcessor& processor) void GrGLPerlinNoise::emitCode(EmitArgs& args) { GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder; + GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; SkString vCoords = fragBuilder->ensureFSCoords2D(args.fCoords, 0); - fBaseFrequencyUni = args.fBuilder->addUniform(GrGLSLProgramBuilder::kFragment_Visibility, - kVec2f_GrSLType, kDefault_GrSLPrecision, - "baseFrequency"); - const char* baseFrequencyUni = args.fBuilder->getUniformCStr(fBaseFrequencyUni); + fBaseFrequencyUni = uniformHandler->addUniform(GrGLSLUniformHandler::kFragment_Visibility, + kVec2f_GrSLType, kDefault_GrSLPrecision, + "baseFrequency"); + const char* baseFrequencyUni = uniformHandler->getUniformCStr(fBaseFrequencyUni); const char* stitchDataUni = nullptr; if (fStitchTiles) { - fStitchDataUni = args.fBuilder->addUniform(GrGLSLProgramBuilder::kFragment_Visibility, - kVec2f_GrSLType, kDefault_GrSLPrecision, - "stitchData"); - stitchDataUni = args.fBuilder->getUniformCStr(fStitchDataUni); + fStitchDataUni = uniformHandler->addUniform(GrGLSLUniformHandler::kFragment_Visibility, + kVec2f_GrSLType, kDefault_GrSLPrecision, + "stitchData"); + stitchDataUni = uniformHandler->getUniformCStr(fStitchDataUni); } // There are 4 lines, so the center of each line is 1/8, 3/8, 5/8 and 7/8 diff --git a/gfx/skia/skia/src/effects/SkTableColorFilter.cpp b/gfx/skia/skia/src/effects/SkTableColorFilter.cpp index 9d97411042..d9c07e254c 100644 --- a/gfx/skia/skia/src/effects/SkTableColorFilter.cpp +++ b/gfx/skia/skia/src/effects/SkTableColorFilter.cpp @@ -337,8 +337,8 @@ SkColorFilter* SkTable_ColorFilter::newComposed(const SkColorFilter* innerFilter #include "effects/GrTextureStripAtlas.h" #include "glsl/GrGLSLFragmentProcessor.h" #include "glsl/GrGLSLFragmentShaderBuilder.h" -#include "glsl/GrGLSLProgramBuilder.h" #include "glsl/GrGLSLProgramDataManager.h" +#include "glsl/GrGLSLUniformHandler.h" class ColorTableEffect : public GrFragmentProcessor { public: @@ -415,9 +415,9 @@ void GLColorTableEffect::onSetData(const GrGLSLProgramDataManager& pdm, const Gr void GLColorTableEffect::emitCode(EmitArgs& args) { const char* yoffsets; - fRGBAYValuesUni = args.fBuilder->addUniform(GrGLSLFPBuilder::kFragment_Visibility, - kVec4f_GrSLType, kDefault_GrSLPrecision, - "yoffsets", &yoffsets); + fRGBAYValuesUni = args.fUniformHandler->addUniform(GrGLSLUniformHandler::kFragment_Visibility, + kVec4f_GrSLType, kDefault_GrSLPrecision, + "yoffsets", &yoffsets); static const float kColorScaleFactor = 255.0f / 256.0f; static const float kColorOffsetFactor = 1.0f / 512.0f; GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder; @@ -442,22 +442,22 @@ void GLColorTableEffect::emitCode(EmitArgs& args) { fragBuilder->codeAppendf("\t\t%s.a = ", args.fOutputColor); coord.printf("vec2(coord.a, %s.a)", yoffsets); fragBuilder->appendTextureLookup(args.fSamplers[0], coord.c_str()); - fragBuilder->codeAppend(";\n"); + fragBuilder->codeAppend(".a;\n"); fragBuilder->codeAppendf("\t\t%s.r = ", args.fOutputColor); coord.printf("vec2(coord.r, %s.r)", yoffsets); fragBuilder->appendTextureLookup(args.fSamplers[0], coord.c_str()); - fragBuilder->codeAppend(";\n"); + fragBuilder->codeAppend(".a;\n"); fragBuilder->codeAppendf("\t\t%s.g = ", args.fOutputColor); coord.printf("vec2(coord.g, %s.g)", yoffsets); fragBuilder->appendTextureLookup(args.fSamplers[0], coord.c_str()); - fragBuilder->codeAppend(";\n"); + fragBuilder->codeAppend(".a;\n"); fragBuilder->codeAppendf("\t\t%s.b = ", args.fOutputColor); coord.printf("vec2(coord.b, %s.b)", yoffsets); fragBuilder->appendTextureLookup(args.fSamplers[0], coord.c_str()); - fragBuilder->codeAppend(";\n"); + fragBuilder->codeAppend(".a;\n"); fragBuilder->codeAppendf("\t\t%s.rgb *= %s.a;\n", args.fOutputColor, args.fOutputColor); } @@ -487,7 +487,7 @@ const GrFragmentProcessor* ColorTableEffect::Create(GrContext* context, SkBitmap ColorTableEffect::ColorTableEffect(GrTexture* texture, GrTextureStripAtlas* atlas, int row, unsigned flags) - : fTextureAccess(texture, "a") + : fTextureAccess(texture) , fFlags(flags) , fAtlas(atlas) , fRow(row) { diff --git a/gfx/skia/skia/src/effects/SkTileImageFilter.cpp b/gfx/skia/skia/src/effects/SkTileImageFilter.cpp index 52ea6a756f..6da572b3b8 100644 --- a/gfx/skia/skia/src/effects/SkTileImageFilter.cpp +++ b/gfx/skia/skia/src/effects/SkTileImageFilter.cpp @@ -9,6 +9,7 @@ #include "SkBitmap.h" #include "SkCanvas.h" #include "SkDevice.h" +#include "SkOffsetImageFilter.h" #include "SkReadBuffer.h" #include "SkWriteBuffer.h" #include "SkMatrix.h" @@ -21,6 +22,16 @@ SkImageFilter* SkTileImageFilter::Create(const SkRect& srcRect, const SkRect& ds if (!SkIsValidRect(srcRect) || !SkIsValidRect(dstRect)) { return nullptr; } + if (srcRect.width() == dstRect.width() && srcRect.height() == dstRect.height()) { + SkRect ir = dstRect; + if (!ir.intersect(srcRect)) { + return SkSafeRef(input); + } + CropRect cropRect(ir); + return SkOffsetImageFilter::Create(dstRect.x() - srcRect.x(), + dstRect.y() - srcRect.y(), + input, &cropRect); + } return new SkTileImageFilter(srcRect, dstRect, input); } @@ -35,6 +46,10 @@ bool SkTileImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& src, SkRect dstRect; ctx.ctm().mapRect(&dstRect, fDstRect); + if (!dstRect.intersect(SkRect::Make(ctx.clipBounds()))) { + offset->fX = offset->fY = 0; + return true; + } const SkIRect dstIRect = dstRect.roundOut(); int w = dstIRect.width(); int h = dstIRect.height(); @@ -48,15 +63,31 @@ bool SkTileImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& src, srcRect.roundOut(&srcIRect); srcIRect.offset(-srcOffset); SkBitmap subset; - SkIRect bounds; - source.getBounds(&bounds); + SkIRect srcBounds; + source.getBounds(&srcBounds); - if (!srcIRect.intersect(bounds)) { + if (!SkIRect::Intersects(srcIRect, srcBounds)) { offset->fX = offset->fY = 0; return true; - } else if (!source.extractSubset(&subset, srcIRect)) { - return false; } + if (srcBounds.contains(srcIRect)) { + if (!source.extractSubset(&subset, srcIRect)) { + return false; + } + } else { + SkAutoTUnref device(proxy->createDevice(srcIRect.width(), + srcIRect.height(), + kPossible_TileUsage)); + if (!device) { + return false; + } + SkCanvas canvas(device); + canvas.drawBitmap(src, SkIntToScalar(srcOffset.x()), + SkIntToScalar(srcOffset.y())); + subset = device->accessBitmap(false); + } + SkASSERT(subset.width() == srcIRect.width()); + SkASSERT(subset.height() == srcIRect.height()); SkAutoTUnref device(proxy->createDevice(w, h)); if (nullptr == device.get()) { @@ -66,12 +97,8 @@ bool SkTileImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& src, SkPaint paint; paint.setXfermodeMode(SkXfermode::kSrc_Mode); - SkMatrix shaderMatrix; - shaderMatrix.setTranslate(SkIntToScalar(srcOffset.fX), - SkIntToScalar(srcOffset.fY)); SkAutoTUnref shader(SkShader::CreateBitmapShader(subset, - SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode, - &shaderMatrix)); + SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode)); paint.setShader(shader); canvas.translate(-dstRect.fLeft, -dstRect.fTop); canvas.drawRect(dstRect, paint); @@ -81,21 +108,30 @@ bool SkTileImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& src, return true; } +void SkTileImageFilter::onFilterNodeBounds(const SkIRect& src, const SkMatrix& ctm, + SkIRect* dst, MapDirection direction) const { + SkRect rect = kReverse_MapDirection == direction ? fSrcRect : fDstRect; + ctm.mapRect(&rect); + rect.roundOut(dst); +#ifdef SK_SUPPORT_SRC_BOUNDS_BLOAT_FOR_IMAGEFILTERS + dst->join(src); +#endif +} + bool SkTileImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm, SkIRect* dst) const { - SkRect srcRect; - ctm.mapRect(&srcRect, fSrcRect); - SkIRect srcIRect; - srcRect.roundOut(&srcIRect); - srcIRect.join(src); - *dst = srcIRect; + this->onFilterNodeBounds(src, ctm, dst, kReverse_MapDirection); return true; } void SkTileImageFilter::computeFastBounds(const SkRect& src, SkRect* dst) const { +#ifdef SK_SUPPORT_SRC_BOUNDS_BLOAT_FOR_IMAGEFILTERS // This is a workaround for skia:3194. *dst = src; dst->join(fDstRect); +#else + *dst = fDstRect; +#endif } SkFlattenable* SkTileImageFilter::CreateProc(SkReadBuffer& buffer) { diff --git a/gfx/skia/skia/src/effects/SkXfermodeImageFilter.cpp b/gfx/skia/skia/src/effects/SkXfermodeImageFilter.cpp index 38d1e5e6b4..5f88240481 100644 --- a/gfx/skia/skia/src/effects/SkXfermodeImageFilter.cpp +++ b/gfx/skia/skia/src/effects/SkXfermodeImageFilter.cpp @@ -147,47 +147,55 @@ bool SkXfermodeImageFilter::filterImageGPU(Proxy* proxy, } GrTexture* foregroundTex = foreground.getTexture(); GrContext* context = foregroundTex->getContext(); + SkIRect bounds = background.bounds().makeOffset(backgroundOffset.x(), backgroundOffset.y()); + bounds.join(foreground.bounds().makeOffset(foregroundOffset.x(), foregroundOffset.y())); + if (bounds.isEmpty()) { + return false; + } const GrFragmentProcessor* xferFP = nullptr; GrSurfaceDesc desc; desc.fFlags = kRenderTarget_GrSurfaceFlag; - desc.fWidth = src.width(); - desc.fHeight = src.height(); + desc.fWidth = bounds.width(); + desc.fHeight = bounds.height(); desc.fConfig = kSkia8888_GrPixelConfig; - auto constraint = GrTextureProvider::FromImageFilter(ctx.sizeConstraint()); - SkAutoTUnref dst(context->textureProvider()->createTexture(desc, constraint)); + SkAutoTUnref dst(context->textureProvider()->createApproxTexture(desc)); if (!dst) { return false; } GrPaint paint; - SkMatrix bgMatrix; - bgMatrix.setIDiv(backgroundTex->width(), backgroundTex->height()); - SkAutoTUnref bgFP( - GrSimpleTextureEffect::Create(backgroundTex, bgMatrix)); + SkMatrix backgroundMatrix; + backgroundMatrix.setIDiv(backgroundTex->width(), backgroundTex->height()); + backgroundMatrix.preTranslate(SkIntToScalar(-backgroundOffset.fX), + SkIntToScalar(-backgroundOffset.fY)); + SkAutoTUnref bgFP(GrTextureDomainEffect::Create( + backgroundTex, backgroundMatrix, + GrTextureDomain::MakeTexelDomain(backgroundTex, background.bounds()), + GrTextureDomain::kDecal_Mode, + GrTextureParams::kNone_FilterMode) + ); if (!fMode || !fMode->asFragmentProcessor(&xferFP, bgFP)) { // canFilterImageGPU() should've taken care of this SkASSERT(false); return false; } - SkMatrix foregroundMatrix = GrCoordTransform::MakeDivByTextureWHMatrix(foregroundTex); - foregroundMatrix.preTranslate(SkIntToScalar(backgroundOffset.fX-foregroundOffset.fX), - SkIntToScalar(backgroundOffset.fY-foregroundOffset.fY)); + SkMatrix foregroundMatrix; + foregroundMatrix.setIDiv(foregroundTex->width(), foregroundTex->height()); + foregroundMatrix.preTranslate(SkIntToScalar(-foregroundOffset.fX), + SkIntToScalar(-foregroundOffset.fY)); - SkRect srcRect; - src.getBounds(&srcRect); - - SkAutoTUnref foregroundDomain(GrTextureDomainEffect::Create( + SkAutoTUnref foregroundFP(GrTextureDomainEffect::Create( foregroundTex, foregroundMatrix, GrTextureDomain::MakeTexelDomain(foregroundTex, foreground.bounds()), GrTextureDomain::kDecal_Mode, GrTextureParams::kNone_FilterMode) ); - paint.addColorFragmentProcessor(foregroundDomain.get()); + paint.addColorFragmentProcessor(foregroundFP.get()); if (xferFP) { paint.addColorFragmentProcessor(xferFP)->unref(); } @@ -198,11 +206,13 @@ bool SkXfermodeImageFilter::filterImageGPU(Proxy* proxy, return false; } - drawContext->drawRect(GrClip::WideOpen(), paint, SkMatrix::I(), srcRect); + SkMatrix matrix; + matrix.setTranslate(SkIntToScalar(-bounds.left()), SkIntToScalar(-bounds.top())); + drawContext->drawRect(GrClip::WideOpen(), paint, matrix, SkRect::Make(bounds)); - offset->fX = backgroundOffset.fX; - offset->fY = backgroundOffset.fY; - WrapTexture(dst, src.width(), src.height(), result); + offset->fX = bounds.left(); + offset->fY = bounds.top(); + GrWrapTextureInBitmap(dst, bounds.width(), bounds.height(), false, result); return true; } diff --git a/gfx/skia/skia/src/effects/gradients/SkClampRange.h b/gfx/skia/skia/src/effects/gradients/SkClampRange.h index 945f9a7ff1..d3d2d08c86 100644 --- a/gfx/skia/skia/src/effects/gradients/SkClampRange.h +++ b/gfx/skia/skia/src/effects/gradients/SkClampRange.h @@ -12,8 +12,8 @@ #include "SkScalar.h" #define SkGradFixed SkFixed3232 -#define SkScalarToGradFixed SkScalarToFixed3232 -#define SkFixedToGradFixed SkFixedToFixed3232 +#define SkScalarToGradFixed(x) SkScalarToFixed3232(x) +#define SkFixedToGradFixed(x) SkFixedToFixed3232(x) #define SkGradFixedToFixed(x) (SkFixed)((x) >> 16) #define kFracMax_SkGradFixed 0xFFFFFFFFLL diff --git a/gfx/skia/skia/src/effects/gradients/SkGradientShader.cpp b/gfx/skia/skia/src/effects/gradients/SkGradientShader.cpp index e486e08a64..ac70bc25e1 100644 --- a/gfx/skia/skia/src/effects/gradients/SkGradientShader.cpp +++ b/gfx/skia/skia/src/effects/gradients/SkGradientShader.cpp @@ -314,11 +314,6 @@ SkGradientShaderBase::GradientShaderBaseContext::GradientShaderBaseContext( if (shader.fColorsAreOpaque && paintAlpha == 0xFF) { fFlags |= kOpaqueAlpha_Flag; } - // we can do span16 as long as our individual colors are opaque, - // regardless of the paint's alpha - if (shader.fColorsAreOpaque) { - fFlags |= kHasSpan16_Flag; - } } SkGradientShaderBase::GradientShaderCache::GradientShaderCache( @@ -910,8 +905,8 @@ SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END #include "GrInvariantOutput.h" #include "gl/GrGLContext.h" #include "glsl/GrGLSLFragmentShaderBuilder.h" -#include "glsl/GrGLSLProgramBuilder.h" #include "glsl/GrGLSLProgramDataManager.h" +#include "glsl/GrGLSLUniformHandler.h" #include "SkGr.h" GrGLGradientEffect::GrGLGradientEffect() @@ -920,31 +915,32 @@ GrGLGradientEffect::GrGLGradientEffect() GrGLGradientEffect::~GrGLGradientEffect() { } -void GrGLGradientEffect::emitUniforms(GrGLSLFPBuilder* builder, const GrGradientEffect& ge) { +void GrGLGradientEffect::emitUniforms(GrGLSLUniformHandler* uniformHandler, + const GrGradientEffect& ge) { if (SkGradientShaderBase::kTwo_GpuColorType == ge.getColorType()) { // 2 Color case - fColorStartUni = builder->addUniform(GrGLSLProgramBuilder::kFragment_Visibility, - kVec4f_GrSLType, kDefault_GrSLPrecision, - "GradientStartColor"); - fColorEndUni = builder->addUniform(GrGLSLProgramBuilder::kFragment_Visibility, - kVec4f_GrSLType, kDefault_GrSLPrecision, - "GradientEndColor"); + fColorStartUni = uniformHandler->addUniform(GrGLSLUniformHandler::kFragment_Visibility, + kVec4f_GrSLType, kDefault_GrSLPrecision, + "GradientStartColor"); + fColorEndUni = uniformHandler->addUniform(GrGLSLUniformHandler::kFragment_Visibility, + kVec4f_GrSLType, kDefault_GrSLPrecision, + "GradientEndColor"); } else if (SkGradientShaderBase::kThree_GpuColorType == ge.getColorType()) { // 3 Color Case - fColorStartUni = builder->addUniform(GrGLSLProgramBuilder::kFragment_Visibility, - kVec4f_GrSLType, kDefault_GrSLPrecision, - "GradientStartColor"); - fColorMidUni = builder->addUniform(GrGLSLProgramBuilder::kFragment_Visibility, - kVec4f_GrSLType, kDefault_GrSLPrecision, - "GradientMidColor"); - fColorEndUni = builder->addUniform(GrGLSLProgramBuilder::kFragment_Visibility, - kVec4f_GrSLType, kDefault_GrSLPrecision, - "GradientEndColor"); + fColorStartUni = uniformHandler->addUniform(GrGLSLUniformHandler::kFragment_Visibility, + kVec4f_GrSLType, kDefault_GrSLPrecision, + "GradientStartColor"); + fColorMidUni = uniformHandler->addUniform(GrGLSLUniformHandler::kFragment_Visibility, + kVec4f_GrSLType, kDefault_GrSLPrecision, + "GradientMidColor"); + fColorEndUni = uniformHandler->addUniform(GrGLSLUniformHandler::kFragment_Visibility, + kVec4f_GrSLType, kDefault_GrSLPrecision, + "GradientEndColor"); } else { // if not a fast case - fFSYUni = builder->addUniform(GrGLSLProgramBuilder::kFragment_Visibility, - kFloat_GrSLType, kDefault_GrSLPrecision, - "GradientYCoordFS"); + fFSYUni = uniformHandler->addUniform(GrGLSLUniformHandler::kFragment_Visibility, + kFloat_GrSLType, kDefault_GrSLPrecision, + "GradientYCoordFS"); } } @@ -1026,8 +1022,8 @@ uint32_t GrGLGradientEffect::GenBaseGradientKey(const GrProcessor& processor) { return key; } -void GrGLGradientEffect::emitColor(GrGLSLFPBuilder* builder, - GrGLSLFragmentBuilder* fragBuilder, +void GrGLGradientEffect::emitColor(GrGLSLFragmentBuilder* fragBuilder, + GrGLSLUniformHandler* uniformHandler, const GrGLSLCaps* glslCaps, const GrGradientEffect& ge, const char* gradientTValue, @@ -1036,8 +1032,8 @@ void GrGLGradientEffect::emitColor(GrGLSLFPBuilder* builder, const TextureSamplerArray& samplers) { if (SkGradientShaderBase::kTwo_GpuColorType == ge.getColorType()){ fragBuilder->codeAppendf("\tvec4 colorTemp = mix(%s, %s, clamp(%s, 0.0, 1.0));\n", - builder->getUniformVariable(fColorStartUni).c_str(), - builder->getUniformVariable(fColorEndUni).c_str(), + uniformHandler->getUniformVariable(fColorStartUni).c_str(), + uniformHandler->getUniformVariable(fColorEndUni).c_str(), gradientTValue); // Note that we could skip this step if both colors are known to be opaque. Two // considerations: @@ -1055,20 +1051,20 @@ void GrGLGradientEffect::emitColor(GrGLSLFPBuilder* builder, fragBuilder->codeAppendf("\tfloat oneMinus2t = 1.0 - (2.0 * (%s));\n", gradientTValue); fragBuilder->codeAppendf("\tvec4 colorTemp = clamp(oneMinus2t, 0.0, 1.0) * %s;\n", - builder->getUniformVariable(fColorStartUni).c_str()); + uniformHandler->getUniformVariable(fColorStartUni).c_str()); if (!glslCaps->canUseMinAndAbsTogether()) { // The Tegra3 compiler will sometimes never return if we have // min(abs(oneMinus2t), 1.0), or do the abs first in a separate expression. fragBuilder->codeAppend("\tfloat minAbs = abs(oneMinus2t);\n"); fragBuilder->codeAppend("\tminAbs = minAbs > 1.0 ? 1.0 : minAbs;\n"); fragBuilder->codeAppendf("\tcolorTemp += (1.0 - minAbs) * %s;\n", - builder->getUniformVariable(fColorMidUni).c_str()); + uniformHandler->getUniformVariable(fColorMidUni).c_str()); } else { fragBuilder->codeAppendf("\tcolorTemp += (1.0 - min(abs(oneMinus2t), 1.0)) * %s;\n", - builder->getUniformVariable(fColorMidUni).c_str()); + uniformHandler->getUniformVariable(fColorMidUni).c_str()); } fragBuilder->codeAppendf("\tcolorTemp += clamp(-oneMinus2t, 0.0, 1.0) * %s;\n", - builder->getUniformVariable(fColorEndUni).c_str()); + uniformHandler->getUniformVariable(fColorEndUni).c_str()); if (GrGradientEffect::kAfterInterp_PremulType == ge.getPremulType()) { fragBuilder->codeAppend("\tcolorTemp.rgb *= colorTemp.a;\n"); } @@ -1078,7 +1074,7 @@ void GrGLGradientEffect::emitColor(GrGLSLFPBuilder* builder, } else { fragBuilder->codeAppendf("\tvec2 coord = vec2(%s, %s);\n", gradientTValue, - builder->getUniformVariable(fFSYUni).c_str()); + uniformHandler->getUniformVariable(fFSYUni).c_str()); fragBuilder->codeAppendf("\t%s = ", outputColor); fragBuilder->appendTextureLookupAndModulate(inputColor, samplers[0], diff --git a/gfx/skia/skia/src/effects/gradients/SkGradientShaderPriv.h b/gfx/skia/skia/src/effects/gradients/SkGradientShaderPriv.h index 3c19152d90..f8a968d1a2 100644 --- a/gfx/skia/skia/src/effects/gradients/SkGradientShaderPriv.h +++ b/gfx/skia/skia/src/effects/gradients/SkGradientShaderPriv.h @@ -58,7 +58,7 @@ static inline SkFixed repeat_tileproc(SkFixed x) { #endif static inline SkFixed mirror_tileproc(SkFixed x) { - int s = x << 15 >> 31; + int s = SkLeftShift(x, 15) >> 31; return (x ^ s) & 0xFFFF; } @@ -419,14 +419,14 @@ protected: // Emits the uniform used as the y-coord to texture samples in derived classes. Subclasses // should call this method from their emitCode(). - void emitUniforms(GrGLSLFPBuilder* builder, const GrGradientEffect&); + void emitUniforms(GrGLSLUniformHandler*, const GrGradientEffect&); // emit code that gets a fragment's color from an expression for t; Has branches for 3 separate // control flows inside -- 2 color gradients, 3 color symmetric gradients (both using // native GLSL mix), and 4+ color gradients that use the traditional texture lookup. - void emitColor(GrGLSLFPBuilder* builder, - GrGLSLFragmentBuilder* fragBuilder, + void emitColor(GrGLSLFragmentBuilder* fragBuilder, + GrGLSLUniformHandler* uniformHandler, const GrGLSLCaps* caps, const GrGradientEffect&, const char* gradientTValue, diff --git a/gfx/skia/skia/src/effects/gradients/SkLinearGradient.cpp b/gfx/skia/skia/src/effects/gradients/SkLinearGradient.cpp index 272d15ecde..6ba2fa8ea2 100644 --- a/gfx/skia/skia/src/effects/gradients/SkLinearGradient.cpp +++ b/gfx/skia/skia/src/effects/gradients/SkLinearGradient.cpp @@ -9,10 +9,6 @@ static const float kInv255Float = 1.0f / 255; -static inline int repeat_bits(int x, const int bits) { - return x & ((1 << bits) - 1); -} - static inline int repeat_8bits(int x) { return x & 0xFF; } @@ -23,13 +19,6 @@ static inline int repeat_8bits(int x) { #pragma optimize("", off) #endif -static inline int mirror_bits(int x, const int bits) { - if (x & (1 << bits)) { - x = ~x; - } - return x & ((1 << bits) - 1); -} - static inline int mirror_8bits(int x) { if (x & 256) { x = ~x; @@ -102,17 +91,6 @@ SkLinearGradient::LinearGradientContext::LinearGradientContext( const SkLinearGradient& shader, const ContextRec& ctx) : INHERITED(shader, ctx) { - unsigned mask = SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask; - if ((fDstToIndex.getType() & ~mask) == 0) { - // when we dither, we are (usually) not const-in-Y - if ((fFlags & SkShader::kHasSpan16_Flag) && !ctx.fPaint->isDither()) { - // only claim this if we do have a 16bit mode (i.e. none of our - // colors have alpha), and if we are not dithering (which obviously - // is not const in Y). - fFlags |= SkShader::kConstInY16_Flag; - } - } - // setup for Sk4f int count = shader.fColorCount; fRecs.setCount(count); @@ -150,7 +128,7 @@ SkLinearGradient::LinearGradientContext::LinearGradientContext( const Sk4f scale(1, 1, 1, paintAlpha); for (int i = 0; i < count; ++i) { uint32_t c = SkSwizzle_Color_to_PMColor(shader.fOrigColors[i]); - rec[i].fColor = Sk4f::FromBytes((const uint8_t*)&c) * scale; + rec[i].fColor = SkNx_cast(Sk4b::Load((const uint8_t*)&c)) * scale; if (i > 0) { SkASSERT(rec[i - 1].fPos <= rec[i].fPos); } @@ -162,7 +140,7 @@ SkLinearGradient::LinearGradientContext::LinearGradientContext( for (int i = 0; i < count; ++i) { SkPMColor pmc = SkPreMultiplyColor(shader.fOrigColors[i]); pmc = SkAlphaMulQ(pmc, alphaScale); - rec[i].fColor = Sk4f::FromBytes((const uint8_t*)&pmc); + rec[i].fColor = SkNx_cast(Sk4b::Load((const uint8_t*)&pmc)); if (i > 0) { SkASSERT(rec[i - 1].fPos <= rec[i].fPos); } @@ -282,7 +260,8 @@ void SkLinearGradient::LinearGradientContext::shadeSpan(int x, int y, SkPMColor* SkASSERT(count > 0); const SkLinearGradient& linearGradient = static_cast(fShader); -#ifndef SK_SUPPORT_LEGACY_LINEAR_GRADIENT_TABLE +// Only use the Sk4f impl when known to be fast. +#if defined(SKNX_IS_FAST) if (SkShader::kClamp_TileMode == linearGradient.fTileMode && kLinear_MatrixClass == fDstToIndexClass) { @@ -346,176 +325,10 @@ SkShader::GradientType SkLinearGradient::asAGradient(GradientInfo* info) const { return kLinear_GradientType; } -static void dither_memset16(uint16_t dst[], uint16_t value, uint16_t other, - int count) { - if (reinterpret_cast(dst) & 2) { - *dst++ = value; - count -= 1; - SkTSwap(value, other); - } - - sk_memset32((uint32_t*)dst, (value << 16) | other, count >> 1); - - if (count & 1) { - dst[count - 1] = value; - } -} - -#define NO_CHECK_ITER_16 \ - do { \ - unsigned fi = SkGradFixedToFixed(fx) >> SkGradientShaderBase::kCache16Shift; \ - SkASSERT(fi < SkGradientShaderBase::kCache16Count); \ - fx += dx; \ - *dstC++ = cache[toggle + fi]; \ - toggle = next_dither_toggle16(toggle); \ - } while (0) - -namespace { - -typedef void (*LinearShade16Proc)(TileProc proc, SkGradFixed dx, SkGradFixed fx, - uint16_t* dstC, const uint16_t* cache, - int toggle, int count); - -void shadeSpan16_linear_vertical(TileProc proc, SkGradFixed dx, SkGradFixed fx, - uint16_t* SK_RESTRICT dstC, - const uint16_t* SK_RESTRICT cache, - int toggle, int count) { - // we're a vertical gradient, so no change in a span - unsigned fi = proc(SkGradFixedToFixed(fx)) >> SkGradientShaderBase::kCache16Shift; - SkASSERT(fi < SkGradientShaderBase::kCache16Count); - dither_memset16(dstC, cache[toggle + fi], - cache[next_dither_toggle16(toggle) + fi], count); -} - -void shadeSpan16_linear_clamp(TileProc proc, SkGradFixed dx, SkGradFixed fx, - uint16_t* SK_RESTRICT dstC, - const uint16_t* SK_RESTRICT cache, - int toggle, int count) { - SkClampRange range; - range.init(fx, dx, count, 0, SkGradientShaderBase::kCache32Count - 1); - range.validate(count); - - if ((count = range.fCount0) > 0) { - dither_memset16(dstC, - cache[toggle + range.fV0], - cache[next_dither_toggle16(toggle) + range.fV0], - count); - dstC += count; - } - if ((count = range.fCount1) > 0) { - int unroll = count >> 3; - fx = range.fFx1; - for (int i = 0; i < unroll; i++) { - NO_CHECK_ITER_16; NO_CHECK_ITER_16; - NO_CHECK_ITER_16; NO_CHECK_ITER_16; - NO_CHECK_ITER_16; NO_CHECK_ITER_16; - NO_CHECK_ITER_16; NO_CHECK_ITER_16; - } - if ((count &= 7) > 0) { - do { - NO_CHECK_ITER_16; - } while (--count != 0); - } - } - if ((count = range.fCount2) > 0) { - dither_memset16(dstC, - cache[toggle + range.fV1], - cache[next_dither_toggle16(toggle) + range.fV1], - count); - } -} - -void shadeSpan16_linear_mirror(TileProc proc, SkGradFixed dx, SkGradFixed fx, - uint16_t* SK_RESTRICT dstC, - const uint16_t* SK_RESTRICT cache, - int toggle, int count) { - do { - unsigned fi = mirror_bits(SkGradFixedToFixed(fx) >> SkGradientShaderBase::kCache16Shift, - SkGradientShaderBase::kCache16Bits); - SkASSERT(fi < SkGradientShaderBase::kCache16Count); - fx += dx; - *dstC++ = cache[toggle + fi]; - toggle = next_dither_toggle16(toggle); - } while (--count != 0); -} - -void shadeSpan16_linear_repeat(TileProc proc, SkGradFixed dx, SkGradFixed fx, - uint16_t* SK_RESTRICT dstC, - const uint16_t* SK_RESTRICT cache, - int toggle, int count) { - do { - unsigned fi = repeat_bits(SkGradFixedToFixed(fx) >> SkGradientShaderBase::kCache16Shift, - SkGradientShaderBase::kCache16Bits); - SkASSERT(fi < SkGradientShaderBase::kCache16Count); - fx += dx; - *dstC++ = cache[toggle + fi]; - toggle = next_dither_toggle16(toggle); - } while (--count != 0); -} -} - -static bool fixed_nearly_zero(SkFixed x) { - return SkAbs32(x) < (SK_Fixed1 >> 12); -} - -void SkLinearGradient::LinearGradientContext::shadeSpan16(int x, int y, - uint16_t* SK_RESTRICT dstC, int count) { - SkASSERT(count > 0); - - const SkLinearGradient& linearGradient = static_cast(fShader); - - SkPoint srcPt; - SkMatrix::MapXYProc dstProc = fDstToIndexProc; - TileProc proc = linearGradient.fTileProc; - const uint16_t* SK_RESTRICT cache = fCache->getCache16(); - int toggle = init_dither_toggle16(x, y); - - if (fDstToIndexClass != kPerspective_MatrixClass) { - dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf, - SkIntToScalar(y) + SK_ScalarHalf, &srcPt); - SkGradFixed dx, fx = SkScalarToGradFixed(srcPt.fX); - - if (fDstToIndexClass == kFixedStepInX_MatrixClass) { - SkFixed dxStorage[1]; - (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), dxStorage, nullptr); - // todo: do we need a real/high-precision value for dx here? - dx = SkFixedToGradFixed(dxStorage[0]); - } else { - SkASSERT(fDstToIndexClass == kLinear_MatrixClass); - dx = SkScalarToGradFixed(fDstToIndex.getScaleX()); - } - - LinearShade16Proc shadeProc = shadeSpan16_linear_repeat; - if (fixed_nearly_zero(SkGradFixedToFixed(dx))) { - shadeProc = shadeSpan16_linear_vertical; - } else if (SkShader::kClamp_TileMode == linearGradient.fTileMode) { - shadeProc = shadeSpan16_linear_clamp; - } else if (SkShader::kMirror_TileMode == linearGradient.fTileMode) { - shadeProc = shadeSpan16_linear_mirror; - } else { - SkASSERT(SkShader::kRepeat_TileMode == linearGradient.fTileMode); - } - (*shadeProc)(proc, dx, fx, dstC, cache, toggle, count); - } else { - SkScalar dstX = SkIntToScalar(x); - SkScalar dstY = SkIntToScalar(y); - do { - dstProc(fDstToIndex, dstX, dstY, &srcPt); - unsigned fi = proc(SkScalarToFixed(srcPt.fX)); - SkASSERT(fi <= 0xFFFF); - - int index = fi >> kCache16Shift; - *dstC++ = cache[toggle + index]; - toggle = next_dither_toggle16(toggle); - - dstX += SK_Scalar1; - } while (--count != 0); - } -} - #if SK_SUPPORT_GPU -#include "gl/builders/GrGLProgramBuilder.h" +#include "glsl/GrGLSLCaps.h" +#include "glsl/GrGLSLFragmentShaderBuilder.h" #include "SkGr.h" ///////////////////////////////////////////////////////////////////// @@ -603,11 +416,11 @@ const GrFragmentProcessor* GrLinearGradient::TestCreate(GrProcessorTestData* d) void GrGLLinearGradient::emitCode(EmitArgs& args) { const GrLinearGradient& ge = args.fFp.cast(); - this->emitUniforms(args.fBuilder, ge); + this->emitUniforms(args.fUniformHandler, ge); SkString t = args.fFragBuilder->ensureFSCoords2D(args.fCoords, 0); t.append(".x"); - this->emitColor(args.fBuilder, - args.fFragBuilder, + this->emitColor(args.fFragBuilder, + args.fUniformHandler, args.fGLSLCaps, ge, t.c_str(), args.fOutputColor, @@ -697,7 +510,7 @@ find_backward(const SkLinearGradient::LinearGradientContext::Rec rec[], float ti template SkPMColor trunc_from_255(const Sk4f& x) { SkPMColor c; - x.toBytes((uint8_t*)&c); + SkNx_cast(x).store((uint8_t*)&c); if (apply_alpha) { c = SkPreMultiplyARGB(SkGetPackedA32(c), SkGetPackedR32(c), SkGetPackedG32(c), SkGetPackedB32(c)); @@ -749,7 +562,7 @@ template void ramp(SkPMColor dstC[], int n, const Sk4f& c, co Sk4f cd3 = cd1 + dc2; while (n >= 4) { if (!apply_alpha) { - Sk4f::ToBytes((uint8_t*)dstC, cd0, cd1, cd2, cd3); + Sk4f_ToBytes((uint8_t*)dstC, cd0, cd1, cd2, cd3); dstC += 4; } else { *dstC++ = trunc_from_255(cd0); @@ -855,12 +668,12 @@ void SkLinearGradient::LinearGradientContext::shade4_dx_clamp(SkPMColor dstC[], } fx += n * dx; - count -= n; - SkASSERT(count >= 0); + // fx should now outside of the p0..p1 interval. However, due to float precision loss, + // its possible that fx is slightly too small/large, so we clamp it. if (dx_is_pos) { - SkASSERT(0 == count || fx >= p1); + fx = SkTMax(fx, p1); } else { - SkASSERT(0 == count || fx <= p0); + fx = SkTMin(fx, p0); } ramp(dstC, n, c, dc, dither0, dither1); @@ -870,6 +683,9 @@ void SkLinearGradient::LinearGradientContext::shade4_dx_clamp(SkPMColor dstC[], if (n & 1) { SkTSwap(dither0, dither1); } + + count -= n; + SkASSERT(count >= 0); } } diff --git a/gfx/skia/skia/src/effects/gradients/SkLinearGradient.h b/gfx/skia/skia/src/effects/gradients/SkLinearGradient.h index 73efd2e778..c8b91a5537 100644 --- a/gfx/skia/skia/src/effects/gradients/SkLinearGradient.h +++ b/gfx/skia/skia/src/effects/gradients/SkLinearGradient.h @@ -35,7 +35,6 @@ public: LinearGradientContext(const SkLinearGradient&, const ContextRec&); void shadeSpan(int x, int y, SkPMColor dstC[], int count) override; - void shadeSpan16(int x, int y, uint16_t dstC[], int count) override; struct Rec { Sk4fStorage fColor; diff --git a/gfx/skia/skia/src/effects/gradients/SkRadialGradient.cpp b/gfx/skia/skia/src/effects/gradients/SkRadialGradient.cpp index 7be8ed111b..7e7ac7a406 100644 --- a/gfx/skia/skia/src/effects/gradients/SkRadialGradient.cpp +++ b/gfx/skia/skia/src/effects/gradients/SkRadialGradient.cpp @@ -7,46 +7,8 @@ */ #include "SkRadialGradient.h" -#include "SkRadialGradient_Table.h" #include "SkNx.h" -#define kSQRT_TABLE_BITS 11 -#define kSQRT_TABLE_SIZE (1 << kSQRT_TABLE_BITS) - -static_assert(sizeof(gSqrt8Table) == kSQRT_TABLE_SIZE, "SqrtTableSizesMatch"); - -#if 0 - -#include - -void SkRadialGradient_BuildTable() { - // build it 0..127 x 0..127, so we use 2^15 - 1 in the numerator for our "fixed" table - - FILE* file = ::fopen("SkRadialGradient_Table.h", "w"); - SkASSERT(file); - ::fprintf(file, "static const uint8_t gSqrt8Table[] = {\n"); - - for (int i = 0; i < kSQRT_TABLE_SIZE; i++) { - if ((i & 15) == 0) { - ::fprintf(file, "\t"); - } - - uint8_t value = SkToU8(SkFixedSqrt(i * SK_Fixed1 / kSQRT_TABLE_SIZE) >> 8); - - ::fprintf(file, "0x%02X", value); - if (i < kSQRT_TABLE_SIZE-1) { - ::fprintf(file, ", "); - } - if ((i & 15) == 15) { - ::fprintf(file, "\n"); - } - } - ::fprintf(file, "};\n"); - ::fclose(file); -} - -#endif - namespace { // GCC doesn't like using static functions as template arguments. So force these to be non-static. @@ -67,83 +29,6 @@ SkMatrix rad_to_unit_matrix(const SkPoint& center, SkScalar radius) { return matrix; } -typedef void (* RadialShade16Proc)(SkScalar sfx, SkScalar sdx, - SkScalar sfy, SkScalar sdy, - uint16_t* dstC, const uint16_t* cache, - int toggle, int count); - -void shadeSpan16_radial_clamp(SkScalar sfx, SkScalar sdx, - SkScalar sfy, SkScalar sdy, - uint16_t* SK_RESTRICT dstC, const uint16_t* SK_RESTRICT cache, - int toggle, int count) { - const uint8_t* SK_RESTRICT sqrt_table = gSqrt8Table; - - /* knock these down so we can pin against +- 0x7FFF, which is an - immediate load, rather than 0xFFFF which is slower. This is a - compromise, since it reduces our precision, but that appears - to be visually OK. If we decide this is OK for all of our cases, - we could (it seems) put this scale-down into fDstToIndex, - to avoid having to do these extra shifts each time. - */ - SkFixed fx = SkScalarToFixed(sfx) >> 1; - SkFixed dx = SkScalarToFixed(sdx) >> 1; - SkFixed fy = SkScalarToFixed(sfy) >> 1; - SkFixed dy = SkScalarToFixed(sdy) >> 1; - // might perform this check for the other modes, - // but the win will be a smaller % of the total - if (dy == 0) { - fy = SkTPin(fy, -0xFFFF >> 1, 0xFFFF >> 1); - fy *= fy; - do { - unsigned xx = SkTPin(fx, -0xFFFF >> 1, 0xFFFF >> 1); - unsigned fi = (xx * xx + fy) >> (14 + 16 - kSQRT_TABLE_BITS); - fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS)); - fx += dx; - *dstC++ = cache[toggle + - (sqrt_table[fi] >> SkGradientShaderBase::kSqrt16Shift)]; - toggle = next_dither_toggle16(toggle); - } while (--count != 0); - } else { - do { - unsigned xx = SkTPin(fx, -0xFFFF >> 1, 0xFFFF >> 1); - unsigned fi = SkTPin(fy, -0xFFFF >> 1, 0xFFFF >> 1); - fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS); - fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS)); - fx += dx; - fy += dy; - *dstC++ = cache[toggle + - (sqrt_table[fi] >> SkGradientShaderBase::kSqrt16Shift)]; - toggle = next_dither_toggle16(toggle); - } while (--count != 0); - } -} - -template -void shadeSpan16_radial(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy, - uint16_t* SK_RESTRICT dstC, const uint16_t* SK_RESTRICT cache, - int toggle, int count) { - do { - const SkFixed dist = SkFloatToFixed(sk_float_sqrt(fx*fx + fy*fy)); - const unsigned fi = TileProc(dist); - SkASSERT(fi <= 0xFFFF); - *dstC++ = cache[toggle + (fi >> SkGradientShaderBase::kCache16Shift)]; - toggle = next_dither_toggle16(toggle); - fx += dx; - fy += dy; - } while (--count != 0); -} - -void shadeSpan16_radial_mirror(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy, - uint16_t* SK_RESTRICT dstC, const uint16_t* SK_RESTRICT cache, - int toggle, int count) { - shadeSpan16_radial(fx, dx, fy, dy, dstC, cache, toggle, count); -} - -void shadeSpan16_radial_repeat(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy, - uint16_t* SK_RESTRICT dstC, const uint16_t* SK_RESTRICT cache, - int toggle, int count) { - shadeSpan16_radial(fx, dx, fy, dy, dstC, cache, toggle, count); -} } // namespace @@ -167,64 +52,6 @@ SkRadialGradient::RadialGradientContext::RadialGradientContext( const SkRadialGradient& shader, const ContextRec& rec) : INHERITED(shader, rec) {} -void SkRadialGradient::RadialGradientContext::shadeSpan16(int x, int y, uint16_t* dstCParam, - int count) { - SkASSERT(count > 0); - - const SkRadialGradient& radialGradient = static_cast(fShader); - - uint16_t* SK_RESTRICT dstC = dstCParam; - - SkPoint srcPt; - SkMatrix::MapXYProc dstProc = fDstToIndexProc; - TileProc proc = radialGradient.fTileProc; - const uint16_t* SK_RESTRICT cache = fCache->getCache16(); - int toggle = init_dither_toggle16(x, y); - - if (fDstToIndexClass != kPerspective_MatrixClass) { - dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf, - SkIntToScalar(y) + SK_ScalarHalf, &srcPt); - - SkScalar sdx = fDstToIndex.getScaleX(); - SkScalar sdy = fDstToIndex.getSkewY(); - - if (fDstToIndexClass == kFixedStepInX_MatrixClass) { - SkFixed storage[2]; - (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), - &storage[0], &storage[1]); - sdx = SkFixedToScalar(storage[0]); - sdy = SkFixedToScalar(storage[1]); - } else { - SkASSERT(fDstToIndexClass == kLinear_MatrixClass); - } - - RadialShade16Proc shadeProc = shadeSpan16_radial_repeat; - if (SkShader::kClamp_TileMode == radialGradient.fTileMode) { - shadeProc = shadeSpan16_radial_clamp; - } else if (SkShader::kMirror_TileMode == radialGradient.fTileMode) { - shadeProc = shadeSpan16_radial_mirror; - } else { - SkASSERT(SkShader::kRepeat_TileMode == radialGradient.fTileMode); - } - (*shadeProc)(srcPt.fX, sdx, srcPt.fY, sdy, dstC, - cache, toggle, count); - } else { // perspective case - SkScalar dstX = SkIntToScalar(x); - SkScalar dstY = SkIntToScalar(y); - do { - dstProc(fDstToIndex, dstX, dstY, &srcPt); - unsigned fi = proc(SkScalarToFixed(srcPt.length())); - SkASSERT(fi <= 0xFFFF); - - int index = fi >> (16 - kCache16Bits); - *dstC++ = cache[toggle + index]; - toggle = next_dither_toggle16(toggle); - - dstX += SK_Scalar1; - } while (--count != 0); - } -} - SkShader::GradientType SkRadialGradient::asAGradient(GradientInfo* info) const { if (info) { commonAsAGradient(info); @@ -307,7 +134,7 @@ void shadeSpan_radial_clamp2(SkScalar sfx, SkScalar sdx, SkScalar sfy, SkScalar dR = dR + ddR; uint8_t fi[4]; - dist.toBytes(fi); + SkNx_cast(dist).store(fi); for (int i = 0; i < 4; i++) { *dstC++ = cache[toggle + fi[i]]; @@ -319,7 +146,7 @@ void shadeSpan_radial_clamp2(SkScalar sfx, SkScalar sdx, SkScalar sfy, SkScalar Sk4f dist = Sk4f::Min(fast_sqrt(R), max); uint8_t fi[4]; - dist.toBytes(fi); + SkNx_cast(dist).store(fi); for (int i = 0; i < count; i++) { *dstC++ = cache[toggle + fi[i]]; toggle = next_dither_toggle(toggle); @@ -415,7 +242,8 @@ void SkRadialGradient::RadialGradientContext::shadeSpan(int x, int y, #if SK_SUPPORT_GPU #include "SkGr.h" -#include "gl/builders/GrGLProgramBuilder.h" +#include "glsl/GrGLSLCaps.h" +#include "glsl/GrGLSLFragmentShaderBuilder.h" class GrGLRadialGradient : public GrGLGradientEffect { public: @@ -499,12 +327,12 @@ const GrFragmentProcessor* GrRadialGradient::TestCreate(GrProcessorTestData* d) void GrGLRadialGradient::emitCode(EmitArgs& args) { const GrRadialGradient& ge = args.fFp.cast(); - this->emitUniforms(args.fBuilder, ge); + this->emitUniforms(args.fUniformHandler, ge); SkString t("length("); t.append(args.fFragBuilder->ensureFSCoords2D(args.fCoords, 0)); t.append(")"); - this->emitColor(args.fBuilder, - args.fFragBuilder, + this->emitColor(args.fFragBuilder, + args.fUniformHandler, args.fGLSLCaps, ge, t.c_str(), args.fOutputColor, diff --git a/gfx/skia/skia/src/effects/gradients/SkRadialGradient.h b/gfx/skia/skia/src/effects/gradients/SkRadialGradient.h index afffddd22d..bd6fb3542f 100644 --- a/gfx/skia/skia/src/effects/gradients/SkRadialGradient.h +++ b/gfx/skia/skia/src/effects/gradients/SkRadialGradient.h @@ -22,7 +22,6 @@ public: RadialGradientContext(const SkRadialGradient&, const ContextRec&); void shadeSpan(int x, int y, SkPMColor dstC[], int count) override; - void shadeSpan16(int x, int y, uint16_t dstC[], int count) override; private: typedef SkGradientShaderBase::GradientShaderBaseContext INHERITED; diff --git a/gfx/skia/skia/src/effects/gradients/SkRadialGradient_Table.h b/gfx/skia/skia/src/effects/gradients/SkRadialGradient_Table.h deleted file mode 100644 index 9a8a5f816e..0000000000 --- a/gfx/skia/skia/src/effects/gradients/SkRadialGradient_Table.h +++ /dev/null @@ -1,139 +0,0 @@ - -/* - * Copyright 2006 The Android Open Source Project - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - - -static const uint8_t gSqrt8Table[] = { - 0x00, 0x05, 0x08, 0x09, 0x0B, 0x0C, 0x0D, 0x0E, 0x10, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x15, - 0x16, 0x17, 0x18, 0x18, 0x19, 0x19, 0x1A, 0x1B, 0x1B, 0x1C, 0x1C, 0x1D, 0x1D, 0x1E, 0x1E, 0x1F, - 0x20, 0x20, 0x20, 0x21, 0x21, 0x22, 0x22, 0x23, 0x23, 0x24, 0x24, 0x25, 0x25, 0x25, 0x26, 0x26, - 0x27, 0x27, 0x28, 0x28, 0x28, 0x29, 0x29, 0x29, 0x2A, 0x2A, 0x2B, 0x2B, 0x2B, 0x2C, 0x2C, 0x2C, - 0x2D, 0x2D, 0x2D, 0x2E, 0x2E, 0x2E, 0x2F, 0x2F, 0x30, 0x30, 0x30, 0x30, 0x31, 0x31, 0x31, 0x32, - 0x32, 0x32, 0x33, 0x33, 0x33, 0x34, 0x34, 0x34, 0x35, 0x35, 0x35, 0x35, 0x36, 0x36, 0x36, 0x37, - 0x37, 0x37, 0x38, 0x38, 0x38, 0x38, 0x39, 0x39, 0x39, 0x39, 0x3A, 0x3A, 0x3A, 0x3B, 0x3B, 0x3B, - 0x3B, 0x3C, 0x3C, 0x3C, 0x3C, 0x3D, 0x3D, 0x3D, 0x3D, 0x3E, 0x3E, 0x3E, 0x3E, 0x3F, 0x3F, 0x3F, - 0x40, 0x40, 0x40, 0x40, 0x40, 0x41, 0x41, 0x41, 0x41, 0x42, 0x42, 0x42, 0x42, 0x43, 0x43, 0x43, - 0x43, 0x44, 0x44, 0x44, 0x44, 0x45, 0x45, 0x45, 0x45, 0x45, 0x46, 0x46, 0x46, 0x46, 0x47, 0x47, - 0x47, 0x47, 0x48, 0x48, 0x48, 0x48, 0x48, 0x49, 0x49, 0x49, 0x49, 0x49, 0x4A, 0x4A, 0x4A, 0x4A, - 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4C, 0x4C, 0x4C, 0x4C, 0x4C, 0x4D, 0x4D, 0x4D, 0x4D, 0x4D, 0x4E, - 0x4E, 0x4E, 0x4E, 0x4E, 0x4F, 0x4F, 0x4F, 0x4F, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x51, 0x51, - 0x51, 0x51, 0x51, 0x52, 0x52, 0x52, 0x52, 0x52, 0x53, 0x53, 0x53, 0x53, 0x53, 0x54, 0x54, 0x54, - 0x54, 0x54, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x56, 0x56, 0x56, 0x56, 0x56, 0x57, 0x57, 0x57, - 0x57, 0x57, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x5A, 0x5A, - 0x5A, 0x5A, 0x5A, 0x5B, 0x5B, 0x5B, 0x5B, 0x5B, 0x5B, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5D, - 0x5D, 0x5D, 0x5D, 0x5D, 0x5D, 0x5E, 0x5E, 0x5E, 0x5E, 0x5E, 0x5E, 0x5F, 0x5F, 0x5F, 0x5F, 0x5F, - 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x62, 0x62, 0x62, - 0x62, 0x62, 0x62, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x65, - 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x67, 0x67, 0x67, 0x67, - 0x67, 0x67, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, - 0x6A, 0x6A, 0x6A, 0x6A, 0x6A, 0x6A, 0x6B, 0x6B, 0x6B, 0x6B, 0x6B, 0x6B, 0x6B, 0x6C, 0x6C, 0x6C, - 0x6C, 0x6C, 0x6C, 0x6C, 0x6D, 0x6D, 0x6D, 0x6D, 0x6D, 0x6D, 0x6D, 0x6E, 0x6E, 0x6E, 0x6E, 0x6E, - 0x6E, 0x6E, 0x6F, 0x6F, 0x6F, 0x6F, 0x6F, 0x6F, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, - 0x71, 0x71, 0x71, 0x71, 0x71, 0x71, 0x71, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x73, 0x73, - 0x73, 0x73, 0x73, 0x73, 0x73, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x75, 0x75, 0x75, 0x75, - 0x75, 0x75, 0x75, 0x75, 0x76, 0x76, 0x76, 0x76, 0x76, 0x76, 0x76, 0x77, 0x77, 0x77, 0x77, 0x77, - 0x77, 0x77, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, - 0x79, 0x79, 0x7A, 0x7A, 0x7A, 0x7A, 0x7A, 0x7A, 0x7A, 0x7B, 0x7B, 0x7B, 0x7B, 0x7B, 0x7B, 0x7B, - 0x7B, 0x7C, 0x7C, 0x7C, 0x7C, 0x7C, 0x7C, 0x7C, 0x7C, 0x7D, 0x7D, 0x7D, 0x7D, 0x7D, 0x7D, 0x7D, - 0x7D, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, - 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, - 0x81, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, - 0x83, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, - 0x85, 0x85, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, - 0x87, 0x87, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x89, 0x89, 0x89, 0x89, 0x89, - 0x89, 0x89, 0x89, 0x89, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8B, 0x8B, 0x8B, 0x8B, - 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8D, 0x8D, - 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, - 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, - 0x90, 0x90, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x92, 0x92, 0x92, 0x92, 0x92, - 0x92, 0x92, 0x92, 0x92, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x94, 0x94, 0x94, - 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, - 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, - 0x97, 0x97, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x99, 0x99, 0x99, 0x99, - 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9B, - 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, - 0x9C, 0x9C, 0x9C, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9E, 0x9E, 0x9E, - 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, - 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, - 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA3, - 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, - 0xA4, 0xA4, 0xA4, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA6, 0xA6, - 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, - 0xA7, 0xA7, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA9, 0xA9, 0xA9, - 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, - 0xAA, 0xAA, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAC, 0xAC, 0xAC, - 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, - 0xAD, 0xAD, 0xAD, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAF, 0xAF, - 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, - 0xB0, 0xB0, 0xB0, 0xB0, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB2, - 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, - 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, - 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB6, 0xB6, 0xB6, 0xB6, - 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, - 0xB7, 0xB7, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB9, 0xB9, - 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, - 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, - 0xBB, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBD, 0xBD, 0xBD, - 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, - 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, - 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC1, 0xC1, 0xC1, - 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, - 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, - 0xC3, 0xC4, 0xC4, 0xC4, 0xC4, 0xC4, 0xC4, 0xC4, 0xC4, 0xC4, 0xC4, 0xC4, 0xC4, 0xC5, 0xC5, 0xC5, - 0xC5, 0xC5, 0xC5, 0xC5, 0xC5, 0xC5, 0xC5, 0xC5, 0xC5, 0xC5, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, - 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC7, 0xC7, 0xC7, 0xC7, 0xC7, 0xC7, 0xC7, 0xC7, 0xC7, 0xC7, - 0xC7, 0xC7, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC9, - 0xC9, 0xC9, 0xC9, 0xC9, 0xC9, 0xC9, 0xC9, 0xC9, 0xC9, 0xC9, 0xC9, 0xC9, 0xCA, 0xCA, 0xCA, 0xCA, - 0xCA, 0xCA, 0xCA, 0xCA, 0xCA, 0xCA, 0xCA, 0xCA, 0xCB, 0xCB, 0xCB, 0xCB, 0xCB, 0xCB, 0xCB, 0xCB, - 0xCB, 0xCB, 0xCB, 0xCB, 0xCB, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, - 0xCC, 0xCC, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCE, - 0xCE, 0xCE, 0xCE, 0xCE, 0xCE, 0xCE, 0xCE, 0xCE, 0xCE, 0xCE, 0xCE, 0xCE, 0xCF, 0xCF, 0xCF, 0xCF, - 0xCF, 0xCF, 0xCF, 0xCF, 0xCF, 0xCF, 0xCF, 0xCF, 0xD0, 0xD0, 0xD0, 0xD0, 0xD0, 0xD0, 0xD0, 0xD0, - 0xD0, 0xD0, 0xD0, 0xD0, 0xD0, 0xD0, 0xD1, 0xD1, 0xD1, 0xD1, 0xD1, 0xD1, 0xD1, 0xD1, 0xD1, 0xD1, - 0xD1, 0xD1, 0xD1, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, - 0xD3, 0xD3, 0xD3, 0xD3, 0xD3, 0xD3, 0xD3, 0xD3, 0xD3, 0xD3, 0xD3, 0xD3, 0xD3, 0xD4, 0xD4, 0xD4, - 0xD4, 0xD4, 0xD4, 0xD4, 0xD4, 0xD4, 0xD4, 0xD4, 0xD4, 0xD4, 0xD5, 0xD5, 0xD5, 0xD5, 0xD5, 0xD5, - 0xD5, 0xD5, 0xD5, 0xD5, 0xD5, 0xD5, 0xD5, 0xD5, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, - 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD7, 0xD7, 0xD7, 0xD7, 0xD7, 0xD7, 0xD7, 0xD7, 0xD7, 0xD7, 0xD7, - 0xD7, 0xD7, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, - 0xD9, 0xD9, 0xD9, 0xD9, 0xD9, 0xD9, 0xD9, 0xD9, 0xD9, 0xD9, 0xD9, 0xD9, 0xD9, 0xD9, 0xDA, 0xDA, - 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, - 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, - 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, - 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDE, 0xDE, 0xDE, 0xDE, 0xDE, 0xDE, 0xDE, 0xDE, 0xDE, 0xDE, 0xDE, - 0xDE, 0xDE, 0xDE, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, - 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE1, - 0xE1, 0xE1, 0xE1, 0xE1, 0xE1, 0xE1, 0xE1, 0xE1, 0xE1, 0xE1, 0xE1, 0xE1, 0xE1, 0xE2, 0xE2, 0xE2, - 0xE2, 0xE2, 0xE2, 0xE2, 0xE2, 0xE2, 0xE2, 0xE2, 0xE2, 0xE2, 0xE2, 0xE3, 0xE3, 0xE3, 0xE3, 0xE3, - 0xE3, 0xE3, 0xE3, 0xE3, 0xE3, 0xE3, 0xE3, 0xE3, 0xE3, 0xE4, 0xE4, 0xE4, 0xE4, 0xE4, 0xE4, 0xE4, - 0xE4, 0xE4, 0xE4, 0xE4, 0xE4, 0xE4, 0xE4, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, - 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, - 0xE6, 0xE6, 0xE6, 0xE6, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, - 0xE7, 0xE7, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, - 0xE8, 0xE9, 0xE9, 0xE9, 0xE9, 0xE9, 0xE9, 0xE9, 0xE9, 0xE9, 0xE9, 0xE9, 0xE9, 0xE9, 0xE9, 0xE9, - 0xEA, 0xEA, 0xEA, 0xEA, 0xEA, 0xEA, 0xEA, 0xEA, 0xEA, 0xEA, 0xEA, 0xEA, 0xEA, 0xEA, 0xEB, 0xEB, - 0xEB, 0xEB, 0xEB, 0xEB, 0xEB, 0xEB, 0xEB, 0xEB, 0xEB, 0xEB, 0xEB, 0xEB, 0xEB, 0xEC, 0xEC, 0xEC, - 0xEC, 0xEC, 0xEC, 0xEC, 0xEC, 0xEC, 0xEC, 0xEC, 0xEC, 0xEC, 0xEC, 0xEC, 0xED, 0xED, 0xED, 0xED, - 0xED, 0xED, 0xED, 0xED, 0xED, 0xED, 0xED, 0xED, 0xED, 0xED, 0xED, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, - 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEF, 0xEF, 0xEF, 0xEF, 0xEF, 0xEF, - 0xEF, 0xEF, 0xEF, 0xEF, 0xEF, 0xEF, 0xEF, 0xEF, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, - 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF1, 0xF1, 0xF1, 0xF1, 0xF1, 0xF1, 0xF1, 0xF1, - 0xF1, 0xF1, 0xF1, 0xF1, 0xF1, 0xF1, 0xF1, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, - 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF3, 0xF3, 0xF3, 0xF3, 0xF3, 0xF3, 0xF3, 0xF3, 0xF3, 0xF3, - 0xF3, 0xF3, 0xF3, 0xF3, 0xF3, 0xF4, 0xF4, 0xF4, 0xF4, 0xF4, 0xF4, 0xF4, 0xF4, 0xF4, 0xF4, 0xF4, - 0xF4, 0xF4, 0xF4, 0xF4, 0xF5, 0xF5, 0xF5, 0xF5, 0xF5, 0xF5, 0xF5, 0xF5, 0xF5, 0xF5, 0xF5, 0xF5, - 0xF5, 0xF5, 0xF5, 0xF5, 0xF6, 0xF6, 0xF6, 0xF6, 0xF6, 0xF6, 0xF6, 0xF6, 0xF6, 0xF6, 0xF6, 0xF6, - 0xF6, 0xF6, 0xF6, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, - 0xF7, 0xF7, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, - 0xF8, 0xF8, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, - 0xF9, 0xF9, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, - 0xFA, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, - 0xFB, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, - 0xFC, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, - 0xFD, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, - 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF -}; diff --git a/gfx/skia/skia/src/effects/gradients/SkSweepGradient.cpp b/gfx/skia/skia/src/effects/gradients/SkSweepGradient.cpp index 3df7144337..ddff882e38 100644 --- a/gfx/skia/skia/src/effects/gradients/SkSweepGradient.cpp +++ b/gfx/skia/skia/src/effects/gradients/SkSweepGradient.cpp @@ -63,6 +63,9 @@ static unsigned SkATan2_255(float y, float x) { static const float g255Over2PI = 40.584510488433314f; float result = sk_float_atan2(y, x); + if (!SkScalarIsFinite(result)) { + return 0; + } if (result < 0) { result += 2 * SK_ScalarPI; } @@ -116,59 +119,14 @@ void SkSweepGradient::SweepGradientContext::shadeSpan(int x, int y, SkPMColor* S } } -void SkSweepGradient::SweepGradientContext::shadeSpan16(int x, int y, uint16_t* SK_RESTRICT dstC, - int count) { - SkMatrix::MapXYProc proc = fDstToIndexProc; - const SkMatrix& matrix = fDstToIndex; - const uint16_t* SK_RESTRICT cache = fCache->getCache16(); - int toggle = init_dither_toggle16(x, y); - SkPoint srcPt; - - if (fDstToIndexClass != kPerspective_MatrixClass) { - proc(matrix, SkIntToScalar(x) + SK_ScalarHalf, - SkIntToScalar(y) + SK_ScalarHalf, &srcPt); - SkScalar dx, fx = srcPt.fX; - SkScalar dy, fy = srcPt.fY; - - if (fDstToIndexClass == kFixedStepInX_MatrixClass) { - SkFixed storage[2]; - (void)matrix.fixedStepInX(SkIntToScalar(y) + SK_ScalarHalf, - &storage[0], &storage[1]); - dx = SkFixedToScalar(storage[0]); - dy = SkFixedToScalar(storage[1]); - } else { - SkASSERT(fDstToIndexClass == kLinear_MatrixClass); - dx = matrix.getScaleX(); - dy = matrix.getSkewY(); - } - - for (; count > 0; --count) { - int index = SkATan2_255(fy, fx) >> (8 - kCache16Bits); - *dstC++ = cache[toggle + index]; - toggle = next_dither_toggle16(toggle); - fx += dx; - fy += dy; - } - } else { // perspective case - for (int stop = x + count; x < stop; x++) { - proc(matrix, SkIntToScalar(x) + SK_ScalarHalf, - SkIntToScalar(y) + SK_ScalarHalf, &srcPt); - - int index = SkATan2_255(srcPt.fY, srcPt.fX); - index >>= (8 - kCache16Bits); - *dstC++ = cache[toggle + index]; - toggle = next_dither_toggle16(toggle); - } - } -} - ///////////////////////////////////////////////////////////////////// #if SK_SUPPORT_GPU #include "SkGr.h" #include "gl/GrGLContext.h" -#include "gl/builders/GrGLProgramBuilder.h" +#include "glsl/GrGLSLCaps.h" +#include "glsl/GrGLSLFragmentShaderBuilder.h" class GrGLSweepGradient : public GrGLGradientEffect { public: @@ -247,7 +205,7 @@ const GrFragmentProcessor* GrSweepGradient::TestCreate(GrProcessorTestData* d) { void GrGLSweepGradient::emitCode(EmitArgs& args) { const GrSweepGradient& ge = args.fFp.cast(); - this->emitUniforms(args.fBuilder, ge); + this->emitUniforms(args.fUniformHandler, ge); SkString coords2D = args.fFragBuilder->ensureFSCoords2D(args.fCoords, 0); SkString t; // 0.1591549430918 is 1/(2*pi), used since atan returns values [-pi, pi] @@ -260,8 +218,8 @@ void GrGLSweepGradient::emitCode(EmitArgs& args) { t.printf("atan(- %s.y, - %s.x) * 0.1591549430918 + 0.5", coords2D.c_str(), coords2D.c_str()); } - this->emitColor(args.fBuilder, - args.fFragBuilder, + this->emitColor(args.fFragBuilder, + args.fUniformHandler, args.fGLSLCaps, ge, t.c_str(), args.fOutputColor, diff --git a/gfx/skia/skia/src/effects/gradients/SkSweepGradient.h b/gfx/skia/skia/src/effects/gradients/SkSweepGradient.h index 728a01474b..a6a0971323 100644 --- a/gfx/skia/skia/src/effects/gradients/SkSweepGradient.h +++ b/gfx/skia/skia/src/effects/gradients/SkSweepGradient.h @@ -22,7 +22,6 @@ public: SweepGradientContext(const SkSweepGradient& shader, const ContextRec&); void shadeSpan(int x, int y, SkPMColor dstC[], int count) override; - void shadeSpan16(int x, int y, uint16_t dstC[], int count) override; private: typedef SkGradientShaderBase::GradientShaderBaseContext INHERITED; diff --git a/gfx/skia/skia/src/effects/gradients/SkTwoPointConicalGradient.cpp b/gfx/skia/skia/src/effects/gradients/SkTwoPointConicalGradient.cpp index 790e1153d8..918f2e06dd 100644 --- a/gfx/skia/skia/src/effects/gradients/SkTwoPointConicalGradient.cpp +++ b/gfx/skia/skia/src/effects/gradients/SkTwoPointConicalGradient.cpp @@ -224,9 +224,6 @@ SkTwoPointConicalGradient::TwoPointConicalGradientContext::TwoPointConicalGradie const SkTwoPointConicalGradient& shader, const ContextRec& rec) : INHERITED(shader, rec) { - // we don't have a span16 proc - fFlags &= ~kHasSpan16_Flag; - // in general, we might discard based on computed-radius, so clear // this flag (todo: sometimes we can detect that we never discard...) fFlags &= ~kOpaqueAlpha_Flag; diff --git a/gfx/skia/skia/src/effects/gradients/SkTwoPointConicalGradient_gpu.cpp b/gfx/skia/skia/src/effects/gradients/SkTwoPointConicalGradient_gpu.cpp index 31ceda4f2a..ac32e72c53 100644 --- a/gfx/skia/skia/src/effects/gradients/SkTwoPointConicalGradient_gpu.cpp +++ b/gfx/skia/skia/src/effects/gradients/SkTwoPointConicalGradient_gpu.cpp @@ -11,10 +11,12 @@ #include "SkTwoPointConicalGradient.h" #if SK_SUPPORT_GPU +#include "GrCoordTransform.h" +#include "GrInvariantOutput.h" #include "GrPaint.h" #include "glsl/GrGLSLFragmentShaderBuilder.h" -#include "glsl/GrGLSLProgramBuilder.h" #include "glsl/GrGLSLProgramDataManager.h" +#include "glsl/GrGLSLUniformHandler.h" // For brevity typedef GrGLSLProgramDataManager::UniformHandle UniformHandle; @@ -223,10 +225,11 @@ GLEdge2PtConicalEffect::GLEdge2PtConicalEffect(const GrProcessor&) void GLEdge2PtConicalEffect::emitCode(EmitArgs& args) { const Edge2PtConicalEffect& ge = args.fFp.cast(); - this->emitUniforms(args.fBuilder, ge); - fParamUni = args.fBuilder->addUniformArray(GrGLSLProgramBuilder::kFragment_Visibility, - kFloat_GrSLType, kDefault_GrSLPrecision, - "Conical2FSParams", 3); + GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; + this->emitUniforms(uniformHandler, ge); + fParamUni = uniformHandler->addUniformArray(GrGLSLUniformHandler::kFragment_Visibility, + kFloat_GrSLType, kDefault_GrSLPrecision, + "Conical2FSParams", 3); SkString cName("c"); SkString tName("t"); @@ -234,9 +237,9 @@ void GLEdge2PtConicalEffect::emitCode(EmitArgs& args) { SkString p1; // start radius squared SkString p2; // difference in radii (r1 - r0) - args.fBuilder->getUniformVariable(fParamUni).appendArrayAccess(0, &p0); - args.fBuilder->getUniformVariable(fParamUni).appendArrayAccess(1, &p1); - args.fBuilder->getUniformVariable(fParamUni).appendArrayAccess(2, &p2); + uniformHandler->getUniformVariable(fParamUni).appendArrayAccess(0, &p0); + uniformHandler->getUniformVariable(fParamUni).appendArrayAccess(1, &p1); + uniformHandler->getUniformVariable(fParamUni).appendArrayAccess(2, &p2); // We interpolate the linear component in coords[1]. SkASSERT(args.fCoords[0].getType() == args.fCoords[1].getType()); @@ -270,8 +273,8 @@ void GLEdge2PtConicalEffect::emitCode(EmitArgs& args) { fragBuilder->codeAppendf("\tif (%s * %s + %s > 0.0) {\n", tName.c_str(), p2.c_str(), p0.c_str()); fragBuilder->codeAppend("\t"); - this->emitColor(args.fBuilder, - fragBuilder, + this->emitColor(fragBuilder, + uniformHandler, args.fGLSLCaps, ge, tName.c_str(), @@ -505,16 +508,17 @@ GLFocalOutside2PtConicalEffect::GLFocalOutside2PtConicalEffect(const GrProcessor void GLFocalOutside2PtConicalEffect::emitCode(EmitArgs& args) { const FocalOutside2PtConicalEffect& ge = args.fFp.cast(); - this->emitUniforms(args.fBuilder, ge); - fParamUni = args.fBuilder->addUniformArray(GrGLSLProgramBuilder::kFragment_Visibility, - kFloat_GrSLType, kDefault_GrSLPrecision, - "Conical2FSParams", 2); + GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; + this->emitUniforms(uniformHandler, ge); + fParamUni = uniformHandler->addUniformArray(GrGLSLUniformHandler::kFragment_Visibility, + kFloat_GrSLType, kDefault_GrSLPrecision, + "Conical2FSParams", 2); SkString tName("t"); SkString p0; // focalX SkString p1; // 1 - focalX * focalX - args.fBuilder->getUniformVariable(fParamUni).appendArrayAccess(0, &p0); - args.fBuilder->getUniformVariable(fParamUni).appendArrayAccess(1, &p1); + uniformHandler->getUniformVariable(fParamUni).appendArrayAccess(0, &p0); + uniformHandler->getUniformVariable(fParamUni).appendArrayAccess(1, &p1); // if we have a vec3 from being in perspective, convert it to a vec2 first GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder; @@ -543,8 +547,8 @@ void GLFocalOutside2PtConicalEffect::emitCode(EmitArgs& args) { fragBuilder->codeAppendf("\tif (%s >= 0.0 && d >= 0.0) {\n", tName.c_str()); fragBuilder->codeAppend("\t\t"); - this->emitColor(args.fBuilder, - fragBuilder, + this->emitColor(fragBuilder, + uniformHandler, args.fGLSLCaps, ge, tName.c_str(), @@ -714,15 +718,16 @@ GLFocalInside2PtConicalEffect::GLFocalInside2PtConicalEffect(const GrProcessor&) void GLFocalInside2PtConicalEffect::emitCode(EmitArgs& args) { const FocalInside2PtConicalEffect& ge = args.fFp.cast(); - this->emitUniforms(args.fBuilder, ge); - fFocalUni = args.fBuilder->addUniform(GrGLSLProgramBuilder::kFragment_Visibility, - kFloat_GrSLType, kDefault_GrSLPrecision, - "Conical2FSParams"); + GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; + this->emitUniforms(uniformHandler, ge); + fFocalUni = uniformHandler->addUniform(GrGLSLUniformHandler::kFragment_Visibility, + kFloat_GrSLType, kDefault_GrSLPrecision, + "Conical2FSParams"); SkString tName("t"); // this is the distance along x-axis from the end center to focal point in // transformed coordinates - GrGLSLShaderVar focal = args.fBuilder->getUniformVariable(fFocalUni); + GrGLSLShaderVar focal = uniformHandler->getUniformVariable(fFocalUni); // if we have a vec3 from being in perspective, convert it to a vec2 first GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder; @@ -731,10 +736,10 @@ void GLFocalInside2PtConicalEffect::emitCode(EmitArgs& args) { // t = p.x * focalX + length(p) fragBuilder->codeAppendf("\tfloat %s = %s.x * %s + length(%s);\n", tName.c_str(), - coords2D, focal.c_str(), coords2D); + coords2D, focal.c_str(), coords2D); - this->emitColor(args.fBuilder, - fragBuilder, + this->emitColor(fragBuilder, + uniformHandler, args.fGLSLCaps, ge, tName.c_str(), @@ -965,20 +970,21 @@ GLCircleInside2PtConicalEffect::GLCircleInside2PtConicalEffect(const GrProcessor void GLCircleInside2PtConicalEffect::emitCode(EmitArgs& args) { const CircleInside2PtConicalEffect& ge = args.fFp.cast(); - this->emitUniforms(args.fBuilder, ge); - fCenterUni = args.fBuilder->addUniform(GrGLSLProgramBuilder::kFragment_Visibility, - kVec2f_GrSLType, kDefault_GrSLPrecision, - "Conical2FSCenter"); - fParamUni = args.fBuilder->addUniform(GrGLSLProgramBuilder::kFragment_Visibility, - kVec3f_GrSLType, kDefault_GrSLPrecision, - "Conical2FSParams"); + GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; + this->emitUniforms(uniformHandler, ge); + fCenterUni = uniformHandler->addUniform(GrGLSLUniformHandler::kFragment_Visibility, + kVec2f_GrSLType, kDefault_GrSLPrecision, + "Conical2FSCenter"); + fParamUni = uniformHandler->addUniform(GrGLSLUniformHandler::kFragment_Visibility, + kVec3f_GrSLType, kDefault_GrSLPrecision, + "Conical2FSParams"); SkString tName("t"); - GrGLSLShaderVar center = args.fBuilder->getUniformVariable(fCenterUni); + GrGLSLShaderVar center = uniformHandler->getUniformVariable(fCenterUni); // params.x = A // params.y = B // params.z = C - GrGLSLShaderVar params = args.fBuilder->getUniformVariable(fParamUni); + GrGLSLShaderVar params = uniformHandler->getUniformVariable(fParamUni); // if we have a vec3 from being in perspective, convert it to a vec2 first GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder; @@ -999,8 +1005,8 @@ void GLCircleInside2PtConicalEffect::emitCode(EmitArgs& args) { fragBuilder->codeAppendf("\tfloat %s = d + sqrt(d * d - %s.x * pDotp + %s.z);\n", tName.c_str(), params.c_str(), params.c_str()); - this->emitColor(args.fBuilder, - fragBuilder, + this->emitColor(fragBuilder, + uniformHandler, args.fGLSLCaps, ge, tName.c_str(), @@ -1201,20 +1207,21 @@ GLCircleOutside2PtConicalEffect::GLCircleOutside2PtConicalEffect(const GrProcess void GLCircleOutside2PtConicalEffect::emitCode(EmitArgs& args) { const CircleOutside2PtConicalEffect& ge = args.fFp.cast(); - this->emitUniforms(args.fBuilder, ge); - fCenterUni = args.fBuilder->addUniform(GrGLSLProgramBuilder::kFragment_Visibility, - kVec2f_GrSLType, kDefault_GrSLPrecision, - "Conical2FSCenter"); - fParamUni = args.fBuilder->addUniform(GrGLSLProgramBuilder::kFragment_Visibility, - kVec4f_GrSLType, kDefault_GrSLPrecision, - "Conical2FSParams"); + GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; + this->emitUniforms(uniformHandler, ge); + fCenterUni = uniformHandler->addUniform(GrGLSLUniformHandler::kFragment_Visibility, + kVec2f_GrSLType, kDefault_GrSLPrecision, + "Conical2FSCenter"); + fParamUni = uniformHandler->addUniform(GrGLSLUniformHandler::kFragment_Visibility, + kVec4f_GrSLType, kDefault_GrSLPrecision, + "Conical2FSParams"); SkString tName("t"); - GrGLSLShaderVar center = args.fBuilder->getUniformVariable(fCenterUni); + GrGLSLShaderVar center = uniformHandler->getUniformVariable(fCenterUni); // params.x = A // params.y = B // params.z = C - GrGLSLShaderVar params = args.fBuilder->getUniformVariable(fParamUni); + GrGLSLShaderVar params = uniformHandler->getUniformVariable(fParamUni); // if we have a vec3 from being in perspective, convert it to a vec2 first GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder; @@ -1248,10 +1255,11 @@ void GLCircleOutside2PtConicalEffect::emitCode(EmitArgs& args) { fragBuilder->codeAppendf("\tfloat %s = d - sqrt(deter);\n", tName.c_str()); } - fragBuilder->codeAppendf("\tif (%s >= %s.w && deter >= 0.0) {\n", tName.c_str(), params.c_str()); + fragBuilder->codeAppendf("\tif (%s >= %s.w && deter >= 0.0) {\n", + tName.c_str(), params.c_str()); fragBuilder->codeAppend("\t\t"); - this->emitColor(args.fBuilder, - fragBuilder, + this->emitColor(fragBuilder, + uniformHandler, args.fGLSLCaps, ge, tName.c_str(), diff --git a/gfx/skia/skia/src/gpu/GrAtlasTextBlob.cpp b/gfx/skia/skia/src/gpu/GrAtlasTextBlob.cpp deleted file mode 100644 index 3fb7ac99c1..0000000000 --- a/gfx/skia/skia/src/gpu/GrAtlasTextBlob.cpp +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright 2015 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "GrAtlasTextBlob.h" - -#ifdef CACHE_SANITY_CHECK -void GrAtlasTextBlob::AssertEqual(const GrAtlasTextBlob& l, const GrAtlasTextBlob& r) { - SkASSERT(l.fSize == r.fSize); - SkASSERT(l.fPool == r.fPool); - - SkASSERT(l.fBlurRec.fSigma == r.fBlurRec.fSigma); - SkASSERT(l.fBlurRec.fStyle == r.fBlurRec.fStyle); - SkASSERT(l.fBlurRec.fQuality == r.fBlurRec.fQuality); - - SkASSERT(l.fStrokeInfo.fFrameWidth == r.fStrokeInfo.fFrameWidth); - SkASSERT(l.fStrokeInfo.fMiterLimit == r.fStrokeInfo.fMiterLimit); - SkASSERT(l.fStrokeInfo.fJoin == r.fStrokeInfo.fJoin); - - SkASSERT(l.fBigGlyphs.count() == r.fBigGlyphs.count()); - for (int i = 0; i < l.fBigGlyphs.count(); i++) { - const BigGlyph& lBigGlyph = l.fBigGlyphs[i]; - const BigGlyph& rBigGlyph = r.fBigGlyphs[i]; - - SkASSERT(lBigGlyph.fPath == rBigGlyph.fPath); - // We can't assert that these have the same translations - } - - SkASSERT(l.fKey == r.fKey); - SkASSERT(l.fViewMatrix.cheapEqualTo(r.fViewMatrix)); - SkASSERT(l.fPaintColor == r.fPaintColor); - SkASSERT(l.fMaxMinScale == r.fMaxMinScale); - SkASSERT(l.fMinMaxScale == r.fMinMaxScale); - SkASSERT(l.fTextType == r.fTextType); - - SkASSERT(l.fRunCount == r.fRunCount); - for (int i = 0; i < l.fRunCount; i++) { - const Run& lRun = l.fRuns[i]; - const Run& rRun = r.fRuns[i]; - - if (lRun.fStrike.get()) { - SkASSERT(rRun.fStrike.get()); - SkASSERT(GrBatchTextStrike::GetKey(*lRun.fStrike) == - GrBatchTextStrike::GetKey(*rRun.fStrike)); - - } else { - SkASSERT(!rRun.fStrike.get()); - } - - if (lRun.fTypeface.get()) { - SkASSERT(rRun.fTypeface.get()); - SkASSERT(SkTypeface::Equal(lRun.fTypeface, rRun.fTypeface)); - } else { - SkASSERT(!rRun.fTypeface.get()); - } - - // We offset bounds right before flush time so they will not be correct here - //SkASSERT(lRun.fVertexBounds == rRun.fVertexBounds); - - SkASSERT(lRun.fDescriptor.getDesc()); - SkASSERT(rRun.fDescriptor.getDesc()); - SkASSERT(lRun.fDescriptor.getDesc()->equals(*rRun.fDescriptor.getDesc())); - - if (lRun.fOverrideDescriptor.get()) { - SkASSERT(lRun.fOverrideDescriptor->getDesc()); - SkASSERT(rRun.fOverrideDescriptor.get() && rRun.fOverrideDescriptor->getDesc());; - SkASSERT(lRun.fOverrideDescriptor->getDesc()->equals( - *rRun.fOverrideDescriptor->getDesc())); - } else { - SkASSERT(!rRun.fOverrideDescriptor.get()); - } - - // color can be changed - //SkASSERT(lRun.fColor == rRun.fColor); - SkASSERT(lRun.fInitialized == rRun.fInitialized); - SkASSERT(lRun.fDrawAsPaths == rRun.fDrawAsPaths); - - SkASSERT(lRun.fSubRunInfo.count() == rRun.fSubRunInfo.count()); - for(int j = 0; j < lRun.fSubRunInfo.count(); j++) { - const Run::SubRunInfo& lSubRun = lRun.fSubRunInfo[j]; - const Run::SubRunInfo& rSubRun = rRun.fSubRunInfo[j]; - - SkASSERT(lSubRun.fVertexStartIndex == rSubRun.fVertexStartIndex); - SkASSERT(lSubRun.fVertexEndIndex == rSubRun.fVertexEndIndex); - SkASSERT(lSubRun.fGlyphStartIndex == rSubRun.fGlyphStartIndex); - SkASSERT(lSubRun.fGlyphEndIndex == rSubRun.fGlyphEndIndex); - SkASSERT(lSubRun.fTextRatio == rSubRun.fTextRatio); - SkASSERT(lSubRun.fMaskFormat == rSubRun.fMaskFormat); - SkASSERT(lSubRun.fDrawAsDistanceFields == rSubRun.fDrawAsDistanceFields); - SkASSERT(lSubRun.fUseLCDText == rSubRun.fUseLCDText); - - //We can't compare the bulk use tokens with this method - /* - SkASSERT(lSubRun.fBulkUseToken.fPlotsToUpdate.count() == - rSubRun.fBulkUseToken.fPlotsToUpdate.count()); - SkASSERT(lSubRun.fBulkUseToken.fPlotAlreadyUpdated == - rSubRun.fBulkUseToken.fPlotAlreadyUpdated); - for (int k = 0; k < lSubRun.fBulkUseToken.fPlotsToUpdate.count(); k++) { - SkASSERT(lSubRun.fBulkUseToken.fPlotsToUpdate[k] == - rSubRun.fBulkUseToken.fPlotsToUpdate[k]); - }*/ - } - } -} - -#endif diff --git a/gfx/skia/skia/src/gpu/GrAtlasTextContext.cpp b/gfx/skia/skia/src/gpu/GrAtlasTextContext.cpp deleted file mode 100644 index 7d0d93684c..0000000000 --- a/gfx/skia/skia/src/gpu/GrAtlasTextContext.cpp +++ /dev/null @@ -1,1398 +0,0 @@ -/* - * Copyright 2015 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ -#include "GrAtlasTextContext.h" - -#include "GrBlurUtils.h" -#include "GrDrawContext.h" -#include "GrDrawTarget.h" -#include "GrFontScaler.h" -#include "GrStrokeInfo.h" -#include "GrTextBlobCache.h" -#include "GrTexturePriv.h" -#include "GrVertexBuffer.h" - -#include "SkAutoKern.h" -#include "SkColorPriv.h" -#include "SkColorFilter.h" -#include "SkDistanceFieldGen.h" -#include "SkDraw.h" -#include "SkDrawFilter.h" -#include "SkDrawProcs.h" -#include "SkFindAndPlaceGlyph.h" -#include "SkGlyphCache.h" -#include "SkGpuDevice.h" -#include "SkGrPriv.h" -#include "SkPath.h" -#include "SkRTConf.h" -#include "SkStrokeRec.h" -#include "SkTextBlob.h" -#include "SkTextMapStateProc.h" - -#include "batches/GrAtlasTextBatch.h" - -namespace { -static const int kMinDFFontSize = 18; -static const int kSmallDFFontSize = 32; -static const int kSmallDFFontLimit = 32; -static const int kMediumDFFontSize = 72; -static const int kMediumDFFontLimit = 72; -static const int kLargeDFFontSize = 162; -#ifdef SK_BUILD_FOR_ANDROID -static const int kLargeDFFontLimit = 384; -#else -static const int kLargeDFFontLimit = 2 * kLargeDFFontSize; -#endif - -SkDEBUGCODE(static const int kExpectedDistanceAdjustTableSize = 8;) -}; - -GrAtlasTextContext::GrAtlasTextContext(GrContext* context, const SkSurfaceProps& surfaceProps) - : INHERITED(context, surfaceProps) - , fDistanceAdjustTable(new DistanceAdjustTable) { - // We overallocate vertices in our textblobs based on the assumption that A8 has the greatest - // vertexStride - static_assert(GrAtlasTextBatch::kGrayTextVASize >= GrAtlasTextBatch::kColorTextVASize && - GrAtlasTextBatch::kGrayTextVASize >= GrAtlasTextBatch::kLCDTextVASize, - "vertex_attribute_changed"); - fCurrStrike = nullptr; - fCache = context->getTextBlobCache(); -} - -void GrAtlasTextContext::DistanceAdjustTable::buildDistanceAdjustTable() { - - // This is used for an approximation of the mask gamma hack, used by raster and bitmap - // text. The mask gamma hack is based off of guessing what the blend color is going to - // be, and adjusting the mask so that when run through the linear blend will - // produce the value closest to the desired result. However, in practice this means - // that the 'adjusted' mask is just increasing or decreasing the coverage of - // the mask depending on what it is thought it will blit against. For black (on - // assumed white) this means that coverages are decreased (on a curve). For white (on - // assumed black) this means that coverages are increased (on a a curve). At - // middle (perceptual) gray (which could be blit against anything) the coverages - // remain the same. - // - // The idea here is that instead of determining the initial (real) coverage and - // then adjusting that coverage, we determine an adjusted coverage directly by - // essentially manipulating the geometry (in this case, the distance to the glyph - // edge). So for black (on assumed white) this thins a bit; for white (on - // assumed black) this fake bolds the geometry a bit. - // - // The distance adjustment is calculated by determining the actual coverage value which - // when fed into in the mask gamma table gives us an 'adjusted coverage' value of 0.5. This - // actual coverage value (assuming it's between 0 and 1) corresponds to a distance from the - // actual edge. So by subtracting this distance adjustment and computing without the - // the coverage adjustment we should get 0.5 coverage at the same point. - // - // This has several implications: - // For non-gray lcd smoothed text, each subpixel essentially is using a - // slightly different geometry. - // - // For black (on assumed white) this may not cover some pixels which were - // previously covered; however those pixels would have been only slightly - // covered and that slight coverage would have been decreased anyway. Also, some pixels - // which were previously fully covered may no longer be fully covered. - // - // For white (on assumed black) this may cover some pixels which weren't - // previously covered at all. - - int width, height; - size_t size; - -#ifdef SK_GAMMA_CONTRAST - SkScalar contrast = SK_GAMMA_CONTRAST; -#else - SkScalar contrast = 0.5f; -#endif - SkScalar paintGamma = SK_GAMMA_EXPONENT; - SkScalar deviceGamma = SK_GAMMA_EXPONENT; - - size = SkScalerContext::GetGammaLUTSize(contrast, paintGamma, deviceGamma, - &width, &height); - - SkASSERT(kExpectedDistanceAdjustTableSize == height); - fTable = new SkScalar[height]; - - SkAutoTArray data((int)size); - SkScalerContext::GetGammaLUTData(contrast, paintGamma, deviceGamma, data.get()); - - // find the inverse points where we cross 0.5 - // binsearch might be better, but we only need to do this once on creation - for (int row = 0; row < height; ++row) { - uint8_t* rowPtr = data.get() + row*width; - for (int col = 0; col < width - 1; ++col) { - if (rowPtr[col] <= 127 && rowPtr[col + 1] >= 128) { - // compute point where a mask value will give us a result of 0.5 - float interp = (127.5f - rowPtr[col]) / (rowPtr[col + 1] - rowPtr[col]); - float borderAlpha = (col + interp) / 255.f; - - // compute t value for that alpha - // this is an approximate inverse for smoothstep() - float t = borderAlpha*(borderAlpha*(4.0f*borderAlpha - 6.0f) + 5.0f) / 3.0f; - - // compute distance which gives us that t value - const float kDistanceFieldAAFactor = 0.65f; // should match SK_DistanceFieldAAFactor - float d = 2.0f*kDistanceFieldAAFactor*t - kDistanceFieldAAFactor; - - fTable[row] = d; - break; - } - } - } -} - -GrAtlasTextContext* GrAtlasTextContext::Create(GrContext* context, - const SkSurfaceProps& surfaceProps) { - return new GrAtlasTextContext(context, surfaceProps); -} - -bool GrAtlasTextContext::canDraw(const SkPaint& skPaint, const SkMatrix& viewMatrix) { - return this->canDrawAsDistanceFields(skPaint, viewMatrix) || - !SkDraw::ShouldDrawTextAsPaths(skPaint, viewMatrix); -} - -GrColor GrAtlasTextContext::ComputeCanonicalColor(const SkPaint& paint, bool lcd) { - GrColor canonicalColor = paint.computeLuminanceColor(); - if (lcd) { - // This is the correct computation, but there are tons of cases where LCD can be overridden. - // For now we just regenerate if any run in a textblob has LCD. - // TODO figure out where all of these overrides are and see if we can incorporate that logic - // at a higher level *OR* use sRGB - SkASSERT(false); - //canonicalColor = SkMaskGamma::CanonicalColor(canonicalColor); - } else { - // A8, though can have mixed BMP text but it shouldn't matter because BMP text won't have - // gamma corrected masks anyways, nor color - U8CPU lum = SkComputeLuminance(SkColorGetR(canonicalColor), - SkColorGetG(canonicalColor), - SkColorGetB(canonicalColor)); - // reduce to our finite number of bits - canonicalColor = SkMaskGamma::CanonicalColor(SkColorSetRGB(lum, lum, lum)); - } - return canonicalColor; -} - -// TODO if this function ever shows up in profiling, then we can compute this value when the -// textblob is being built and cache it. However, for the time being textblobs mostly only have 1 -// run so this is not a big deal to compute here. -bool GrAtlasTextContext::HasLCD(const SkTextBlob* blob) { - SkTextBlobRunIterator it(blob); - for (; !it.done(); it.next()) { - if (it.isLCD()) { - return true; - } - } - return false; -} - -bool GrAtlasTextContext::MustRegenerateBlob(SkScalar* outTransX, SkScalar* outTransY, - const GrAtlasTextBlob& blob, const SkPaint& paint, - GrColor color, const SkMaskFilter::BlurRec& blurRec, - const SkMatrix& viewMatrix, SkScalar x, SkScalar y) { - // If we have LCD text then our canonical color will be set to transparent, in this case we have - // to regenerate the blob on any color change - // We use the grPaint to get any color filter effects - if (blob.fKey.fCanonicalColor == SK_ColorTRANSPARENT && - blob.fPaintColor != color) { - return true; - } - - if (blob.fViewMatrix.hasPerspective() != viewMatrix.hasPerspective()) { - return true; - } - - if (blob.fViewMatrix.hasPerspective() && !blob.fViewMatrix.cheapEqualTo(viewMatrix)) { - return true; - } - - // We only cache one masked version - if (blob.fKey.fHasBlur && - (blob.fBlurRec.fSigma != blurRec.fSigma || - blob.fBlurRec.fStyle != blurRec.fStyle || - blob.fBlurRec.fQuality != blurRec.fQuality)) { - return true; - } - - // Similarly, we only cache one version for each style - if (blob.fKey.fStyle != SkPaint::kFill_Style && - (blob.fStrokeInfo.fFrameWidth != paint.getStrokeWidth() || - blob.fStrokeInfo.fMiterLimit != paint.getStrokeMiter() || - blob.fStrokeInfo.fJoin != paint.getStrokeJoin())) { - return true; - } - - // Mixed blobs must be regenerated. We could probably figure out a way to do integer scrolls - // for mixed blobs if this becomes an issue. - if (blob.hasBitmap() && blob.hasDistanceField()) { - // Identical viewmatrices and we can reuse in all cases - if (blob.fViewMatrix.cheapEqualTo(viewMatrix) && x == blob.fX && y == blob.fY) { - return false; - } - return true; - } - - if (blob.hasBitmap()) { - if (blob.fViewMatrix.getScaleX() != viewMatrix.getScaleX() || - blob.fViewMatrix.getScaleY() != viewMatrix.getScaleY() || - blob.fViewMatrix.getSkewX() != viewMatrix.getSkewX() || - blob.fViewMatrix.getSkewY() != viewMatrix.getSkewY()) { - return true; - } - - // We can update the positions in the cachedtextblobs without regenerating the whole blob, - // but only for integer translations. - // This cool bit of math will determine the necessary translation to apply to the already - // generated vertex coordinates to move them to the correct position - SkScalar transX = viewMatrix.getTranslateX() + - viewMatrix.getScaleX() * (x - blob.fX) + - viewMatrix.getSkewX() * (y - blob.fY) - - blob.fViewMatrix.getTranslateX(); - SkScalar transY = viewMatrix.getTranslateY() + - viewMatrix.getSkewY() * (x - blob.fX) + - viewMatrix.getScaleY() * (y - blob.fY) - - blob.fViewMatrix.getTranslateY(); - if (!SkScalarIsInt(transX) || !SkScalarIsInt(transY) ) { - return true; - } - - (*outTransX) = transX; - (*outTransY) = transY; - } else if (blob.hasDistanceField()) { - // A scale outside of [blob.fMaxMinScale, blob.fMinMaxScale] would result in a different - // distance field being generated, so we have to regenerate in those cases - SkScalar newMaxScale = viewMatrix.getMaxScale(); - SkScalar oldMaxScale = blob.fViewMatrix.getMaxScale(); - SkScalar scaleAdjust = newMaxScale / oldMaxScale; - if (scaleAdjust < blob.fMaxMinScale || scaleAdjust > blob.fMinMaxScale) { - return true; - } - - (*outTransX) = x - blob.fX; - (*outTransY) = y - blob.fY; - } - - // It is possible that a blob has neither distanceField nor bitmaptext. This is in the case - // when all of the runs inside the blob are drawn as paths. In this case, we always regenerate - // the blob anyways at flush time, so no need to regenerate explicitly - return false; -} - - -inline SkGlyphCache* GrAtlasTextContext::setupCache(GrAtlasTextBlob::Run* run, - const SkPaint& skPaint, - const SkMatrix* viewMatrix, - bool noGamma) { - skPaint.getScalerContextDescriptor(&run->fDescriptor, fSurfaceProps, viewMatrix, noGamma); - run->fTypeface.reset(SkSafeRef(skPaint.getTypeface())); - return SkGlyphCache::DetachCache(run->fTypeface, run->fDescriptor.getDesc()); -} - -void GrAtlasTextContext::drawTextBlob(GrDrawContext* dc, GrRenderTarget* rt, - const GrClip& clip, const SkPaint& skPaint, - const SkMatrix& viewMatrix, const SkTextBlob* blob, - SkScalar x, SkScalar y, - SkDrawFilter* drawFilter, const SkIRect& clipBounds) { - // If we have been abandoned, then don't draw - if (fContext->abandoned()) { - return; - } - - SkAutoTUnref cacheBlob; - SkMaskFilter::BlurRec blurRec; - GrAtlasTextBlob::Key key; - // It might be worth caching these things, but its not clear at this time - // TODO for animated mask filters, this will fill up our cache. We need a safeguard here - const SkMaskFilter* mf = skPaint.getMaskFilter(); - bool canCache = !(skPaint.getPathEffect() || - (mf && !mf->asABlur(&blurRec)) || - drawFilter); - - if (canCache) { - bool hasLCD = HasLCD(blob); - - // We canonicalize all non-lcd draws to use kUnknown_SkPixelGeometry - SkPixelGeometry pixelGeometry = hasLCD ? fSurfaceProps.pixelGeometry() : - kUnknown_SkPixelGeometry; - - // TODO we want to figure out a way to be able to use the canonical color on LCD text, - // see the note on ComputeCanonicalColor above. We pick a dummy value for LCD text to - // ensure we always match the same key - GrColor canonicalColor = hasLCD ? SK_ColorTRANSPARENT : - ComputeCanonicalColor(skPaint, hasLCD); - - key.fPixelGeometry = pixelGeometry; - key.fUniqueID = blob->uniqueID(); - key.fStyle = skPaint.getStyle(); - key.fHasBlur = SkToBool(mf); - key.fCanonicalColor = canonicalColor; - cacheBlob.reset(SkSafeRef(fCache->find(key))); - } - - SkScalar transX = 0.f; - SkScalar transY = 0.f; - - // Though for the time being runs in the textblob can override the paint, they only touch font - // info. - GrPaint grPaint; - if (!SkPaintToGrPaint(fContext, skPaint, viewMatrix, &grPaint)) { - return; - } - - if (cacheBlob) { - if (MustRegenerateBlob(&transX, &transY, *cacheBlob, skPaint, grPaint.getColor(), blurRec, - viewMatrix, x, y)) { - // We have to remake the blob because changes may invalidate our masks. - // TODO we could probably get away reuse most of the time if the pointer is unique, - // but we'd have to clear the subrun information - fCache->remove(cacheBlob); - cacheBlob.reset(SkRef(fCache->createCachedBlob(blob, key, blurRec, skPaint, - GrAtlasTextBatch::kGrayTextVASize))); - this->regenerateTextBlob(cacheBlob, skPaint, grPaint.getColor(), viewMatrix, - blob, x, y, drawFilter, clip); - } else { - // If we can reuse the blob, then make sure we update the blob's viewmatrix, and x/y - // offsets. Note, we offset the vertex bounds right before flushing - cacheBlob->fViewMatrix = viewMatrix; - cacheBlob->fX = x; - cacheBlob->fY = y; - fCache->makeMRU(cacheBlob); -#ifdef CACHE_SANITY_CHECK - { - int glyphCount = 0; - int runCount = 0; - GrTextBlobCache::BlobGlyphCount(&glyphCount, &runCount, blob); - SkAutoTUnref sanityBlob(fCache->createBlob(glyphCount, runCount, - kGrayTextVASize)); - GrTextBlobCache::SetupCacheBlobKey(sanityBlob, key, blurRec, skPaint); - this->regenerateTextBlob(sanityBlob, skPaint, grPaint.getColor(), viewMatrix, - blob, x, y, drawFilter, clip); - GrAtlasTextBlob::AssertEqual(*sanityBlob, *cacheBlob); - } - -#endif - } - } else { - if (canCache) { - cacheBlob.reset(SkRef(fCache->createCachedBlob(blob, key, blurRec, skPaint, - GrAtlasTextBatch::kGrayTextVASize))); - } else { - cacheBlob.reset(fCache->createBlob(blob, GrAtlasTextBatch::kGrayTextVASize)); - } - this->regenerateTextBlob(cacheBlob, skPaint, grPaint.getColor(), viewMatrix, - blob, x, y, drawFilter, clip); - } - - this->flush(blob, cacheBlob, dc, rt, skPaint, grPaint, drawFilter, - clip, viewMatrix, clipBounds, x, y, transX, transY); -} - -inline bool GrAtlasTextContext::canDrawAsDistanceFields(const SkPaint& skPaint, - const SkMatrix& viewMatrix) { - // TODO: support perspective (need getMaxScale replacement) - if (viewMatrix.hasPerspective()) { - return false; - } - - SkScalar maxScale = viewMatrix.getMaxScale(); - SkScalar scaledTextSize = maxScale*skPaint.getTextSize(); - // Hinted text looks far better at small resolutions - // Scaling up beyond 2x yields undesireable artifacts - if (scaledTextSize < kMinDFFontSize || scaledTextSize > kLargeDFFontLimit) { - return false; - } - - bool useDFT = fSurfaceProps.isUseDeviceIndependentFonts(); -#if SK_FORCE_DISTANCE_FIELD_TEXT - useDFT = true; -#endif - - if (!useDFT && scaledTextSize < kLargeDFFontSize) { - return false; - } - - // rasterizers and mask filters modify alpha, which doesn't - // translate well to distance - if (skPaint.getRasterizer() || skPaint.getMaskFilter() || - !fContext->caps()->shaderCaps()->shaderDerivativeSupport()) { - return false; - } - - // TODO: add some stroking support - if (skPaint.getStyle() != SkPaint::kFill_Style) { - return false; - } - - return true; -} - -void GrAtlasTextContext::regenerateTextBlob(GrAtlasTextBlob* cacheBlob, - const SkPaint& skPaint, GrColor color, - const SkMatrix& viewMatrix, - const SkTextBlob* blob, SkScalar x, SkScalar y, - SkDrawFilter* drawFilter, - const GrClip& clip) { - // The color here is the GrPaint color, and it is used to determine whether we - // have to regenerate LCD text blobs. - // We use this color vs the SkPaint color because it has the colorfilter applied. - cacheBlob->fPaintColor = color; - cacheBlob->fViewMatrix = viewMatrix; - cacheBlob->fX = x; - cacheBlob->fY = y; - - // Regenerate textblob - SkPaint runPaint = skPaint; - SkTextBlobRunIterator it(blob); - for (int run = 0; !it.done(); it.next(), run++) { - int glyphCount = it.glyphCount(); - size_t textLen = glyphCount * sizeof(uint16_t); - const SkPoint& offset = it.offset(); - // applyFontToPaint() always overwrites the exact same attributes, - // so it is safe to not re-seed the paint for this reason. - it.applyFontToPaint(&runPaint); - - if (drawFilter && !drawFilter->filter(&runPaint, SkDrawFilter::kText_Type)) { - // A false return from filter() means we should abort the current draw. - runPaint = skPaint; - continue; - } - - runPaint.setFlags(FilterTextFlags(fSurfaceProps, runPaint)); - - // setup vertex / glyphIndex for the new run - if (run > 0) { - PerSubRunInfo& newRun = cacheBlob->fRuns[run].fSubRunInfo.back(); - PerSubRunInfo& lastRun = cacheBlob->fRuns[run - 1].fSubRunInfo.back(); - - newRun.fVertexStartIndex = lastRun.fVertexEndIndex; - newRun.fVertexEndIndex = lastRun.fVertexEndIndex; - - newRun.fGlyphStartIndex = lastRun.fGlyphEndIndex; - newRun.fGlyphEndIndex = lastRun.fGlyphEndIndex; - } - - if (this->canDrawAsDistanceFields(runPaint, viewMatrix)) { - cacheBlob->setHasDistanceField(); - SkPaint dfPaint = runPaint; - SkScalar textRatio; - this->initDistanceFieldPaint(cacheBlob, &dfPaint, &textRatio, viewMatrix); - Run& runIdx = cacheBlob->fRuns[run]; - PerSubRunInfo& subRun = runIdx.fSubRunInfo.back(); - subRun.fUseLCDText = runPaint.isLCDRenderText(); - subRun.fDrawAsDistanceFields = true; - - SkTDArray fallbackTxt; - SkTDArray fallbackPos; - SkPoint dfOffset; - int scalarsPerPosition = 2; - switch (it.positioning()) { - case SkTextBlob::kDefault_Positioning: { - this->internalDrawDFText(cacheBlob, run, dfPaint, color, viewMatrix, - (const char *)it.glyphs(), textLen, - x + offset.x(), y + offset.y(), textRatio, - &fallbackTxt, &fallbackPos, &dfOffset, runPaint); - break; - } - case SkTextBlob::kHorizontal_Positioning: { - scalarsPerPosition = 1; - dfOffset = SkPoint::Make(x, y + offset.y()); - this->internalDrawDFPosText(cacheBlob, run, dfPaint, color, viewMatrix, - (const char*)it.glyphs(), textLen, it.pos(), - scalarsPerPosition, dfOffset, textRatio, - &fallbackTxt, &fallbackPos); - break; - } - case SkTextBlob::kFull_Positioning: { - dfOffset = SkPoint::Make(x, y); - this->internalDrawDFPosText(cacheBlob, run, dfPaint, color, viewMatrix, - (const char*)it.glyphs(), textLen, it.pos(), - scalarsPerPosition, dfOffset, textRatio, - &fallbackTxt, &fallbackPos); - break; - } - } - if (fallbackTxt.count()) { - this->fallbackDrawPosText(cacheBlob, run, clip, color, runPaint, viewMatrix, - fallbackTxt, fallbackPos, scalarsPerPosition, dfOffset); - } - } else if (SkDraw::ShouldDrawTextAsPaths(runPaint, viewMatrix)) { - cacheBlob->fRuns[run].fDrawAsPaths = true; - } else { - cacheBlob->setHasBitmap(); - SkGlyphCache* cache = this->setupCache(&cacheBlob->fRuns[run], runPaint, &viewMatrix, - false); - switch (it.positioning()) { - case SkTextBlob::kDefault_Positioning: - this->internalDrawBMPText(cacheBlob, run, cache, runPaint, color, viewMatrix, - (const char *)it.glyphs(), textLen, - x + offset.x(), y + offset.y()); - break; - case SkTextBlob::kHorizontal_Positioning: - this->internalDrawBMPPosText(cacheBlob, run, cache, runPaint, color, viewMatrix, - (const char*)it.glyphs(), textLen, it.pos(), 1, - SkPoint::Make(x, y + offset.y())); - break; - case SkTextBlob::kFull_Positioning: - this->internalDrawBMPPosText(cacheBlob, run, cache, runPaint, color, viewMatrix, - (const char*)it.glyphs(), textLen, it.pos(), 2, - SkPoint::Make(x, y)); - break; - } - SkGlyphCache::AttachCache(cache); - } - - if (drawFilter) { - // A draw filter may change the paint arbitrarily, so we must re-seed in this case. - runPaint = skPaint; - } - } -} - -inline void GrAtlasTextContext::initDistanceFieldPaint(GrAtlasTextBlob* blob, - SkPaint* skPaint, - SkScalar* textRatio, - const SkMatrix& viewMatrix) { - // getMaxScale doesn't support perspective, so neither do we at the moment - SkASSERT(!viewMatrix.hasPerspective()); - SkScalar maxScale = viewMatrix.getMaxScale(); - SkScalar textSize = skPaint->getTextSize(); - SkScalar scaledTextSize = textSize; - // if we have non-unity scale, we need to choose our base text size - // based on the SkPaint's text size multiplied by the max scale factor - // TODO: do we need to do this if we're scaling down (i.e. maxScale < 1)? - if (maxScale > 0 && !SkScalarNearlyEqual(maxScale, SK_Scalar1)) { - scaledTextSize *= maxScale; - } - - // We have three sizes of distance field text, and within each size 'bucket' there is a floor - // and ceiling. A scale outside of this range would require regenerating the distance fields - SkScalar dfMaskScaleFloor; - SkScalar dfMaskScaleCeil; - if (scaledTextSize <= kSmallDFFontLimit) { - dfMaskScaleFloor = kMinDFFontSize; - dfMaskScaleCeil = kSmallDFFontLimit; - *textRatio = textSize / kSmallDFFontSize; - skPaint->setTextSize(SkIntToScalar(kSmallDFFontSize)); - } else if (scaledTextSize <= kMediumDFFontLimit) { - dfMaskScaleFloor = kSmallDFFontLimit; - dfMaskScaleCeil = kMediumDFFontLimit; - *textRatio = textSize / kMediumDFFontSize; - skPaint->setTextSize(SkIntToScalar(kMediumDFFontSize)); - } else { - dfMaskScaleFloor = kMediumDFFontLimit; - dfMaskScaleCeil = kLargeDFFontLimit; - *textRatio = textSize / kLargeDFFontSize; - skPaint->setTextSize(SkIntToScalar(kLargeDFFontSize)); - } - - // Because there can be multiple runs in the blob, we want the overall maxMinScale, and - // minMaxScale to make regeneration decisions. Specifically, we want the maximum minimum scale - // we can tolerate before we'd drop to a lower mip size, and the minimum maximum scale we can - // tolerate before we'd have to move to a large mip size. When we actually test these values - // we look at the delta in scale between the new viewmatrix and the old viewmatrix, and test - // against these values to decide if we can reuse or not(ie, will a given scale change our mip - // level) - SkASSERT(dfMaskScaleFloor <= scaledTextSize && scaledTextSize <= dfMaskScaleCeil); - blob->fMaxMinScale = SkMaxScalar(dfMaskScaleFloor / scaledTextSize, blob->fMaxMinScale); - blob->fMinMaxScale = SkMinScalar(dfMaskScaleCeil / scaledTextSize, blob->fMinMaxScale); - - skPaint->setLCDRenderText(false); - skPaint->setAutohinted(false); - skPaint->setHinting(SkPaint::kNormal_Hinting); - skPaint->setSubpixelText(true); -} - -inline void GrAtlasTextContext::fallbackDrawPosText(GrAtlasTextBlob* blob, - int runIndex, - const GrClip& clip, - GrColor color, - const SkPaint& skPaint, - const SkMatrix& viewMatrix, - const SkTDArray& fallbackTxt, - const SkTDArray& fallbackPos, - int scalarsPerPosition, - const SkPoint& offset) { - SkASSERT(fallbackTxt.count()); - blob->setHasBitmap(); - Run& run = blob->fRuns[runIndex]; - // Push back a new subrun to fill and set the override descriptor - run.push_back(); - run.fOverrideDescriptor.reset(new SkAutoDescriptor); - skPaint.getScalerContextDescriptor(run.fOverrideDescriptor, - fSurfaceProps, &viewMatrix, false); - SkGlyphCache* cache = SkGlyphCache::DetachCache(run.fTypeface, - run.fOverrideDescriptor->getDesc()); - this->internalDrawBMPPosText(blob, runIndex, cache, skPaint, color, viewMatrix, - fallbackTxt.begin(), fallbackTxt.count(), - fallbackPos.begin(), scalarsPerPosition, offset); - SkGlyphCache::AttachCache(cache); -} - -inline GrAtlasTextBlob* -GrAtlasTextContext::setupDFBlob(int glyphCount, const SkPaint& origPaint, - const SkMatrix& viewMatrix, SkPaint* dfPaint, - SkScalar* textRatio) { - GrAtlasTextBlob* blob = fCache->createBlob(glyphCount, 1, GrAtlasTextBatch::kGrayTextVASize); - - *dfPaint = origPaint; - this->initDistanceFieldPaint(blob, dfPaint, textRatio, viewMatrix); - blob->fViewMatrix = viewMatrix; - Run& run = blob->fRuns[0]; - PerSubRunInfo& subRun = run.fSubRunInfo.back(); - subRun.fUseLCDText = origPaint.isLCDRenderText(); - subRun.fDrawAsDistanceFields = true; - - return blob; -} - -inline GrAtlasTextBlob* -GrAtlasTextContext::createDrawTextBlob(const GrClip& clip, - const GrPaint& paint, const SkPaint& skPaint, - const SkMatrix& viewMatrix, - const char text[], size_t byteLength, - SkScalar x, SkScalar y, const SkIRect& regionClipBounds) { - int glyphCount = skPaint.countText(text, byteLength); - - GrAtlasTextBlob* blob; - if (this->canDrawAsDistanceFields(skPaint, viewMatrix)) { - SkPaint dfPaint; - SkScalar textRatio; - blob = this->setupDFBlob(glyphCount, skPaint, viewMatrix, &dfPaint, &textRatio); - - SkTDArray fallbackTxt; - SkTDArray fallbackPos; - SkPoint offset; - this->internalDrawDFText(blob, 0, dfPaint, paint.getColor(), viewMatrix, text, - byteLength, x, y, textRatio, &fallbackTxt, &fallbackPos, - &offset, skPaint); - if (fallbackTxt.count()) { - this->fallbackDrawPosText(blob, 0, clip, paint.getColor(), skPaint, viewMatrix, - fallbackTxt, fallbackPos, 2, offset); - } - } else { - blob = fCache->createBlob(glyphCount, 1, GrAtlasTextBatch::kGrayTextVASize); - blob->fViewMatrix = viewMatrix; - - SkGlyphCache* cache = this->setupCache(&blob->fRuns[0], skPaint, &viewMatrix, false); - this->internalDrawBMPText(blob, 0, cache, skPaint, paint.getColor(), viewMatrix, text, - byteLength, x, y); - SkGlyphCache::AttachCache(cache); - } - return blob; -} - -inline GrAtlasTextBlob* -GrAtlasTextContext::createDrawPosTextBlob(const GrClip& clip, - const GrPaint& paint, const SkPaint& skPaint, - const SkMatrix& viewMatrix, - const char text[], size_t byteLength, - const SkScalar pos[], int scalarsPerPosition, - const SkPoint& offset, const SkIRect& regionClipBounds) { - int glyphCount = skPaint.countText(text, byteLength); - - GrAtlasTextBlob* blob; - if (this->canDrawAsDistanceFields(skPaint, viewMatrix)) { - SkPaint dfPaint; - SkScalar textRatio; - blob = this->setupDFBlob(glyphCount, skPaint, viewMatrix, &dfPaint, &textRatio); - - SkTDArray fallbackTxt; - SkTDArray fallbackPos; - this->internalDrawDFPosText(blob, 0, dfPaint, paint.getColor(), viewMatrix, text, - byteLength, pos, scalarsPerPosition, offset, - textRatio, &fallbackTxt, &fallbackPos); - if (fallbackTxt.count()) { - this->fallbackDrawPosText(blob, 0, clip, paint.getColor(), skPaint, viewMatrix, - fallbackTxt, fallbackPos, scalarsPerPosition, offset); - } - } else { - blob = fCache->createBlob(glyphCount, 1, GrAtlasTextBatch::kGrayTextVASize); - blob->fViewMatrix = viewMatrix; - SkGlyphCache* cache = this->setupCache(&blob->fRuns[0], skPaint, &viewMatrix, false); - this->internalDrawBMPPosText(blob, 0, cache, skPaint, paint.getColor(), viewMatrix, text, - byteLength, pos, scalarsPerPosition, offset); - SkGlyphCache::AttachCache(cache); - } - return blob; -} - -void GrAtlasTextContext::onDrawText(GrDrawContext* dc, GrRenderTarget* rt, - const GrClip& clip, - const GrPaint& paint, const SkPaint& skPaint, - const SkMatrix& viewMatrix, - const char text[], size_t byteLength, - SkScalar x, SkScalar y, const SkIRect& regionClipBounds) { - SkAutoTUnref blob( - this->createDrawTextBlob(clip, paint, skPaint, viewMatrix, - text, byteLength, x, y, regionClipBounds)); - this->flush(blob, dc, rt, skPaint, paint, clip, regionClipBounds); -} - -void GrAtlasTextContext::onDrawPosText(GrDrawContext* dc, GrRenderTarget* rt, - const GrClip& clip, - const GrPaint& paint, const SkPaint& skPaint, - const SkMatrix& viewMatrix, - const char text[], size_t byteLength, - const SkScalar pos[], int scalarsPerPosition, - const SkPoint& offset, const SkIRect& regionClipBounds) { - SkAutoTUnref blob( - this->createDrawPosTextBlob(clip, paint, skPaint, viewMatrix, - text, byteLength, - pos, scalarsPerPosition, - offset, regionClipBounds)); - - this->flush(blob, dc, rt, skPaint, paint, clip, regionClipBounds); -} - -void GrAtlasTextContext::internalDrawBMPText(GrAtlasTextBlob* blob, int runIndex, - SkGlyphCache* cache, const SkPaint& skPaint, - GrColor color, - const SkMatrix& viewMatrix, - const char text[], size_t byteLength, - SkScalar x, SkScalar y) { - SkASSERT(byteLength == 0 || text != nullptr); - - // nothing to draw - if (text == nullptr || byteLength == 0) { - return; - } - - fCurrStrike = nullptr; - - // Get GrFontScaler from cache - GrFontScaler* fontScaler = GetGrFontScaler(cache); - - SkFindAndPlaceGlyph::ProcessText( - skPaint.getTextEncoding(), text, byteLength, - {x, y}, viewMatrix, skPaint.getTextAlign(), - cache, - [&](const SkGlyph& glyph, SkPoint position, SkPoint rounding) { - position += rounding; - this->bmpAppendGlyph( - blob, runIndex, glyph, - SkScalarFloorToInt(position.fX), SkScalarFloorToInt(position.fY), - color, fontScaler); - } - ); -} - -void GrAtlasTextContext::internalDrawBMPPosText(GrAtlasTextBlob* blob, int runIndex, - SkGlyphCache* cache, const SkPaint& skPaint, - GrColor color, - const SkMatrix& viewMatrix, - const char text[], size_t byteLength, - const SkScalar pos[], int scalarsPerPosition, - const SkPoint& offset) { - SkASSERT(byteLength == 0 || text != nullptr); - SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition); - - // nothing to draw - if (text == nullptr || byteLength == 0) { - return; - } - - fCurrStrike = nullptr; - - // Get GrFontScaler from cache - GrFontScaler* fontScaler = GetGrFontScaler(cache); - - SkFindAndPlaceGlyph::ProcessPosText( - skPaint.getTextEncoding(), text, byteLength, - offset, viewMatrix, pos, scalarsPerPosition, - skPaint.getTextAlign(), cache, - [&](const SkGlyph& glyph, SkPoint position, SkPoint rounding) { - position += rounding; - this->bmpAppendGlyph( - blob, runIndex, glyph, - SkScalarFloorToInt(position.fX), SkScalarFloorToInt(position.fY), - color, fontScaler); - } - ); -} - -void GrAtlasTextContext::internalDrawDFText(GrAtlasTextBlob* blob, int runIndex, - const SkPaint& skPaint, GrColor color, - const SkMatrix& viewMatrix, - const char text[], size_t byteLength, - SkScalar x, SkScalar y, - SkScalar textRatio, - SkTDArray* fallbackTxt, - SkTDArray* fallbackPos, - SkPoint* offset, - const SkPaint& origPaint) { - SkASSERT(byteLength == 0 || text != nullptr); - - // nothing to draw - if (text == nullptr || byteLength == 0) { - return; - } - - SkDrawCacheProc glyphCacheProc = origPaint.getDrawCacheProc(); - SkAutoDescriptor desc; - origPaint.getScalerContextDescriptor(&desc, fSurfaceProps, nullptr, true); - SkGlyphCache* origPaintCache = SkGlyphCache::DetachCache(origPaint.getTypeface(), - desc.getDesc()); - - SkTArray positions; - - const char* textPtr = text; - SkFixed stopX = 0; - SkFixed stopY = 0; - SkFixed origin = 0; - switch (origPaint.getTextAlign()) { - case SkPaint::kRight_Align: origin = SK_Fixed1; break; - case SkPaint::kCenter_Align: origin = SK_FixedHalf; break; - case SkPaint::kLeft_Align: origin = 0; break; - } - - SkAutoKern autokern; - const char* stop = text + byteLength; - while (textPtr < stop) { - // don't need x, y here, since all subpixel variants will have the - // same advance - const SkGlyph& glyph = glyphCacheProc(origPaintCache, &textPtr, 0, 0); - - SkFixed width = glyph.fAdvanceX + autokern.adjust(glyph); - positions.push_back(SkFixedToScalar(stopX + SkFixedMul(origin, width))); - - SkFixed height = glyph.fAdvanceY; - positions.push_back(SkFixedToScalar(stopY + SkFixedMul(origin, height))); - - stopX += width; - stopY += height; - } - SkASSERT(textPtr == stop); - - SkGlyphCache::AttachCache(origPaintCache); - - // now adjust starting point depending on alignment - SkScalar alignX = SkFixedToScalar(stopX); - SkScalar alignY = SkFixedToScalar(stopY); - if (origPaint.getTextAlign() == SkPaint::kCenter_Align) { - alignX = SkScalarHalf(alignX); - alignY = SkScalarHalf(alignY); - } else if (origPaint.getTextAlign() == SkPaint::kLeft_Align) { - alignX = 0; - alignY = 0; - } - x -= alignX; - y -= alignY; - *offset = SkPoint::Make(x, y); - - this->internalDrawDFPosText(blob, runIndex, skPaint, color, viewMatrix, text, byteLength, - positions.begin(), 2, *offset, textRatio, fallbackTxt, - fallbackPos); -} - -void GrAtlasTextContext::internalDrawDFPosText(GrAtlasTextBlob* blob, int runIndex, - const SkPaint& skPaint, GrColor color, - const SkMatrix& viewMatrix, - const char text[], size_t byteLength, - const SkScalar pos[], int scalarsPerPosition, - const SkPoint& offset, - SkScalar textRatio, - SkTDArray* fallbackTxt, - SkTDArray* fallbackPos) { - - SkASSERT(byteLength == 0 || text != nullptr); - SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition); - - // nothing to draw - if (text == nullptr || byteLength == 0) { - return; - } - - fCurrStrike = nullptr; - - SkDrawCacheProc glyphCacheProc = skPaint.getDrawCacheProc(); - SkGlyphCache* cache = this->setupCache(&blob->fRuns[runIndex], skPaint, nullptr, true); - GrFontScaler* fontScaler = GetGrFontScaler(cache); - - const char* stop = text + byteLength; - - if (SkPaint::kLeft_Align == skPaint.getTextAlign()) { - while (text < stop) { - const char* lastText = text; - // the last 2 parameters are ignored - const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0); - - if (glyph.fWidth) { - SkScalar x = offset.x() + pos[0]; - SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0); - - if (!this->dfAppendGlyph(blob, - runIndex, - glyph, - x, y, color, fontScaler, - textRatio, viewMatrix)) { - // couldn't append, send to fallback - fallbackTxt->append(SkToInt(text-lastText), lastText); - *fallbackPos->append() = pos[0]; - if (2 == scalarsPerPosition) { - *fallbackPos->append() = pos[1]; - } - } - } - pos += scalarsPerPosition; - } - } else { - SkScalar alignMul = SkPaint::kCenter_Align == skPaint.getTextAlign() ? SK_ScalarHalf - : SK_Scalar1; - while (text < stop) { - const char* lastText = text; - // the last 2 parameters are ignored - const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0); - - if (glyph.fWidth) { - SkScalar x = offset.x() + pos[0]; - SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0); - - SkScalar advanceX = SkFixedToScalar(glyph.fAdvanceX) * alignMul * textRatio; - SkScalar advanceY = SkFixedToScalar(glyph.fAdvanceY) * alignMul * textRatio; - - if (!this->dfAppendGlyph(blob, - runIndex, - glyph, - x - advanceX, y - advanceY, color, - fontScaler, - textRatio, - viewMatrix)) { - // couldn't append, send to fallback - fallbackTxt->append(SkToInt(text-lastText), lastText); - *fallbackPos->append() = pos[0]; - if (2 == scalarsPerPosition) { - *fallbackPos->append() = pos[1]; - } - } - } - pos += scalarsPerPosition; - } - } - - SkGlyphCache::AttachCache(cache); -} - -void GrAtlasTextContext::bmpAppendGlyph(GrAtlasTextBlob* blob, int runIndex, - const SkGlyph& skGlyph, - int vx, int vy, GrColor color, GrFontScaler* scaler) { - Run& run = blob->fRuns[runIndex]; - if (!fCurrStrike) { - fCurrStrike = fContext->getBatchFontCache()->getStrike(scaler); - } - - GrGlyph::PackedID id = GrGlyph::Pack(skGlyph.getGlyphID(), - skGlyph.getSubXFixed(), - skGlyph.getSubYFixed(), - GrGlyph::kCoverage_MaskStyle); - GrGlyph* glyph = fCurrStrike->getGlyph(skGlyph, id, scaler); - if (!glyph) { - return; - } - - int x = vx + glyph->fBounds.fLeft; - int y = vy + glyph->fBounds.fTop; - - // keep them as ints until we've done the clip-test - int width = glyph->fBounds.width(); - int height = glyph->fBounds.height(); - - // If the glyph is too large we fall back to paths - if (glyph->fTooLargeForAtlas) { - this->appendGlyphPath(blob, glyph, scaler, skGlyph, SkIntToScalar(vx), SkIntToScalar(vy)); - return; - } - - GrMaskFormat format = glyph->fMaskFormat; - - PerSubRunInfo* subRun = &run.fSubRunInfo.back(); - if (run.fInitialized && subRun->fMaskFormat != format) { - subRun = &run.push_back(); - subRun->fStrike.reset(SkRef(fCurrStrike)); - } else if (!run.fInitialized) { - subRun->fStrike.reset(SkRef(fCurrStrike)); - } - - run.fInitialized = true; - - size_t vertexStride = GrAtlasTextBatch::GetVertexStride(format); - - SkRect r; - r.fLeft = SkIntToScalar(x); - r.fTop = SkIntToScalar(y); - r.fRight = r.fLeft + SkIntToScalar(width); - r.fBottom = r.fTop + SkIntToScalar(height); - subRun->fMaskFormat = format; - this->appendGlyphCommon(blob, &run, subRun, r, color, vertexStride, kA8_GrMaskFormat == format, - glyph); -} - -bool GrAtlasTextContext::dfAppendGlyph(GrAtlasTextBlob* blob, int runIndex, - const SkGlyph& skGlyph, - SkScalar sx, SkScalar sy, GrColor color, - GrFontScaler* scaler, - SkScalar textRatio, const SkMatrix& viewMatrix) { - Run& run = blob->fRuns[runIndex]; - if (!fCurrStrike) { - fCurrStrike = fContext->getBatchFontCache()->getStrike(scaler); - } - - GrGlyph::PackedID id = GrGlyph::Pack(skGlyph.getGlyphID(), - skGlyph.getSubXFixed(), - skGlyph.getSubYFixed(), - GrGlyph::kDistance_MaskStyle); - GrGlyph* glyph = fCurrStrike->getGlyph(skGlyph, id, scaler); - if (!glyph) { - return true; - } - - // fallback to color glyph support - if (kA8_GrMaskFormat != glyph->fMaskFormat) { - return false; - } - - SkScalar dx = SkIntToScalar(glyph->fBounds.fLeft + SK_DistanceFieldInset); - SkScalar dy = SkIntToScalar(glyph->fBounds.fTop + SK_DistanceFieldInset); - SkScalar width = SkIntToScalar(glyph->fBounds.width() - 2 * SK_DistanceFieldInset); - SkScalar height = SkIntToScalar(glyph->fBounds.height() - 2 * SK_DistanceFieldInset); - - SkScalar scale = textRatio; - dx *= scale; - dy *= scale; - width *= scale; - height *= scale; - sx += dx; - sy += dy; - SkRect glyphRect = SkRect::MakeXYWH(sx, sy, width, height); - - // TODO combine with the above - // If the glyph is too large we fall back to paths - if (glyph->fTooLargeForAtlas) { - this->appendGlyphPath(blob, glyph, scaler, skGlyph, sx - dx, sy - dy, scale, true); - return true; - } - - PerSubRunInfo* subRun = &run.fSubRunInfo.back(); - if (!run.fInitialized) { - subRun->fStrike.reset(SkRef(fCurrStrike)); - } - run.fInitialized = true; - SkASSERT(glyph->fMaskFormat == kA8_GrMaskFormat); - subRun->fMaskFormat = kA8_GrMaskFormat; - - size_t vertexStride = GrAtlasTextBatch::GetVertexStrideDf(kA8_GrMaskFormat, - subRun->fUseLCDText); - - bool useColorVerts = !subRun->fUseLCDText; - this->appendGlyphCommon(blob, &run, subRun, glyphRect, color, vertexStride, useColorVerts, - glyph); - return true; -} - -inline void GrAtlasTextContext::appendGlyphPath(GrAtlasTextBlob* blob, GrGlyph* glyph, - GrFontScaler* scaler, const SkGlyph& skGlyph, - SkScalar x, SkScalar y, SkScalar scale, - bool applyVM) { - if (nullptr == glyph->fPath) { - const SkPath* glyphPath = scaler->getGlyphPath(skGlyph); - if (!glyphPath) { - return; - } - - glyph->fPath = new SkPath(*glyphPath); - } - blob->fBigGlyphs.push_back(GrAtlasTextBlob::BigGlyph(*glyph->fPath, x, y, scale, applyVM)); -} - -inline void GrAtlasTextContext::appendGlyphCommon(GrAtlasTextBlob* blob, Run* run, - Run::SubRunInfo* subRun, - const SkRect& positions, GrColor color, - size_t vertexStride, bool useVertexColor, - GrGlyph* glyph) { - blob->fGlyphs[subRun->fGlyphEndIndex] = glyph; - run->fVertexBounds.joinNonEmptyArg(positions); - run->fColor = color; - - intptr_t vertex = reinterpret_cast(blob->fVertices + subRun->fVertexEndIndex); - - if (useVertexColor) { - // V0 - SkPoint* position = reinterpret_cast(vertex); - position->set(positions.fLeft, positions.fTop); - SkColor* colorPtr = reinterpret_cast(vertex + sizeof(SkPoint)); - *colorPtr = color; - vertex += vertexStride; - - // V1 - position = reinterpret_cast(vertex); - position->set(positions.fLeft, positions.fBottom); - colorPtr = reinterpret_cast(vertex + sizeof(SkPoint)); - *colorPtr = color; - vertex += vertexStride; - - // V2 - position = reinterpret_cast(vertex); - position->set(positions.fRight, positions.fBottom); - colorPtr = reinterpret_cast(vertex + sizeof(SkPoint)); - *colorPtr = color; - vertex += vertexStride; - - // V3 - position = reinterpret_cast(vertex); - position->set(positions.fRight, positions.fTop); - colorPtr = reinterpret_cast(vertex + sizeof(SkPoint)); - *colorPtr = color; - } else { - // V0 - SkPoint* position = reinterpret_cast(vertex); - position->set(positions.fLeft, positions.fTop); - vertex += vertexStride; - - // V1 - position = reinterpret_cast(vertex); - position->set(positions.fLeft, positions.fBottom); - vertex += vertexStride; - - // V2 - position = reinterpret_cast(vertex); - position->set(positions.fRight, positions.fBottom); - vertex += vertexStride; - - // V3 - position = reinterpret_cast(vertex); - position->set(positions.fRight, positions.fTop); - } - - subRun->fGlyphEndIndex++; - subRun->fVertexEndIndex += vertexStride * GrAtlasTextBatch::kVerticesPerGlyph; -} - -void GrAtlasTextContext::flushRunAsPaths(GrDrawContext* dc, - const SkTextBlobRunIterator& it, - const GrClip& clip, const SkPaint& skPaint, - SkDrawFilter* drawFilter, const SkMatrix& viewMatrix, - const SkIRect& clipBounds, SkScalar x, SkScalar y) { - SkPaint runPaint = skPaint; - - size_t textLen = it.glyphCount() * sizeof(uint16_t); - const SkPoint& offset = it.offset(); - - it.applyFontToPaint(&runPaint); - - if (drawFilter && !drawFilter->filter(&runPaint, SkDrawFilter::kText_Type)) { - return; - } - - runPaint.setFlags(FilterTextFlags(fSurfaceProps, runPaint)); - - switch (it.positioning()) { - case SkTextBlob::kDefault_Positioning: - this->drawTextAsPath(dc, clip, runPaint, viewMatrix, - (const char *)it.glyphs(), - textLen, x + offset.x(), y + offset.y(), clipBounds); - break; - case SkTextBlob::kHorizontal_Positioning: - this->drawPosTextAsPath(dc, clip, runPaint, viewMatrix, - (const char*)it.glyphs(), - textLen, it.pos(), 1, SkPoint::Make(x, y + offset.y()), - clipBounds); - break; - case SkTextBlob::kFull_Positioning: - this->drawPosTextAsPath(dc, clip, runPaint, viewMatrix, - (const char*)it.glyphs(), - textLen, it.pos(), 2, SkPoint::Make(x, y), clipBounds); - break; - } -} - -inline GrDrawBatch* -GrAtlasTextContext::createBatch(GrAtlasTextBlob* cacheBlob, const PerSubRunInfo& info, - int glyphCount, int run, int subRun, - GrColor color, SkScalar transX, SkScalar transY, - const SkPaint& skPaint) { - GrMaskFormat format = info.fMaskFormat; - GrColor subRunColor; - if (kARGB_GrMaskFormat == format) { - uint8_t paintAlpha = skPaint.getAlpha(); - subRunColor = SkColorSetARGB(paintAlpha, paintAlpha, paintAlpha, paintAlpha); - } else { - subRunColor = color; - } - - GrAtlasTextBatch* batch; - if (info.fDrawAsDistanceFields) { - SkColor filteredColor; - SkColorFilter* colorFilter = skPaint.getColorFilter(); - if (colorFilter) { - filteredColor = colorFilter->filterColor(skPaint.getColor()); - } else { - filteredColor = skPaint.getColor(); - } - bool useBGR = SkPixelGeometryIsBGR(fSurfaceProps.pixelGeometry()); - batch = GrAtlasTextBatch::CreateDistanceField(glyphCount, fContext->getBatchFontCache(), - fDistanceAdjustTable, filteredColor, - info.fUseLCDText, useBGR); - } else { - batch = GrAtlasTextBatch::CreateBitmap(format, glyphCount, fContext->getBatchFontCache()); - } - GrAtlasTextBatch::Geometry& geometry = batch->geometry(); - geometry.fBlob = SkRef(cacheBlob); - geometry.fRun = run; - geometry.fSubRun = subRun; - geometry.fColor = subRunColor; - geometry.fTransX = transX; - geometry.fTransY = transY; - batch->init(); - - return batch; -} - -inline void GrAtlasTextContext::flushRun(GrDrawContext* dc, GrPipelineBuilder* pipelineBuilder, - GrAtlasTextBlob* cacheBlob, int run, GrColor color, - SkScalar transX, SkScalar transY, - const SkPaint& skPaint) { - for (int subRun = 0; subRun < cacheBlob->fRuns[run].fSubRunInfo.count(); subRun++) { - const PerSubRunInfo& info = cacheBlob->fRuns[run].fSubRunInfo[subRun]; - int glyphCount = info.fGlyphEndIndex - info.fGlyphStartIndex; - if (0 == glyphCount) { - continue; - } - - SkAutoTUnref batch(this->createBatch(cacheBlob, info, glyphCount, run, - subRun, color, transX, transY, - skPaint)); - dc->drawBatch(pipelineBuilder, batch); - } -} - -inline void GrAtlasTextContext::flushBigGlyphs(GrAtlasTextBlob* cacheBlob, - GrDrawContext* dc, - const GrClip& clip, const SkPaint& skPaint, - SkScalar transX, SkScalar transY, - const SkIRect& clipBounds) { - if (!cacheBlob->fBigGlyphs.count()) { - return; - } - - for (int i = 0; i < cacheBlob->fBigGlyphs.count(); i++) { - GrAtlasTextBlob::BigGlyph& bigGlyph = cacheBlob->fBigGlyphs[i]; - bigGlyph.fVx += transX; - bigGlyph.fVy += transY; - SkMatrix ctm; - ctm.setScale(bigGlyph.fScale, bigGlyph.fScale); - ctm.postTranslate(bigGlyph.fVx, bigGlyph.fVy); - if (bigGlyph.fApplyVM) { - ctm.postConcat(cacheBlob->fViewMatrix); - } - - GrBlurUtils::drawPathWithMaskFilter(fContext, dc, clip, bigGlyph.fPath, - skPaint, ctm, nullptr, clipBounds, false); - } -} - -void GrAtlasTextContext::flush(const SkTextBlob* blob, - GrAtlasTextBlob* cacheBlob, - GrDrawContext* dc, - GrRenderTarget* rt, - const SkPaint& skPaint, - const GrPaint& grPaint, - SkDrawFilter* drawFilter, - const GrClip& clip, - const SkMatrix& viewMatrix, - const SkIRect& clipBounds, - SkScalar x, SkScalar y, - SkScalar transX, SkScalar transY) { - // We loop through the runs of the blob, flushing each. If any run is too large, then we flush - // it as paths - GrPipelineBuilder pipelineBuilder(grPaint, rt, clip); - - GrColor color = grPaint.getColor(); - - SkTextBlobRunIterator it(blob); - for (int run = 0; !it.done(); it.next(), run++) { - if (cacheBlob->fRuns[run].fDrawAsPaths) { - this->flushRunAsPaths(dc, it, clip, skPaint, - drawFilter, viewMatrix, clipBounds, x, y); - continue; - } - cacheBlob->fRuns[run].fVertexBounds.offset(transX, transY); - this->flushRun(dc, &pipelineBuilder, cacheBlob, run, color, - transX, transY, skPaint); - } - - // Now flush big glyphs - this->flushBigGlyphs(cacheBlob, dc, clip, skPaint, transX, transY, clipBounds); -} - -void GrAtlasTextContext::flush(GrAtlasTextBlob* cacheBlob, - GrDrawContext* dc, - GrRenderTarget* rt, - const SkPaint& skPaint, - const GrPaint& grPaint, - const GrClip& clip, - const SkIRect& clipBounds) { - GrPipelineBuilder pipelineBuilder(grPaint, rt, clip); - - GrColor color = grPaint.getColor(); - for (int run = 0; run < cacheBlob->fRunCount; run++) { - this->flushRun(dc, &pipelineBuilder, cacheBlob, run, color, 0, 0, skPaint); - } - - // Now flush big glyphs - this->flushBigGlyphs(cacheBlob, dc, clip, skPaint, 0, 0, clipBounds); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// - -#ifdef GR_TEST_UTILS - -DRAW_BATCH_TEST_DEFINE(TextBlobBatch) { - static uint32_t gContextID = SK_InvalidGenID; - static GrAtlasTextContext* gTextContext = nullptr; - static SkSurfaceProps gSurfaceProps(SkSurfaceProps::kLegacyFontHost_InitType); - - if (context->uniqueID() != gContextID) { - gContextID = context->uniqueID(); - delete gTextContext; - - // We don't yet test the fall back to paths in the GrTextContext base class. This is mostly - // because we don't really want to have a gpu device here. - // We enable distance fields by twiddling a knob on the paint - gTextContext = GrAtlasTextContext::Create(context, gSurfaceProps); - } - - // Setup dummy SkPaint / GrPaint - GrColor color = GrRandomColor(random); - SkMatrix viewMatrix = GrTest::TestMatrixInvertible(random); - SkPaint skPaint; - skPaint.setColor(color); - skPaint.setLCDRenderText(random->nextBool()); - skPaint.setAntiAlias(skPaint.isLCDRenderText() ? true : random->nextBool()); - skPaint.setSubpixelText(random->nextBool()); - - GrPaint grPaint; - if (!SkPaintToGrPaint(context, skPaint, viewMatrix, &grPaint)) { - SkFAIL("couldn't convert paint\n"); - } - - const char* text = "The quick brown fox jumps over the lazy dog."; - int textLen = (int)strlen(text); - - // Setup clip - GrClip clip; - SkIRect noClip = SkIRect::MakeLargest(); - - // right now we don't handle textblobs, nor do we handle drawPosText. Since we only - // intend to test the batch with this unit test, that is okay. - SkAutoTUnref blob( - gTextContext->createDrawTextBlob(clip, grPaint, skPaint, viewMatrix, text, - static_cast(textLen), 0, 0, noClip)); - - SkScalar transX = static_cast(random->nextU()); - SkScalar transY = static_cast(random->nextU()); - const GrAtlasTextBlob::Run::SubRunInfo& info = blob->fRuns[0].fSubRunInfo[0]; - return gTextContext->createBatch(blob, info, textLen, 0, 0, color, transX, transY, skPaint); -} - -#endif diff --git a/gfx/skia/skia/src/gpu/GrAtlasTextContext.h b/gfx/skia/skia/src/gpu/GrAtlasTextContext.h deleted file mode 100644 index 2d445f35d8..0000000000 --- a/gfx/skia/skia/src/gpu/GrAtlasTextContext.h +++ /dev/null @@ -1,193 +0,0 @@ -/* - * Copyright 2015 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#ifndef GrAtlasTextContext_DEFINED -#define GrAtlasTextContext_DEFINED - -#include "GrTextContext.h" - -#include "GrAtlasTextBlob.h" -#include "GrGeometryProcessor.h" -#include "SkTextBlobRunIterator.h" - -#ifdef GR_TEST_UTILS -#include "GrBatchTest.h" -#endif - -class GrDrawBatch; -class GrDrawContext; -class GrDrawTarget; -class GrPipelineBuilder; -class GrTextBlobCache; -class SkGlyph; - -/* - * This class implements GrTextContext using standard bitmap fonts, and can also process textblobs. - */ -class GrAtlasTextContext : public GrTextContext { -public: - static GrAtlasTextContext* Create(GrContext*, const SkSurfaceProps&); - -private: - GrAtlasTextContext(GrContext*, const SkSurfaceProps&); - ~GrAtlasTextContext() override {} - - bool canDraw(const SkPaint&, const SkMatrix& viewMatrix) override; - - void onDrawText(GrDrawContext*, GrRenderTarget*, const GrClip&, const GrPaint&, const SkPaint&, - const SkMatrix& viewMatrix, const char text[], size_t byteLength, - SkScalar x, SkScalar y, const SkIRect& regionClipBounds) override; - void onDrawPosText(GrDrawContext*, GrRenderTarget*, const GrClip&, const GrPaint&, - const SkPaint&, const SkMatrix& viewMatrix, - const char text[], size_t byteLength, - const SkScalar pos[], int scalarsPerPosition, - const SkPoint& offset, const SkIRect& regionClipBounds) override; - void drawTextBlob(GrDrawContext*, GrRenderTarget*, const GrClip&, const SkPaint&, - const SkMatrix& viewMatrix, const SkTextBlob*, SkScalar x, SkScalar y, - SkDrawFilter*, const SkIRect& clipBounds) override; - - typedef GrAtlasTextBlob::Run Run; - typedef Run::SubRunInfo PerSubRunInfo; - - inline bool canDrawAsDistanceFields(const SkPaint&, const SkMatrix& viewMatrix); - GrAtlasTextBlob* setupDFBlob(int glyphCount, const SkPaint& origPaint, - const SkMatrix& viewMatrix, SkPaint* dfPaint, - SkScalar* textRatio); - void bmpAppendGlyph(GrAtlasTextBlob*, int runIndex, const SkGlyph&, int left, int top, - GrColor color, GrFontScaler*); - bool dfAppendGlyph(GrAtlasTextBlob*, int runIndex, const SkGlyph&, SkScalar sx, SkScalar sy, - GrColor color, GrFontScaler*, SkScalar textRatio, - const SkMatrix& viewMatrix); - inline void appendGlyphPath(GrAtlasTextBlob*, GrGlyph*, GrFontScaler*, const SkGlyph&, - SkScalar x, SkScalar y, SkScalar scale = 1.0f, - bool applyVM = false); - inline void appendGlyphCommon(GrAtlasTextBlob*, Run*, Run::SubRunInfo*, - const SkRect& positions, GrColor color, - size_t vertexStride, bool useVertexColor, - GrGlyph*); - - inline void flushRunAsPaths(GrDrawContext*, - const SkTextBlobRunIterator&, const GrClip& clip, - const SkPaint&, SkDrawFilter*, - const SkMatrix& viewMatrix, const SkIRect& clipBounds, SkScalar x, - SkScalar y); - inline GrDrawBatch* createBatch(GrAtlasTextBlob*, const PerSubRunInfo&, - int glyphCount, int run, int subRun, - GrColor, SkScalar transX, SkScalar transY, - const SkPaint&); - inline void flushRun(GrDrawContext*, GrPipelineBuilder*, GrAtlasTextBlob*, int run, GrColor, - SkScalar transX, SkScalar transY, const SkPaint&); - inline void flushBigGlyphs(GrAtlasTextBlob* cacheBlob, GrDrawContext*, - const GrClip& clip, const SkPaint& skPaint, - SkScalar transX, SkScalar transY, const SkIRect& clipBounds); - - // We have to flush SkTextBlobs differently from drawText / drawPosText - void flush(const SkTextBlob*, GrAtlasTextBlob*, GrDrawContext*, GrRenderTarget*, - const SkPaint&, const GrPaint&, SkDrawFilter*, const GrClip&, - const SkMatrix& viewMatrix, const SkIRect& clipBounds, SkScalar x, SkScalar y, - SkScalar transX, SkScalar transY); - void flush(GrAtlasTextBlob*, GrDrawContext*, GrRenderTarget*, const SkPaint&, - const GrPaint&, const GrClip&, const SkIRect& clipBounds); - - // A helper for drawing BitmapText in a run of distance fields - inline void fallbackDrawPosText(GrAtlasTextBlob*, int runIndex, - const GrClip&, GrColor color, - const SkPaint&, const SkMatrix& viewMatrix, - const SkTDArray& fallbackTxt, - const SkTDArray& fallbackPos, - int scalarsPerPosition, - const SkPoint& offset); - - void internalDrawBMPText(GrAtlasTextBlob*, int runIndex, SkGlyphCache*, const SkPaint&, - GrColor color, const SkMatrix& viewMatrix, - const char text[], size_t byteLength, - SkScalar x, SkScalar y); - void internalDrawBMPPosText(GrAtlasTextBlob*, int runIndex, SkGlyphCache*, const SkPaint&, - GrColor color, const SkMatrix& viewMatrix, - const char text[], size_t byteLength, - const SkScalar pos[], int scalarsPerPosition, - const SkPoint& offset); - - void internalDrawDFText(GrAtlasTextBlob*, int runIndex, const SkPaint&, - GrColor color, const SkMatrix& viewMatrix, - const char text[], size_t byteLength, - SkScalar x, SkScalar y, - SkScalar textRatio, - SkTDArray* fallbackTxt, - SkTDArray* fallbackPos, - SkPoint* offset, const SkPaint& origPaint); - void internalDrawDFPosText(GrAtlasTextBlob*, int runIndex, const SkPaint&, - GrColor color, const SkMatrix& viewMatrix, - const char text[], size_t byteLength, - const SkScalar pos[], int scalarsPerPosition, - const SkPoint& offset, - SkScalar textRatio, - SkTDArray* fallbackTxt, - SkTDArray* fallbackPos); - - // sets up the descriptor on the blob and returns a detached cache. Client must attach - inline static GrColor ComputeCanonicalColor(const SkPaint&, bool lcd); - inline SkGlyphCache* setupCache(Run*, const SkPaint&, const SkMatrix* viewMatrix, bool noGamma); - static inline bool MustRegenerateBlob(SkScalar* outTransX, SkScalar* outTransY, - const GrAtlasTextBlob&, const SkPaint&, GrColor, - const SkMaskFilter::BlurRec&, - const SkMatrix& viewMatrix, SkScalar x, SkScalar y); - void regenerateTextBlob(GrAtlasTextBlob* bmp, const SkPaint& skPaint, GrColor, - const SkMatrix& viewMatrix, - const SkTextBlob* blob, SkScalar x, SkScalar y, - SkDrawFilter* drawFilter, - const GrClip&); - inline static bool HasLCD(const SkTextBlob*); - inline void initDistanceFieldPaint(GrAtlasTextBlob*, SkPaint*, SkScalar* textRatio, - const SkMatrix&); - - // Test methods - // TODO this is really ugly. It'd be much nicer if positioning could be moved to batch - inline GrAtlasTextBlob* createDrawTextBlob(const GrClip&, const GrPaint&, - const SkPaint&, const SkMatrix& viewMatrix, - const char text[], size_t byteLength, - SkScalar x, SkScalar y, - const SkIRect& regionClipBounds); - inline GrAtlasTextBlob* createDrawPosTextBlob(const GrClip&, const GrPaint&, - const SkPaint&, const SkMatrix& viewMatrix, - const char text[], size_t byteLength, - const SkScalar pos[], int scalarsPerPosition, - const SkPoint& offset, - const SkIRect& regionClipBounds); - - // Distance field text needs this table to compute a value for use in the fragment shader. - // Because the GrAtlasTextContext can go out of scope before the final flush, this needs to be - // refcnted and malloced - struct DistanceAdjustTable : public SkNVRefCnt { - DistanceAdjustTable() { this->buildDistanceAdjustTable(); } - ~DistanceAdjustTable() { delete[] fTable; } - - const SkScalar& operator[] (int i) const { - return fTable[i]; - } - - private: - void buildDistanceAdjustTable(); - - SkScalar* fTable; - }; - - GrBatchTextStrike* fCurrStrike; - GrTextBlobCache* fCache; - SkAutoTUnref fDistanceAdjustTable; - - friend class GrTextBlobCache; - friend class GrAtlasTextBatch; - -#ifdef GR_TEST_UTILS - DRAW_BATCH_TEST_FRIEND(TextBlobBatch); -#endif - - typedef GrTextContext INHERITED; -}; - -#endif diff --git a/gfx/skia/skia/src/gpu/GrAuditTrail.cpp b/gfx/skia/skia/src/gpu/GrAuditTrail.cpp new file mode 100644 index 0000000000..6ff04d1144 --- /dev/null +++ b/gfx/skia/skia/src/gpu/GrAuditTrail.cpp @@ -0,0 +1,121 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "GrAuditTrail.h" + +void GrAuditTrail::JsonifyTArray(SkString* json, const char* name, const FrameArray& array) { + if (array.count()) { + json->appendf("\"%s\": [", name); + for (int i = 0; i < array.count(); i++) { + json->append(array[i]->toJson()); + if (i < array.count() - 1) { + json->append(","); + } + } + json->append("]"); + } +} + +// This will pretty print a very small subset of json +// The parsing rules are straightforward, aside from the fact that we do not want an extra newline +// before ',' and after '}', so we have a comma exception rule. +class PrettyPrintJson { +public: + SkString prettify(const SkString& json) { + fPrettyJson.reset(); + fTabCount = 0; + fFreshLine = false; + fCommaException = false; + for (size_t i = 0; i < json.size(); i++) { + if ('[' == json[i] || '{' == json[i]) { + this->newline(); + this->appendChar(json[i]); + fTabCount++; + this->newline(); + } else if (']' == json[i] || '}' == json[i]) { + fTabCount--; + this->newline(); + this->appendChar(json[i]); + fCommaException = true; + } else if (',' == json[i]) { + this->appendChar(json[i]); + this->newline(); + } else { + this->appendChar(json[i]); + } + } + return fPrettyJson; + } +private: + void appendChar(char appendee) { + if (fCommaException && ',' != appendee) { + this->newline(); + } + this->tab(); + fPrettyJson += appendee; + fFreshLine = false; + fCommaException = false; + } + + void tab() { + if (fFreshLine) { + for (int i = 0; i < fTabCount; i++) { + fPrettyJson += '\t'; + } + } + } + + void newline() { + if (!fFreshLine) { + fFreshLine = true; + fPrettyJson += '\n'; + } + } + + SkString fPrettyJson; + int fTabCount; + bool fFreshLine; + bool fCommaException; +}; + +static SkString pretty_print_json(SkString json) { + class PrettyPrintJson prettyPrintJson; + return prettyPrintJson.prettify(json); +} + +SkString GrAuditTrail::toJson() const { + SkString json; + json.append("{"); + JsonifyTArray(&json, "Stacks", fFrames); + json.append("}"); + + // TODO if this becomes a performance issue we should make pretty print configurable + return pretty_print_json(json); +} + +SkString GrAuditTrail::Frame::toJson() const { + SkString json; + json.append("{"); + json.appendf("\"Name\": \"%s\",", fName); + JsonifyTArray(&json, "Frames", fChildren); + json.append("}"); + return json; +} + +SkString GrAuditTrail::Batch::toJson() const { + SkString json; + json.append("{"); + json.appendf("\"Name\": \"%s\",", fName); + json.append("\"Bounds\": {"); + json.appendf("\"Left\": %f,", fBounds.fLeft); + json.appendf("\"Right\": %f,", fBounds.fRight); + json.appendf("\"Top\": %f,", fBounds.fTop); + json.appendf("\"Bottom\": %f", fBounds.fBottom); + json.append("}"); + json.append("}"); + return json; +} diff --git a/gfx/skia/skia/src/gpu/GrAutoLocaleSetter.h b/gfx/skia/skia/src/gpu/GrAutoLocaleSetter.h index bcb23cfb29..6bfff0cd89 100644 --- a/gfx/skia/skia/src/gpu/GrAutoLocaleSetter.h +++ b/gfx/skia/skia/src/gpu/GrAutoLocaleSetter.h @@ -10,6 +10,10 @@ #include "GrTypes.h" +#if defined(SK_BUILD_FOR_WIN) +#include "SkString.h" +#endif + #if !defined(SK_BUILD_FOR_ANDROID) #include #endif @@ -18,6 +22,12 @@ #include #endif +#if defined(SK_BUILD_FOR_ANDROID) || defined(__UCLIBC__) || defined(_NEWLIB_VERSION) +#define HAVE_LOCALE_T 0 +#else +#define HAVE_LOCALE_T 1 +#endif + /** * Helper class for ensuring that we don't use the wrong locale when building shaders. Android * doesn't support locale in the NDK, so this is a no-op there. @@ -26,9 +36,15 @@ class GrAutoLocaleSetter : public SkNoncopyable { public: GrAutoLocaleSetter (const char* name) { #if defined(SK_BUILD_FOR_WIN) - fOldPerThreadLocale = _configthreadlocale(_ENABLE_PER_THREAD_LOCALE); - fOldLocale = setlocale(LC_ALL, name); -#elif !defined(SK_BUILD_FOR_ANDROID) && !defined(__UCLIBC__) + fOldPerThreadLocale = _configthreadlocale(_ENABLE_PER_THREAD_LOCALE); + char* oldLocale = setlocale(LC_ALL, name); + if (oldLocale) { + fOldLocale = oldLocale; + fShouldRestoreLocale = true; + } else { + fShouldRestoreLocale = false; + } +#elif HAVE_LOCALE_T fLocale = newlocale(LC_ALL, name, 0); if (fLocale) { fOldLocale = uselocale(fLocale); @@ -42,9 +58,11 @@ public: ~GrAutoLocaleSetter () { #if defined(SK_BUILD_FOR_WIN) - setlocale(LC_ALL, fOldLocale); + if (fShouldRestoreLocale) { + setlocale(LC_ALL, fOldLocale.c_str()); + } _configthreadlocale(fOldPerThreadLocale); -#elif !defined(SK_BUILD_FOR_ANDROID) && !defined(__UCLIBC__) +#elif HAVE_LOCALE_T if (fLocale) { uselocale(fOldLocale); freelocale(fLocale); @@ -55,12 +73,15 @@ public: private: #if defined(SK_BUILD_FOR_WIN) int fOldPerThreadLocale; - const char* fOldLocale; -#elif !defined(SK_BUILD_FOR_ANDROID) && !defined(__UCLIBC__) + bool fShouldRestoreLocale; + SkString fOldLocale; +#elif HAVE_LOCALE_T locale_t fOldLocale; locale_t fLocale; #endif }; +#undef HAVE_LOCALE_T + #endif diff --git a/gfx/skia/skia/src/gpu/GrCaps.cpp b/gfx/skia/skia/src/gpu/GrCaps.cpp index 7003cc5bfe..101d5f445b 100644 --- a/gfx/skia/skia/src/gpu/GrCaps.cpp +++ b/gfx/skia/skia/src/gpu/GrCaps.cpp @@ -106,9 +106,6 @@ GrCaps::GrCaps(const GrContextOptions& options) { fMaxTextureSize = 1; fMaxSampleCount = 0; - memset(fConfigRenderSupport, 0, sizeof(fConfigRenderSupport)); - memset(fConfigTextureSupport, 0, sizeof(fConfigTextureSupport)); - fSuppressPrints = options.fSuppressPrints; fImmediateFlush = options.fImmediateMode; fDrawPathMasksToCompressedTextureSupport = options.fDrawPathToCompressedTexture; @@ -229,22 +226,24 @@ SkString GrCaps::dump() const { GR_STATIC_ASSERT(14 == kRGBA_half_GrPixelConfig); GR_STATIC_ASSERT(SK_ARRAY_COUNT(kConfigNames) == kGrPixelConfigCnt); - SkASSERT(!fConfigRenderSupport[kUnknown_GrPixelConfig][0]); - SkASSERT(!fConfigRenderSupport[kUnknown_GrPixelConfig][1]); + SkASSERT(!this->isConfigRenderable(kUnknown_GrPixelConfig, false)); + SkASSERT(!this->isConfigRenderable(kUnknown_GrPixelConfig, true)); for (size_t i = 1; i < SK_ARRAY_COUNT(kConfigNames); ++i) { + GrPixelConfig config = static_cast(i); r.appendf("%s is renderable: %s, with MSAA: %s\n", kConfigNames[i], - gNY[fConfigRenderSupport[i][0]], - gNY[fConfigRenderSupport[i][1]]); + gNY[this->isConfigRenderable(config, false)], + gNY[this->isConfigRenderable(config, true)]); } - SkASSERT(!fConfigTextureSupport[kUnknown_GrPixelConfig]); + SkASSERT(!this->isConfigTexturable(kUnknown_GrPixelConfig)); for (size_t i = 1; i < SK_ARRAY_COUNT(kConfigNames); ++i) { + GrPixelConfig config = static_cast(i); r.appendf("%s is uploadable to a texture: %s\n", kConfigNames[i], - gNY[fConfigTextureSupport[i]]); + gNY[this->isConfigTexturable(config)]); } return r; diff --git a/gfx/skia/skia/src/gpu/GrClipMaskManager.cpp b/gfx/skia/skia/src/gpu/GrClipMaskManager.cpp index 9eaf415cce..3168424cf8 100644 --- a/gfx/skia/skia/src/gpu/GrClipMaskManager.cpp +++ b/gfx/skia/skia/src/gpu/GrClipMaskManager.cpp @@ -20,6 +20,7 @@ #include "GrSWMaskHelper.h" #include "SkRasterClip.h" #include "SkTLazy.h" +#include "batches/GrRectBatchFactory.h" #include "effects/GrConvexPolyEffect.h" #include "effects/GrPorterDuffXferProcessor.h" #include "effects/GrRRectEffect.h" @@ -47,6 +48,16 @@ static const GrFragmentProcessor* create_fp_for_mask(GrTexture* result, const Sk kDevice_GrCoordSet); } +static void draw_non_aa_rect(GrDrawTarget* drawTarget, + const GrPipelineBuilder& pipelineBuilder, + GrColor color, + const SkMatrix& viewMatrix, + const SkRect& rect) { + SkAutoTUnref batch(GrRectBatchFactory::CreateNonAAFill(color, viewMatrix, rect, + nullptr, nullptr)); + drawTarget->drawBatch(pipelineBuilder, batch); +} + // Does the path in 'element' require SW rendering? If so, return true (and, // optionally, set 'prOut' to NULL. If not, return false (and, optionally, set // 'prOut' to the non-SW path renderer that will do the job). @@ -506,20 +517,24 @@ bool GrClipMaskManager::drawElement(GrPipelineBuilder* pipelineBuilder, case Element::kEmpty_Type: SkDEBUGFAIL("Should never get here with an empty element."); break; - case Element::kRect_Type: + case Element::kRect_Type: { // TODO: Do rects directly to the accumulator using a aa-rect GrProcessor that covers // the entire mask bounds and writes 0 outside the rect. if (element->isAA()) { SkRect devRect = element->getRect(); viewMatrix.mapRect(&devRect); - fDrawTarget->drawAARect(*pipelineBuilder, color, viewMatrix, - element->getRect(), devRect); + SkAutoTUnref batch( + GrRectBatchFactory::CreateAAFill(color, viewMatrix, element->getRect(), + devRect)); + + fDrawTarget->drawBatch(*pipelineBuilder, batch); } else { - fDrawTarget->drawNonAARect(*pipelineBuilder, color, viewMatrix, - element->getRect()); + draw_non_aa_rect(fDrawTarget, *pipelineBuilder, color, viewMatrix, + element->getRect()); } return true; + } default: { SkPath path; element->asPath(&path); @@ -691,8 +706,8 @@ GrTexture* GrClipMaskManager::createAlphaClipMask(int32_t elementsGenID, backgroundPipelineBuilder.setStencil(kDrawOutsideElement); // The color passed in here does not matter since the coverageSetOpXP won't read it. - fDrawTarget->drawNonAARect(backgroundPipelineBuilder, GrColor_WHITE, translate, - clipSpaceIBounds); + draw_non_aa_rect(fDrawTarget, backgroundPipelineBuilder, GrColor_WHITE, translate, + SkRect::Make(clipSpaceIBounds)); } } else { GrPipelineBuilder pipelineBuilder; @@ -831,11 +846,8 @@ bool GrClipMaskManager::createStencilClipMask(GrRenderTarget* rt, if (Element::kRect_Type == element->getType()) { *pipelineBuilder.stencil() = gDrawToStencil; - // We need this AGP until everything is in GrBatch - fDrawTarget->drawNonAARect(pipelineBuilder, - GrColor_WHITE, - viewMatrix, - element->getRect()); + draw_non_aa_rect(fDrawTarget, pipelineBuilder, GrColor_WHITE, viewMatrix, + element->getRect()); } else { if (!clipPath.isEmpty()) { if (canRenderDirectToStencil) { @@ -873,11 +885,8 @@ bool GrClipMaskManager::createStencilClipMask(GrRenderTarget* rt, if (canDrawDirectToClip) { if (Element::kRect_Type == element->getType()) { - // We need this AGP until everything is in GrBatch - fDrawTarget->drawNonAARect(pipelineBuilder, - GrColor_WHITE, - viewMatrix, - element->getRect()); + draw_non_aa_rect(fDrawTarget, pipelineBuilder, GrColor_WHITE, viewMatrix, + element->getRect()); } else { GrPathRenderer::DrawPathArgs args; args.fTarget = fDrawTarget; @@ -893,10 +902,8 @@ bool GrClipMaskManager::createStencilClipMask(GrRenderTarget* rt, } else { // The view matrix is setup to do clip space -> stencil space translation, so // draw rect in clip space. - fDrawTarget->drawNonAARect(pipelineBuilder, - GrColor_WHITE, - viewMatrix, - SkRect::Make(clipSpaceIBounds)); + draw_non_aa_rect(fDrawTarget, pipelineBuilder, GrColor_WHITE, viewMatrix, + SkRect::Make(clipSpaceIBounds)); } } } diff --git a/gfx/skia/skia/src/gpu/GrContext.cpp b/gfx/skia/skia/src/gpu/GrContext.cpp index ad89672f3b..e92014415c 100644 --- a/gfx/skia/skia/src/gpu/GrContext.cpp +++ b/gfx/skia/skia/src/gpu/GrContext.cpp @@ -15,14 +15,16 @@ #include "GrResourceProvider.h" #include "GrSoftwarePathRenderer.h" #include "GrSurfacePriv.h" -#include "GrTextBlobCache.h" #include "SkConfig8888.h" #include "SkGrPriv.h" #include "effects/GrConfigConversionEffect.h" +#include "text/GrTextBlobCache.h" #define ASSERT_OWNED_RESOURCE(R) SkASSERT(!(R) || (R)->getContext() == this) +#define ASSERT_SINGLE_OWNER \ + SkDEBUGCODE(GrSingleOwner::AutoEnforce debug_SingleOwner(&fSingleOwner);) #define RETURN_IF_ABANDONED if (fDrawingManager->abandoned()) { return; } #define RETURN_FALSE_IF_ABANDONED if (fDrawingManager->abandoned()) { return false; } #define RETURN_NULL_IF_ABANDONED if (fDrawingManager->abandoned()) { return nullptr; } @@ -66,6 +68,7 @@ GrContext::GrContext() : fUniqueID(next_id()) { bool GrContext::init(GrBackend backend, GrBackendContext backendContext, const GrContextOptions& options) { + ASSERT_SINGLE_OWNER SkASSERT(!fGpu); fGpu = GrGpu::Create(backend, backendContext, options, this); @@ -77,10 +80,12 @@ bool GrContext::init(GrBackend backend, GrBackendContext backendContext, } void GrContext::initCommon(const GrContextOptions& options) { + ASSERT_SINGLE_OWNER + fCaps = SkRef(fGpu->caps()); fResourceCache = new GrResourceCache(fCaps); fResourceCache->setOverBudgetCallback(OverBudgetCB, this); - fResourceProvider = new GrResourceProvider(fGpu, fResourceCache); + fResourceProvider = new GrResourceProvider(fGpu, fResourceCache, &fSingleOwner); fLayerCache.reset(new GrLayerCache(this)); @@ -88,7 +93,9 @@ void GrContext::initCommon(const GrContextOptions& options) { GrDrawTarget::Options dtOptions; dtOptions.fClipBatchToBounds = options.fClipBatchToBounds; - fDrawingManager.reset(new GrDrawingManager(this, dtOptions)); + dtOptions.fDrawBatchBounds = options.fDrawBatchBounds; + dtOptions.fMaxBatchLookback = options.fMaxBatchLookback; + fDrawingManager.reset(new GrDrawingManager(this, dtOptions, &fSingleOwner)); // GrBatchFontCache will eventually replace GrFontCache fBatchFontCache = new GrBatchFontCache(this); @@ -97,6 +104,8 @@ void GrContext::initCommon(const GrContextOptions& options) { } GrContext::~GrContext() { + ASSERT_SINGLE_OWNER + if (!fGpu) { SkASSERT(!fCaps); return; @@ -119,6 +128,8 @@ GrContext::~GrContext() { } void GrContext::abandonContext() { + ASSERT_SINGLE_OWNER + fResourceProvider->abandon(); // Need to abandon the drawing manager first so all the render targets @@ -137,10 +148,13 @@ void GrContext::abandonContext() { } void GrContext::resetContext(uint32_t state) { + ASSERT_SINGLE_OWNER fGpu->markContextDirty(state); } void GrContext::freeGpuResources() { + ASSERT_SINGLE_OWNER + this->flush(); fBatchFontCache->freeAll(); @@ -152,6 +166,8 @@ void GrContext::freeGpuResources() { } void GrContext::getResourceCacheUsage(int* resourceCount, size_t* resourceBytes) const { + ASSERT_SINGLE_OWNER + if (resourceCount) { *resourceCount = fResourceCache->getBudgetedResourceCount(); } @@ -185,6 +201,7 @@ void GrContext::TextBlobCacheOverBudgetCB(void* data) { //////////////////////////////////////////////////////////////////////////////// void GrContext::flush(int flagsBitfield) { + ASSERT_SINGLE_OWNER RETURN_IF_ABANDONED if (kDiscard_FlushBit & flagsBitfield) { @@ -219,9 +236,11 @@ bool GrContext::writeSurfacePixels(GrSurface* surface, int left, int top, int width, int height, GrPixelConfig srcConfig, const void* buffer, size_t rowBytes, uint32_t pixelOpsFlags) { + ASSERT_SINGLE_OWNER RETURN_FALSE_IF_ABANDONED ASSERT_OWNED_RESOURCE(surface); SkASSERT(surface); + GR_AUDIT_TRAIL_AUTO_FRAME(&fAuditTrail, "GrContext::writeSurfacePixels"); this->testPMConversionsIfNecessary(pixelOpsFlags); @@ -357,9 +376,11 @@ bool GrContext::readSurfacePixels(GrSurface* src, int left, int top, int width, int height, GrPixelConfig dstConfig, void* buffer, size_t rowBytes, uint32_t flags) { + ASSERT_SINGLE_OWNER RETURN_FALSE_IF_ABANDONED ASSERT_OWNED_RESOURCE(src); SkASSERT(src); + GR_AUDIT_TRAIL_AUTO_FRAME(&fAuditTrail, "GrContext::readSurfacePixels"); this->testPMConversionsIfNecessary(flags); SkAutoMutexAcquire ama(fReadPixelsMutex); @@ -484,6 +505,7 @@ bool GrContext::readSurfacePixels(GrSurface* src, } void GrContext::prepareSurfaceForExternalIO(GrSurface* surface) { + ASSERT_SINGLE_OWNER RETURN_IF_ABANDONED SkASSERT(surface); ASSERT_OWNED_RESOURCE(surface); @@ -498,7 +520,10 @@ void GrContext::prepareSurfaceForExternalIO(GrSurface* surface) { void GrContext::copySurface(GrSurface* dst, GrSurface* src, const SkIRect& srcRect, const SkIPoint& dstPoint, uint32_t pixelOpsFlags) { + ASSERT_SINGLE_OWNER RETURN_IF_ABANDONED + GR_AUDIT_TRAIL_AUTO_FRAME(&fAuditTrail, "GrContext::copySurface"); + if (!src || !dst) { return; } @@ -524,6 +549,7 @@ void GrContext::copySurface(GrSurface* dst, GrSurface* src, const SkIRect& srcRe } void GrContext::flushSurfaceWrites(GrSurface* surface) { + ASSERT_SINGLE_OWNER RETURN_IF_ABANDONED if (surface->surfacePriv().hasPendingWrite()) { this->flush(); @@ -533,6 +559,8 @@ void GrContext::flushSurfaceWrites(GrSurface* surface) { //////////////////////////////////////////////////////////////////////////////// int GrContext::getRecommendedSampleCount(GrPixelConfig config, SkScalar dpi) const { + ASSERT_SINGLE_OWNER + if (!this->caps()->isConfigRenderable(config, true)) { return 0; } @@ -550,10 +578,12 @@ int GrContext::getRecommendedSampleCount(GrPixelConfig config, GrDrawContext* GrContext::drawContext(GrRenderTarget* rt, const SkSurfaceProps* surfaceProps) { + ASSERT_SINGLE_OWNER return fDrawingManager->drawContext(rt, surfaceProps); } -bool GrContext::abandoned() const { +bool GrContext::abandoned() const { + ASSERT_SINGLE_OWNER return fDrawingManager->abandoned(); } @@ -568,6 +598,7 @@ void test_pm_conversions(GrContext* ctx, int* pmToUPMValue, int* upmToPMValue) { } void GrContext::testPMConversionsIfNecessary(uint32_t flags) { + ASSERT_SINGLE_OWNER if (SkToBool(kUnpremul_PixelOpsFlag & flags)) { SkAutoMutexAcquire ama(fTestPMConversionsMutex); if (!fDidTestPMConversions) { @@ -580,6 +611,7 @@ void GrContext::testPMConversionsIfNecessary(uint32_t flags) { const GrFragmentProcessor* GrContext::createPMToUPMEffect(GrTexture* texture, bool swapRAndB, const SkMatrix& matrix) const { + ASSERT_SINGLE_OWNER // We should have already called this->testPMConversionsIfNecessary(). SkASSERT(fDidTestPMConversions); GrConfigConversionEffect::PMConversion pmToUPM = @@ -594,6 +626,7 @@ const GrFragmentProcessor* GrContext::createPMToUPMEffect(GrTexture* texture, const GrFragmentProcessor* GrContext::createUPMToPMEffect(GrTexture* texture, bool swapRAndB, const SkMatrix& matrix) const { + ASSERT_SINGLE_OWNER // We should have already called this->testPMConversionsIfNecessary(). SkASSERT(fDidTestPMConversions); GrConfigConversionEffect::PMConversion upmToPM = @@ -606,6 +639,7 @@ const GrFragmentProcessor* GrContext::createUPMToPMEffect(GrTexture* texture, } bool GrContext::didFailPMUPMConversionTest() const { + ASSERT_SINGLE_OWNER // We should have already called this->testPMConversionsIfNecessary(). SkASSERT(fDidTestPMConversions); // The PM<->UPM tests fail or succeed together so we only need to check one. @@ -615,6 +649,7 @@ bool GrContext::didFailPMUPMConversionTest() const { ////////////////////////////////////////////////////////////////////////////// void GrContext::getResourceCacheLimits(int* maxTextures, size_t* maxTextureBytes) const { + ASSERT_SINGLE_OWNER if (maxTextures) { *maxTextures = fResourceCache->getMaxResourceCount(); } @@ -624,11 +659,13 @@ void GrContext::getResourceCacheLimits(int* maxTextures, size_t* maxTextureBytes } void GrContext::setResourceCacheLimits(int maxTextures, size_t maxTextureBytes) { + ASSERT_SINGLE_OWNER fResourceCache->setLimits(maxTextures, maxTextureBytes); } ////////////////////////////////////////////////////////////////////////////// void GrContext::dumpMemoryStatistics(SkTraceMemoryDump* traceMemoryDump) const { + ASSERT_SINGLE_OWNER fResourceCache->dumpMemoryStatistics(traceMemoryDump); } diff --git a/gfx/skia/skia/src/gpu/GrContextFactory.cpp b/gfx/skia/skia/src/gpu/GrContextFactory.cpp index 424b3fda3a..b7e48254c8 100644 --- a/gfx/skia/skia/src/gpu/GrContextFactory.cpp +++ b/gfx/skia/skia/src/gpu/GrContextFactory.cpp @@ -23,66 +23,106 @@ #include "gl/GrGLGpu.h" #include "GrCaps.h" -GrContextFactory::ContextInfo* GrContextFactory::getContextInfo(GLContextType type, - GrGLStandard forcedGpuAPI) { - for (int i = 0; i < fContexts.count(); ++i) { - if (forcedGpuAPI != kNone_GrGLStandard && - forcedGpuAPI != fContexts[i]->fGLContext->gl()->fStandard) - continue; +GrContextFactory::GrContextFactory() { } - if (fContexts[i]->fType == type) { - fContexts[i]->fGLContext->makeCurrent(); - return fContexts[i]; +GrContextFactory::GrContextFactory(const GrContextOptions& opts) + : fGlobalOptions(opts) { +} + +GrContextFactory::~GrContextFactory() { + this->destroyContexts(); +} + +void GrContextFactory::destroyContexts() { + for (Context& context : fContexts) { + if (context.fGLContext) { + context.fGLContext->makeCurrent(); + } + if (!context.fGrContext->unique()) { + context.fGrContext->abandonContext(); + } + context.fGrContext->unref(); + delete(context.fGLContext); + } + fContexts.reset(); +} + +void GrContextFactory::abandonContexts() { + for (Context& context : fContexts) { + if (context.fGLContext) { + context.fGLContext->makeCurrent(); + context.fGLContext->testAbandon(); + delete(context.fGLContext); + context.fGLContext = nullptr; + } + context.fGrContext->abandonContext(); + } +} + +GrContextFactory::ContextInfo GrContextFactory::getContextInfo(GLContextType type, + GLContextOptions options) { + for (int i = 0; i < fContexts.count(); ++i) { + Context& context = fContexts[i]; + if (!context.fGLContext) { + continue; + } + if (context.fType == type && + context.fOptions == options) { + context.fGLContext->makeCurrent(); + return ContextInfo(context.fGrContext, context.fGLContext); } } - SkAutoTUnref glCtx; + SkAutoTDelete glCtx; SkAutoTUnref grCtx; switch (type) { - case kNVPR_GLContextType: // fallthru case kNative_GLContextType: - glCtx.reset(SkCreatePlatformGLContext(forcedGpuAPI)); + glCtx.reset(SkCreatePlatformGLContext(kNone_GrGLStandard)); break; -#ifdef SK_ANGLE + case kGL_GLContextType: + glCtx.reset(SkCreatePlatformGLContext(kGL_GrGLStandard)); + break; + case kGLES_GLContextType: + glCtx.reset(SkCreatePlatformGLContext(kGLES_GrGLStandard)); + break; +#if SK_ANGLE +#ifdef SK_BUILD_FOR_WIN case kANGLE_GLContextType: - glCtx.reset(SkANGLEGLContext::Create(forcedGpuAPI, false)); + glCtx.reset(SkANGLEGLContext::CreateDirectX()); break; +#endif case kANGLE_GL_GLContextType: - glCtx.reset(SkANGLEGLContext::Create(forcedGpuAPI, true)); + glCtx.reset(SkANGLEGLContext::CreateOpenGL()); break; #endif -#ifdef SK_COMMAND_BUFFER +#if SK_COMMAND_BUFFER case kCommandBuffer_GLContextType: - glCtx.reset(SkCommandBufferGLContext::Create(forcedGpuAPI)); + glCtx.reset(SkCommandBufferGLContext::Create()); break; #endif -#ifdef SK_MESA +#if SK_MESA case kMESA_GLContextType: - glCtx.reset(SkMesaGLContext::Create(forcedGpuAPI)); + glCtx.reset(SkMesaGLContext::Create()); break; #endif case kNull_GLContextType: - glCtx.reset(SkNullGLContext::Create(forcedGpuAPI)); + glCtx.reset(SkNullGLContext::Create()); break; case kDebug_GLContextType: - glCtx.reset(SkDebugGLContext::Create(forcedGpuAPI)); + glCtx.reset(SkDebugGLContext::Create()); break; } if (nullptr == glCtx.get()) { - return nullptr; + return ContextInfo(); } SkASSERT(glCtx->isValid()); // Block NVPR from non-NVPR types. SkAutoTUnref glInterface(SkRef(glCtx->gl())); - if (kNVPR_GLContextType != type) { + if (!(kEnableNVPR_GLContextOptions & options)) { glInterface.reset(GrGLInterfaceRemoveNVPR(glInterface)); if (!glInterface) { - return nullptr; - } - } else { - if (!glInterface->hasExtension("GL_NV_path_rendering")) { - return nullptr; + return ContextInfo(); } } @@ -94,30 +134,18 @@ GrContextFactory::ContextInfo* GrContextFactory::getContextInfo(GLContextType ty grCtx.reset(GrContext::Create(kOpenGL_GrBackend, p3dctx, fGlobalOptions)); #endif if (!grCtx.get()) { - return nullptr; + return ContextInfo(); } - // Warn if path rendering support is not available for the NVPR type. - if (kNVPR_GLContextType == type) { + if (kEnableNVPR_GLContextOptions & options) { if (!grCtx->caps()->shaderCaps()->pathRenderingSupport()) { - GrGpu* gpu = grCtx->getGpu(); - const GrGLContext* ctx = gpu->glContextForTesting(); - if (ctx) { - const GrGLubyte* verUByte; - GR_GL_CALL_RET(ctx->interface(), verUByte, GetString(GR_GL_VERSION)); - const char* ver = reinterpret_cast(verUByte); - SkDebugf("\nWARNING: nvprmsaa config requested, but driver path rendering " - "support not available. Maybe update the driver? Your driver version " - "string: \"%s\"\n", ver); - } else { - SkDebugf("\nWARNING: nvprmsaa config requested, but driver path rendering " - "support not available.\n"); - } + return ContextInfo(); } } - ContextInfo* ctx = fContexts.emplace_back(new ContextInfo); - ctx->fGLContext = SkRef(glCtx.get()); - ctx->fGrContext = SkRef(grCtx.get()); - ctx->fType = type; - return ctx; + Context& context = fContexts.push_back(); + context.fGLContext = glCtx.detach(); + context.fGrContext = SkRef(grCtx.get()); + context.fType = type; + context.fOptions = options; + return ContextInfo(context.fGrContext, context.fGLContext); } diff --git a/gfx/skia/skia/src/gpu/GrContextFactory.h b/gfx/skia/skia/src/gpu/GrContextFactory.h index c837e74a25..7afa3108c6 100644 --- a/gfx/skia/skia/src/gpu/GrContextFactory.h +++ b/gfx/skia/skia/src/gpu/GrContextFactory.h @@ -23,35 +23,38 @@ */ class GrContextFactory : SkNoncopyable { public: - /** - * Types of GL contexts supported. For historical and testing reasons the native GrContext will - * not use "GL_NV_path_rendering" even when the driver supports it. There is a separate context - * type that does not remove NVPR support and which will fail when the driver does not support - * the extension. - */ enum GLContextType { - kNative_GLContextType, + kNative_GLContextType, //! OpenGL or OpenGL ES context. + kGL_GLContextType, //! OpenGL context. + kGLES_GLContextType, //! OpenGL ES context. #if SK_ANGLE - kANGLE_GLContextType, - kANGLE_GL_GLContextType, +#ifdef SK_BUILD_FOR_WIN + kANGLE_GLContextType, //! ANGLE on DirectX OpenGL ES context. +#endif + kANGLE_GL_GLContextType, //! ANGLE on OpenGL OpenGL ES context. #endif #if SK_COMMAND_BUFFER - kCommandBuffer_GLContextType, + kCommandBuffer_GLContextType, //! Chromium command buffer OpenGL ES context. #endif #if SK_MESA - kMESA_GLContextType, + kMESA_GLContextType, //! MESA OpenGL context #endif - /** Similar to kNative but does not filter NVPR. It will fail if the GL driver does not - support NVPR */ - kNVPR_GLContextType, - kNull_GLContextType, - kDebug_GLContextType, - - kLastGLContextType = kDebug_GLContextType + kNull_GLContextType, //! Non-rendering OpenGL mock context. + kDebug_GLContextType, //! Non-rendering, state verifying OpenGL context. + kLastGLContextType = kDebug_GLContextType }; static const int kGLContextTypeCnt = kLastGLContextType + 1; + /** + * Options for GL context creation. For historical and testing reasons the options will default + * to not using GL_NV_path_rendering extension even when the driver supports it. + */ + enum GLContextOptions { + kNone_GLContextOptions = 0, + kEnableNVPR_GLContextOptions = 0x1, + }; + static bool IsRenderingGLContext(GLContextType type) { switch (type) { case kNull_GLContextType: @@ -66,11 +69,15 @@ public: switch (type) { case kNative_GLContextType: return "native"; - case kNull_GLContextType: - return "null"; + case kGL_GLContextType: + return "gl"; + case kGLES_GLContextType: + return "gles"; #if SK_ANGLE +#ifdef SK_BUILD_FOR_WIN case kANGLE_GLContextType: return "angle"; +#endif case kANGLE_GL_GLContextType: return "angle-gl"; #endif @@ -82,8 +89,8 @@ public: case kMESA_GLContextType: return "mesa"; #endif - case kNVPR_GLContextType: - return "nvpr"; + case kNull_GLContextType: + return "null"; case kDebug_GLContextType: return "debug"; default: @@ -91,57 +98,47 @@ public: } } - explicit GrContextFactory(const GrContextOptions& opts) : fGlobalOptions(opts) { } - GrContextFactory() { } + explicit GrContextFactory(const GrContextOptions& opts); + GrContextFactory(); - ~GrContextFactory() { this->destroyContexts(); } + ~GrContextFactory(); - void destroyContexts() { - for (int i = 0; i < fContexts.count(); ++i) { - if (fContexts[i]->fGLContext) { // could be abandoned. - fContexts[i]->fGLContext->makeCurrent(); - } - fContexts[i]->fGrContext->unref(); - SkSafeUnref(fContexts[i]->fGLContext); - } - fContexts.reset(); - } - - void abandonContexts() { - for (int i = 0; i < fContexts.count(); ++i) { - if (fContexts[i]->fGLContext) { - fContexts[i]->fGLContext->testAbandon(); - SkSafeSetNull(fContexts[i]->fGLContext); - } - fContexts[i]->fGrContext->abandonContext(); - } - } + void destroyContexts(); + void abandonContexts(); struct ContextInfo { - GLContextType fType; - SkGLContext* fGLContext; - GrContext* fGrContext; + ContextInfo() + : fGrContext(nullptr), fGLContext(nullptr) { } + ContextInfo(GrContext* grContext, SkGLContext* glContext) + : fGrContext(grContext), fGLContext(glContext) { } + GrContext* fGrContext; + SkGLContext* fGLContext; //! Valid until the factory destroys it via abandonContexts() or + //! destroyContexts(). }; + /** * Get a context initialized with a type of GL context. It also makes the GL context current. - * Pointer is valid until destroyContexts() is called. */ - ContextInfo* getContextInfo(GLContextType type, GrGLStandard forcedGpuAPI = kNone_GrGLStandard); - + ContextInfo getContextInfo(GLContextType type, + GLContextOptions options = kNone_GLContextOptions); /** * Get a GrContext initialized with a type of GL context. It also makes the GL context current. */ - GrContext* get(GLContextType type, GrGLStandard forcedGpuAPI = kNone_GrGLStandard) { - if (ContextInfo* info = this->getContextInfo(type, forcedGpuAPI)) { - return info->fGrContext; - } - return nullptr; + GrContext* get(GLContextType type, + GLContextOptions options = kNone_GLContextOptions) { + return this->getContextInfo(type, options).fGrContext; } const GrContextOptions& getGlobalOptions() const { return fGlobalOptions; } private: - SkTArray, true> fContexts; - const GrContextOptions fGlobalOptions; + struct Context { + GLContextType fType; + GLContextOptions fOptions; + SkGLContext* fGLContext; + GrContext* fGrContext; + }; + SkTArray fContexts; + const GrContextOptions fGlobalOptions; }; #endif diff --git a/gfx/skia/skia/src/gpu/GrDefaultGeoProcFactory.cpp b/gfx/skia/skia/src/gpu/GrDefaultGeoProcFactory.cpp index 85339ea9b5..25da4dcae9 100644 --- a/gfx/skia/skia/src/gpu/GrDefaultGeoProcFactory.cpp +++ b/gfx/skia/skia/src/gpu/GrDefaultGeoProcFactory.cpp @@ -10,9 +10,9 @@ #include "GrInvariantOutput.h" #include "glsl/GrGLSLFragmentShaderBuilder.h" #include "glsl/GrGLSLGeometryProcessor.h" -#include "glsl/GrGLSLProgramBuilder.h" #include "glsl/GrGLSLVertexShaderBuilder.h" #include "glsl/GrGLSLVarying.h" +#include "glsl/GrGLSLUniformHandler.h" #include "glsl/GrGLSLUtil.h" /* @@ -64,10 +64,10 @@ public: void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override { const DefaultGeoProc& gp = args.fGP.cast(); - GrGLSLGPBuilder* pb = args.fPB; GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder; GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder; GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler; + GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; // emit attributes varyingHandler->emitAttributes(gp); @@ -77,13 +77,14 @@ public: if (gp.hasVertexColor()) { varyingHandler->addPassThroughAttribute(gp.inColor(), args.fOutputColor); } else { - this->setupUniformColor(pb, fragBuilder, args.fOutputColor, &fColorUniform); + this->setupUniformColor(fragBuilder, uniformHandler, args.fOutputColor, + &fColorUniform); } } // Setup position - this->setupPosition(pb, - vertBuilder, + this->setupPosition(vertBuilder, + uniformHandler, gpArgs, gp.inPosition()->fName, gp.viewMatrix(), @@ -91,9 +92,9 @@ public: if (gp.hasExplicitLocalCoords()) { // emit transforms with explicit local coords - this->emitTransforms(pb, - vertBuilder, + this->emitTransforms(vertBuilder, varyingHandler, + uniformHandler, gpArgs->fPositionVar, gp.inLocalCoords()->fName, gp.localMatrix(), @@ -101,17 +102,16 @@ public: args.fTransformsOut); } else if(gp.hasTransformedLocalCoords()) { // transforms have already been applied to vertex attributes on the cpu - this->emitTransforms(pb, - vertBuilder, + this->emitTransforms(vertBuilder, varyingHandler, gp.inLocalCoords()->fName, args.fTransformsIn, args.fTransformsOut); } else { // emit transforms with position - this->emitTransforms(pb, - vertBuilder, + this->emitTransforms(vertBuilder, varyingHandler, + uniformHandler, gpArgs->fPositionVar, gp.inPosition()->fName, gp.localMatrix(), @@ -129,11 +129,12 @@ public: fragBuilder->codeAppendf("%s = vec4(1);", args.fOutputCoverage); } else { const char* fragCoverage; - fCoverageUniform = pb->addUniform(GrGLSLProgramBuilder::kFragment_Visibility, - kFloat_GrSLType, - kDefault_GrSLPrecision, - "Coverage", - &fragCoverage); + fCoverageUniform = uniformHandler->addUniform( + GrGLSLUniformHandler::kFragment_Visibility, + kFloat_GrSLType, + kDefault_GrSLPrecision, + "Coverage", + &fragCoverage); fragBuilder->codeAppendf("%s = vec4(%s);", args.fOutputCoverage, fragCoverage); } } diff --git a/gfx/skia/skia/src/gpu/GrDrawContext.cpp b/gfx/skia/skia/src/gpu/GrDrawContext.cpp index c07b0602ea..6161626501 100644 --- a/gfx/skia/skia/src/gpu/GrDrawContext.cpp +++ b/gfx/skia/skia/src/gpu/GrDrawContext.cpp @@ -6,7 +6,6 @@ * found in the LICENSE file. */ -#include "GrAtlasTextContext.h" #include "GrBatchTest.h" #include "GrColor.h" #include "GrDrawContext.h" @@ -16,7 +15,6 @@ #include "GrRenderTarget.h" #include "GrRenderTargetPriv.h" #include "GrResourceProvider.h" -#include "GrStencilAndCoverTextContext.h" #include "SkSurfacePriv.h" #include "batches/GrBatch.h" @@ -25,7 +23,14 @@ #include "batches/GrRectBatchFactory.h" #include "batches/GrNinePatch.h" // TODO Factory +#include "text/GrAtlasTextContext.h" +#include "text/GrStencilAndCoverTextContext.h" + +#include "../private/GrAuditTrail.h" + #define ASSERT_OWNED_RESOURCE(R) SkASSERT(!(R) || (R)->getContext() == fDrawingManager->getContext()) +#define ASSERT_SINGLE_OWNER \ + SkDEBUGCODE(GrSingleOwner::AutoEnforce debug_SingleOwner(fSingleOwner);) #define RETURN_IF_ABANDONED if (fDrawingManager->abandoned()) { return; } #define RETURN_FALSE_IF_ABANDONED if (fDrawingManager->abandoned()) { return false; } #define RETURN_NULL_IF_ABANDONED if (fDrawingManager->abandoned()) { return nullptr; } @@ -47,12 +52,19 @@ private: // when the drawContext attempts to use it (via getDrawTarget). GrDrawContext::GrDrawContext(GrDrawingManager* drawingMgr, GrRenderTarget* rt, - const SkSurfaceProps* surfaceProps) + const SkSurfaceProps* surfaceProps, + GrAuditTrail* auditTrail, + GrSingleOwner* singleOwner) : fDrawingManager(drawingMgr) , fRenderTarget(rt) , fDrawTarget(SkSafeRef(rt->getLastDrawTarget())) , fTextContext(nullptr) - , fSurfaceProps(SkSurfacePropsCopyOrDefault(surfaceProps)) { + , fSurfaceProps(SkSurfacePropsCopyOrDefault(surfaceProps)) + , fAuditTrail(auditTrail) +#ifdef SK_DEBUG + , fSingleOwner(singleOwner) +#endif +{ SkDEBUGCODE(this->validate();) } @@ -68,10 +80,12 @@ void GrDrawContext::validate() const { #endif GrDrawContext::~GrDrawContext() { + ASSERT_SINGLE_OWNER SkSafeUnref(fDrawTarget); } GrDrawTarget* GrDrawContext::getDrawTarget() { + ASSERT_SINGLE_OWNER SkDEBUGCODE(this->validate();) if (!fDrawTarget || fDrawTarget->isClosed()) { @@ -82,8 +96,10 @@ GrDrawTarget* GrDrawContext::getDrawTarget() { } void GrDrawContext::copySurface(GrSurface* src, const SkIRect& srcRect, const SkIPoint& dstPoint) { + ASSERT_SINGLE_OWNER RETURN_IF_ABANDONED SkDEBUGCODE(this->validate();) + GR_AUDIT_TRAIL_AUTO_FRAME(fAuditTrail, "GrDrawContext::copySurface"); this->getDrawTarget()->copySurface(fRenderTarget, src, srcRect, dstPoint); } @@ -93,14 +109,16 @@ void GrDrawContext::drawText(const GrClip& clip, const GrPaint& grPaint, const SkMatrix& viewMatrix, const char text[], size_t byteLength, SkScalar x, SkScalar y, const SkIRect& clipBounds) { + ASSERT_SINGLE_OWNER RETURN_IF_ABANDONED SkDEBUGCODE(this->validate();) + GR_AUDIT_TRAIL_AUTO_FRAME(fAuditTrail, "GrDrawContext::drawText"); if (!fTextContext) { fTextContext = fDrawingManager->textContext(fSurfaceProps, fRenderTarget); } - fTextContext->drawText(this, fRenderTarget, clip, grPaint, skPaint, viewMatrix, + fTextContext->drawText(this, clip, grPaint, skPaint, viewMatrix, text, byteLength, x, y, clipBounds); } @@ -110,14 +128,16 @@ void GrDrawContext::drawPosText(const GrClip& clip, const GrPaint& grPaint, const char text[], size_t byteLength, const SkScalar pos[], int scalarsPerPosition, const SkPoint& offset, const SkIRect& clipBounds) { + ASSERT_SINGLE_OWNER RETURN_IF_ABANDONED SkDEBUGCODE(this->validate();) + GR_AUDIT_TRAIL_AUTO_FRAME(fAuditTrail, "GrDrawContext::drawPosText"); if (!fTextContext) { fTextContext = fDrawingManager->textContext(fSurfaceProps, fRenderTarget); } - fTextContext->drawPosText(this, fRenderTarget, clip, grPaint, skPaint, viewMatrix, text, byteLength, + fTextContext->drawPosText(this, clip, grPaint, skPaint, viewMatrix, text, byteLength, pos, scalarsPerPosition, offset, clipBounds); } @@ -126,36 +146,23 @@ void GrDrawContext::drawTextBlob(const GrClip& clip, const SkPaint& skPaint, const SkMatrix& viewMatrix, const SkTextBlob* blob, SkScalar x, SkScalar y, SkDrawFilter* filter, const SkIRect& clipBounds) { + ASSERT_SINGLE_OWNER RETURN_IF_ABANDONED SkDEBUGCODE(this->validate();) + GR_AUDIT_TRAIL_AUTO_FRAME(fAuditTrail, "GrDrawContext::drawTextBlob"); if (!fTextContext) { fTextContext = fDrawingManager->textContext(fSurfaceProps, fRenderTarget); } - fTextContext->drawTextBlob(this, fRenderTarget, - clip, skPaint, viewMatrix, blob, x, y, filter, clipBounds); -} - -void GrDrawContext::drawPathsFromRange(const GrPipelineBuilder* pipelineBuilder, - const SkMatrix& viewMatrix, - const SkMatrix& localMatrix, - GrColor color, - GrPathRange* range, - GrPathRangeDraw* draw, - int /*GrPathRendering::FillType*/ fill, - const SkRect& bounds) { - RETURN_IF_ABANDONED - SkDEBUGCODE(this->validate();) - - this->getDrawTarget()->drawPathsFromRange(*pipelineBuilder, viewMatrix, localMatrix, color, - range, draw, (GrPathRendering::FillType) fill, - bounds); + fTextContext->drawTextBlob(this, clip, skPaint, viewMatrix, blob, x, y, filter, clipBounds); } void GrDrawContext::discard() { + ASSERT_SINGLE_OWNER RETURN_IF_ABANDONED SkDEBUGCODE(this->validate();) + GR_AUDIT_TRAIL_AUTO_FRAME(fAuditTrail, "GrDrawContext::discard"); AutoCheckFlush acf(fDrawingManager); this->getDrawTarget()->discard(fRenderTarget); @@ -164,8 +171,10 @@ void GrDrawContext::discard() { void GrDrawContext::clear(const SkIRect* rect, const GrColor color, bool canIgnoreRect) { + ASSERT_SINGLE_OWNER RETURN_IF_ABANDONED SkDEBUGCODE(this->validate();) + GR_AUDIT_TRAIL_AUTO_FRAME(fAuditTrail, "GrDrawContext::clear"); AutoCheckFlush acf(fDrawingManager); this->getDrawTarget()->clear(rect, color, canIgnoreRect, fRenderTarget); @@ -175,8 +184,10 @@ void GrDrawContext::clear(const SkIRect* rect, void GrDrawContext::drawPaint(const GrClip& clip, const GrPaint& origPaint, const SkMatrix& viewMatrix) { + ASSERT_SINGLE_OWNER RETURN_IF_ABANDONED SkDEBUGCODE(this->validate();) + GR_AUDIT_TRAIL_AUTO_FRAME(fAuditTrail, "GrDrawContext::drawPaint"); // set rect to be big enough to fill the space, but not super-huge, so we // don't overflow fixed-point implementations @@ -214,11 +225,10 @@ void GrDrawContext::drawPaint(const GrClip& clip, AutoCheckFlush acf(fDrawingManager); GrPipelineBuilder pipelineBuilder(*paint, fRenderTarget, clip); - this->getDrawTarget()->drawNonAARect(pipelineBuilder, - paint->getColor(), - SkMatrix::I(), - r, - localMatrix); + SkAutoTUnref batch( + GrRectBatchFactory::CreateNonAAFill(paint->getColor(), SkMatrix::I(), r, nullptr, + &localMatrix)); + this->getDrawTarget()->drawBatch(pipelineBuilder, batch); } } @@ -240,8 +250,10 @@ void GrDrawContext::drawRect(const GrClip& clip, const SkMatrix& viewMatrix, const SkRect& rect, const GrStrokeInfo* strokeInfo) { + ASSERT_SINGLE_OWNER RETURN_IF_ABANDONED SkDEBUGCODE(this->validate();) + GR_AUDIT_TRAIL_AUTO_FRAME(fAuditTrail, "GrDrawContext::drawRect"); // Dashing should've been devolved to a path in SkGpuDevice SkASSERT(!strokeInfo || !strokeInfo->isDashed()); @@ -284,47 +296,50 @@ void GrDrawContext::drawRect(const GrClip& clip, } } - GrColor color = paint.getColor(); - bool needAA = should_apply_coverage_aa(paint, fRenderTarget); - - // The fill path can handle rotation but not skew - // The stroke path needs the rect to remain axis aligned (no rotation or skew) - // None of our AA draw rect calls can handle perspective yet - bool canApplyAA = width >=0 ? viewMatrix.rectStaysRect() : - view_matrix_ok_for_aa_fill_rect(viewMatrix); - GrPipelineBuilder pipelineBuilder(paint, fRenderTarget, clip); + GrColor color = paint.getColor(); - if (needAA && canApplyAA) { - SkASSERT(!viewMatrix.hasPerspective()); - SkAutoTUnref batch; + SkAutoTUnref batch; + if (should_apply_coverage_aa(paint, fRenderTarget)) { if (width >= 0) { - batch.reset(GrRectBatchFactory::CreateAAStroke(color, viewMatrix, rect, *strokeInfo)); + // The stroke path needs the rect to remain axis aligned (no rotation or skew). + if (viewMatrix.rectStaysRect()) { + batch.reset(GrRectBatchFactory::CreateAAStroke(color, viewMatrix, rect, + *strokeInfo)); + } } else { - SkRect devBoundRect; - viewMatrix.mapRect(&devBoundRect, rect); - batch.reset(GrRectBatchFactory::CreateAAFill(color, viewMatrix, rect, devBoundRect)); + // The fill path can handle rotation but not skew. + if (view_matrix_ok_for_aa_fill_rect(viewMatrix)) { + SkRect devBoundRect; + viewMatrix.mapRect(&devBoundRect, rect); + batch.reset(GrRectBatchFactory::CreateAAFill(color, viewMatrix, rect, + devBoundRect)); + } } - this->getDrawTarget()->drawBatch(pipelineBuilder, batch); - return; - } - - if (width >= 0) { + if (!batch) { + SkPath path; + path.setIsVolatile(true); + path.addRect(rect); + this->internalDrawPath(&pipelineBuilder, viewMatrix, color, true, path, *strokeInfo); + SkASSERT(paint.isAntiAlias()); + return; + } + } else if (width >= 0) { // Non-AA hairlines are snapped to pixel centers to make which pixels are hit deterministic bool snapToPixelCenters = (0 == width && !fRenderTarget->isUnifiedMultisampled()); - SkAutoTUnref batch(GrRectBatchFactory::CreateNonAAStroke( - color, viewMatrix, rect, width, snapToPixelCenters)); + batch.reset(GrRectBatchFactory::CreateNonAAStroke(color, viewMatrix, rect, width, + snapToPixelCenters)); // Depending on sub-pixel coordinates and the particular GPU, we may lose a corner of // hairline rects. We jam all the vertices to pixel centers to avoid this, but not when MSAA // is enabled because it can cause ugly artifacts. pipelineBuilder.setState(GrPipelineBuilder::kSnapVerticesToPixelCenters_Flag, snapToPixelCenters); - this->getDrawTarget()->drawBatch(pipelineBuilder, batch); } else { // filled BW rect - this->getDrawTarget()->drawNonAARect(pipelineBuilder, color, viewMatrix, rect); + batch.reset(GrRectBatchFactory::CreateNonAAFill(color, viewMatrix, rect, nullptr, nullptr)); } + this->getDrawTarget()->drawBatch(pipelineBuilder, batch); } void GrDrawContext::fillRectToRect(const GrClip& clip, @@ -332,25 +347,26 @@ void GrDrawContext::fillRectToRect(const GrClip& clip, const SkMatrix& viewMatrix, const SkRect& rectToDraw, const SkRect& localRect) { + ASSERT_SINGLE_OWNER RETURN_IF_ABANDONED SkDEBUGCODE(this->validate();) + GR_AUDIT_TRAIL_AUTO_FRAME(fAuditTrail, "GrDrawContext::fillRectToRect"); AutoCheckFlush acf(fDrawingManager); GrPipelineBuilder pipelineBuilder(paint, fRenderTarget, clip); + SkAutoTUnref batch; if (should_apply_coverage_aa(paint, fRenderTarget) && view_matrix_ok_for_aa_fill_rect(viewMatrix)) { - SkAutoTUnref batch(GrAAFillRectBatch::CreateWithLocalRect( - paint.getColor(), viewMatrix, rectToDraw, localRect)); - if (batch) { - this->drawBatch(&pipelineBuilder, batch); - } + batch.reset(GrAAFillRectBatch::CreateWithLocalRect(paint.getColor(), viewMatrix, rectToDraw, + localRect)); } else { - this->getDrawTarget()->drawNonAARect(pipelineBuilder, - paint.getColor(), - viewMatrix, - rectToDraw, - localRect); + batch.reset(GrRectBatchFactory::CreateNonAAFill(paint.getColor(), viewMatrix, rectToDraw, + &localRect, nullptr)); + } + + if (batch) { + this->drawBatch(&pipelineBuilder, batch); } } @@ -359,25 +375,25 @@ void GrDrawContext::fillRectWithLocalMatrix(const GrClip& clip, const SkMatrix& viewMatrix, const SkRect& rectToDraw, const SkMatrix& localMatrix) { + ASSERT_SINGLE_OWNER RETURN_IF_ABANDONED SkDEBUGCODE(this->validate();) + GR_AUDIT_TRAIL_AUTO_FRAME(fAuditTrail, "GrDrawContext::fillRectWithLocalMatrix"); AutoCheckFlush acf(fDrawingManager); GrPipelineBuilder pipelineBuilder(paint, fRenderTarget, clip); + SkAutoTUnref batch; if (should_apply_coverage_aa(paint, fRenderTarget) && view_matrix_ok_for_aa_fill_rect(viewMatrix)) { - SkAutoTUnref batch(GrAAFillRectBatch::Create( - paint.getColor(), viewMatrix, localMatrix, rectToDraw)); - this->drawBatch(&pipelineBuilder, batch); + batch.reset(GrAAFillRectBatch::Create(paint.getColor(), viewMatrix, localMatrix, + rectToDraw)); } else { - this->getDrawTarget()->drawNonAARect(pipelineBuilder, - paint.getColor(), - viewMatrix, - rectToDraw, - localMatrix); + batch.reset(GrRectBatchFactory::CreateNonAAFill(paint.getColor(), viewMatrix, rectToDraw, + nullptr, &localMatrix)); } + this->getDrawTarget()->drawBatch(pipelineBuilder, batch); } void GrDrawContext::drawVertices(const GrClip& clip, @@ -390,8 +406,10 @@ void GrDrawContext::drawVertices(const GrClip& clip, const GrColor colors[], const uint16_t indices[], int indexCount) { + ASSERT_SINGLE_OWNER RETURN_IF_ABANDONED SkDEBUGCODE(this->validate();) + GR_AUDIT_TRAIL_AUTO_FRAME(fAuditTrail, "GrDrawContext::drawVertices"); AutoCheckFlush acf(fDrawingManager); @@ -433,8 +451,10 @@ void GrDrawContext::drawAtlas(const GrClip& clip, const SkRSXform xform[], const SkRect texRect[], const SkColor colors[]) { + ASSERT_SINGLE_OWNER RETURN_IF_ABANDONED SkDEBUGCODE(this->validate();) + GR_AUDIT_TRAIL_AUTO_FRAME(fAuditTrail, "GrDrawContext::drawAtlas"); AutoCheckFlush acf(fDrawingManager); @@ -455,8 +475,10 @@ void GrDrawContext::drawRRect(const GrClip& clip, const SkMatrix& viewMatrix, const SkRRect& rrect, const GrStrokeInfo& strokeInfo) { + ASSERT_SINGLE_OWNER RETURN_IF_ABANDONED SkDEBUGCODE(this->validate();) + GR_AUDIT_TRAIL_AUTO_FRAME(fAuditTrail, "GrDrawContext::drawRRect"); if (rrect.isEmpty()) { return; @@ -491,8 +513,10 @@ void GrDrawContext::drawDRRect(const GrClip& clip, const SkMatrix& viewMatrix, const SkRRect& outer, const SkRRect& inner) { + ASSERT_SINGLE_OWNER RETURN_IF_ABANDONED SkDEBUGCODE(this->validate();) + GR_AUDIT_TRAIL_AUTO_FRAME(fAuditTrail, "GrDrawContext::drawDRRect"); if (outer.isEmpty()) { return; @@ -528,8 +552,10 @@ void GrDrawContext::drawOval(const GrClip& clip, const SkMatrix& viewMatrix, const SkRect& oval, const GrStrokeInfo& strokeInfo) { + ASSERT_SINGLE_OWNER RETURN_IF_ABANDONED SkDEBUGCODE(this->validate();) + GR_AUDIT_TRAIL_AUTO_FRAME(fAuditTrail, "GrDrawContext::drawOval"); if (oval.isEmpty()) { return; @@ -564,8 +590,10 @@ void GrDrawContext::drawImageNine(const GrClip& clip, int imageHeight, const SkIRect& center, const SkRect& dst) { + ASSERT_SINGLE_OWNER RETURN_IF_ABANDONED SkDEBUGCODE(this->validate();) + GR_AUDIT_TRAIL_AUTO_FRAME(fAuditTrail, "GrDrawContext::drawImageNine"); AutoCheckFlush acf(fDrawingManager); @@ -630,8 +658,10 @@ static bool is_nested_rects(const SkMatrix& viewMatrix, void GrDrawContext::drawBatch(const GrClip& clip, const GrPaint& paint, GrDrawBatch* batch) { + ASSERT_SINGLE_OWNER RETURN_IF_ABANDONED SkDEBUGCODE(this->validate();) + GR_AUDIT_TRAIL_AUTO_FRAME(fAuditTrail, "GrDrawContext::drawBatch"); AutoCheckFlush acf(fDrawingManager); @@ -639,13 +669,27 @@ void GrDrawContext::drawBatch(const GrClip& clip, this->getDrawTarget()->drawBatch(pipelineBuilder, batch); } +void GrDrawContext::drawPathBatch(const GrPipelineBuilder& pipelineBuilder, + GrDrawPathBatchBase* batch) { + ASSERT_SINGLE_OWNER + RETURN_IF_ABANDONED + SkDEBUGCODE(this->validate();) + GR_AUDIT_TRAIL_AUTO_FRAME(fAuditTrail, "GrDrawContext::drawPathBatch"); + + AutoCheckFlush acf(fDrawingManager); + + this->getDrawTarget()->drawPathBatch(pipelineBuilder, batch); +} + void GrDrawContext::drawPath(const GrClip& clip, const GrPaint& paint, const SkMatrix& viewMatrix, const SkPath& path, const GrStrokeInfo& strokeInfo) { + ASSERT_SINGLE_OWNER RETURN_IF_ABANDONED SkDEBUGCODE(this->validate();) + GR_AUDIT_TRAIL_AUTO_FRAME(fAuditTrail, "GrDrawContext::drawPath"); if (path.isEmpty()) { if (path.isInverseFillType()) { @@ -703,6 +747,7 @@ void GrDrawContext::internalDrawPath(GrPipelineBuilder* pipelineBuilder, bool useAA, const SkPath& path, const GrStrokeInfo& strokeInfo) { + ASSERT_SINGLE_OWNER RETURN_IF_ABANDONED SkASSERT(!path.isEmpty()); @@ -799,8 +844,10 @@ void GrDrawContext::internalDrawPath(GrPipelineBuilder* pipelineBuilder, } void GrDrawContext::drawBatch(GrPipelineBuilder* pipelineBuilder, GrDrawBatch* batch) { + ASSERT_SINGLE_OWNER RETURN_IF_ABANDONED SkDEBUGCODE(this->validate();) + GR_AUDIT_TRAIL_AUTO_FRAME(fAuditTrail, "GrDrawContext::drawBatch"); this->getDrawTarget()->drawBatch(*pipelineBuilder, batch); } diff --git a/gfx/skia/skia/src/gpu/GrDrawTarget.cpp b/gfx/skia/skia/src/gpu/GrDrawTarget.cpp index 9b7fa31ab0..e2e188eb12 100644 --- a/gfx/skia/skia/src/gpu/GrDrawTarget.cpp +++ b/gfx/skia/skia/src/gpu/GrDrawTarget.cpp @@ -8,6 +8,7 @@ #include "GrDrawTarget.h" +#include "GrAuditTrail.h" #include "GrCaps.h" #include "GrGpu.h" #include "GrPath.h" @@ -20,6 +21,8 @@ #include "GrTexture.h" #include "GrVertexBuffer.h" +#include "SkStrokeRec.h" + #include "batches/GrClearBatch.h" #include "batches/GrCopySurfaceBatch.h" #include "batches/GrDiscardBatch.h" @@ -28,21 +31,26 @@ #include "batches/GrRectBatchFactory.h" #include "batches/GrStencilPathBatch.h" -#include "SkStrokeRec.h" - //////////////////////////////////////////////////////////////////////////////// +// Experimentally we have found that most batching occurs within the first 10 comparisons. +static const int kDefaultMaxBatchLookback = 10; + GrDrawTarget::GrDrawTarget(GrRenderTarget* rt, GrGpu* gpu, GrResourceProvider* resourceProvider, - const Options& options) + GrAuditTrail* auditTrail, const Options& options) : fGpu(SkRef(gpu)) , fResourceProvider(resourceProvider) - , fFlushing(false) + , fAuditTrail(auditTrail) , fFlags(0) , fRenderTarget(rt) { // TODO: Stop extracting the context (currently needed by GrClipMaskManager) fContext = fGpu->getContext(); fClipMaskManager.reset(new GrClipMaskManager(this, options.fClipBatchToBounds)); + fDrawBatchBounds = options.fDrawBatchBounds; + fMaxBatchLookback = (options.fMaxBatchLookback < 0) ? kDefaultMaxBatchLookback : + options.fMaxBatchLookback; + rt->setLastDrawTarget(this); #ifdef SK_DEBUG @@ -176,11 +184,6 @@ bool GrDrawTarget::setupDstReadIfNecessary(const GrPipelineBuilder& pipelineBuil } void GrDrawTarget::prepareBatches(GrBatchFlushState* flushState) { - if (fFlushing) { - return; - } - fFlushing = true; - // Semi-usually the drawTargets are already closed at this point, but sometimes Ganesh // needs to flush mid-draw. In that case, the SkGpuDevice's drawTargets won't be closed // but need to be flushed anyway. Closing such drawTargets here will mean new @@ -195,11 +198,20 @@ void GrDrawTarget::prepareBatches(GrBatchFlushState* flushState) { void GrDrawTarget::drawBatches(GrBatchFlushState* flushState) { // Draw all the generated geometry. + SkRandom random; for (int i = 0; i < fBatches.count(); ++i) { + if (fDrawBatchBounds) { + const SkRect& bounds = fBatches[i]->bounds(); + SkIRect ibounds; + bounds.roundOut(&ibounds); + // In multi-draw buffer all the batches use the same render target and we won't need to + // get the batchs bounds. + if (GrRenderTarget* rt = fBatches[i]->renderTarget()) { + fGpu->drawDebugWireRect(rt, ibounds, 0xFF000000 | random.nextU()); + } + } fBatches[i]->draw(flushState); } - - fFlushing = false; } void GrDrawTarget::reset() { @@ -303,40 +315,13 @@ void GrDrawTarget::stencilPath(const GrPipelineBuilder& pipelineBuilder, batch->unref(); } -void GrDrawTarget::drawPath(const GrPipelineBuilder& pipelineBuilder, - const SkMatrix& viewMatrix, - GrColor color, - const GrPath* path, - GrPathRendering::FillType fill) { - SkASSERT(path); - SkASSERT(this->caps()->shaderCaps()->pathRenderingSupport()); - - GrDrawPathBatchBase* batch = GrDrawPathBatch::Create(viewMatrix, color, path); - this->drawPathBatch(pipelineBuilder, batch, fill); - batch->unref(); -} - -void GrDrawTarget::drawPathsFromRange(const GrPipelineBuilder& pipelineBuilder, - const SkMatrix& viewMatrix, - const SkMatrix& localMatrix, - GrColor color, - GrPathRange* range, - GrPathRangeDraw* draw, - GrPathRendering::FillType fill, - const SkRect& bounds) { - GrDrawPathBatchBase* batch = GrDrawPathRangeBatch::Create(viewMatrix, localMatrix, color, - range, draw, bounds); - this->drawPathBatch(pipelineBuilder, batch, fill); - batch->unref(); -} - void GrDrawTarget::drawPathBatch(const GrPipelineBuilder& pipelineBuilder, - GrDrawPathBatchBase* batch, - GrPathRendering::FillType fill) { + GrDrawPathBatchBase* batch) { // This looks like drawBatch() but there is an added wrinkle that stencil settings get inserted // after setting up clipping but before onDrawBatch(). TODO: Figure out a better model for // handling stencil settings WRT interactions between pipeline(builder), clipmaskmanager, and // batches. + SkASSERT(this->caps()->shaderCaps()->pathRenderingSupport()); GrPipelineBuilder::AutoRestoreStencil ars; GrAppliedClip clip; @@ -354,7 +339,7 @@ void GrDrawTarget::drawPathBatch(const GrPipelineBuilder& pipelineBuilder, GrStencilSettings stencilSettings; GrRenderTarget* rt = pipelineBuilder.getRenderTarget(); GrStencilAttachment* sb = fResourceProvider->attachStencilAttachment(rt); - this->getPathStencilSettingsForFilltype(fill, sb, &stencilSettings); + this->getPathStencilSettingsForFilltype(batch->fillType(), sb, &stencilSettings); batch->setStencilSettings(stencilSettings); GrPipeline::CreateArgs args; @@ -365,46 +350,6 @@ void GrDrawTarget::drawPathBatch(const GrPipelineBuilder& pipelineBuilder, this->recordBatch(batch); } -void GrDrawTarget::drawNonAARect(const GrPipelineBuilder& pipelineBuilder, - GrColor color, - const SkMatrix& viewMatrix, - const SkRect& rect) { - SkAutoTUnref batch(GrRectBatchFactory::CreateNonAAFill(color, viewMatrix, rect, - nullptr, nullptr)); - this->drawBatch(pipelineBuilder, batch); -} - -void GrDrawTarget::drawNonAARect(const GrPipelineBuilder& pipelineBuilder, - GrColor color, - const SkMatrix& viewMatrix, - const SkRect& rect, - const SkMatrix& localMatrix) { - SkAutoTUnref batch(GrRectBatchFactory::CreateNonAAFill(color, viewMatrix, rect, - nullptr, &localMatrix)); - this->drawBatch(pipelineBuilder, batch); -} - -void GrDrawTarget::drawNonAARect(const GrPipelineBuilder& pipelineBuilder, - GrColor color, - const SkMatrix& viewMatrix, - const SkRect& rect, - const SkRect& localRect) { - SkAutoTUnref batch(GrRectBatchFactory::CreateNonAAFill(color, viewMatrix, rect, - &localRect, nullptr)); - this->drawBatch(pipelineBuilder, batch); -} - - -void GrDrawTarget::drawAARect(const GrPipelineBuilder& pipelineBuilder, - GrColor color, - const SkMatrix& viewMatrix, - const SkRect& rect, - const SkRect& devRect) { - SkAutoTUnref batch(GrRectBatchFactory::CreateAAFill(color, viewMatrix, rect, - devRect)); - this->drawBatch(pipelineBuilder, batch); -} - void GrDrawTarget::clear(const SkIRect* rect, GrColor color, bool canIgnoreRect, @@ -436,7 +381,11 @@ void GrDrawTarget::clear(const SkIRect* rect, GrPorterDuffXPFactory::Create(SkXfermode::kSrc_Mode))->unref(); pipelineBuilder.setRenderTarget(renderTarget); - this->drawNonAARect(pipelineBuilder, color, SkMatrix::I(), *rect); + SkRect scalarRect = SkRect::Make(*rect); + SkAutoTUnref batch( + GrRectBatchFactory::CreateNonAAFill(color, SkMatrix::I(), scalarRect, + nullptr, nullptr)); + this->drawBatch(pipelineBuilder, batch); } else { GrBatch* batch = new GrClearBatch(*rect, color, renderTarget); this->recordBatch(batch); @@ -483,9 +432,7 @@ void GrDrawTarget::recordBatch(GrBatch* batch) { // 1) check every draw // 2) intersect with something // 3) find a 'blocker' - // Experimentally we have found that most batching occurs within the first 10 comparisons. - static const int kMaxLookback = 10; - + GR_AUDIT_TRAIL_ADDBATCH(fAuditTrail, batch->name(), batch->bounds()); GrBATCH_INFO("Re-Recording (%s, B%u)\n" "\tBounds LRTB (%f, %f, %f, %f)\n", batch->name(), @@ -494,7 +441,7 @@ void GrDrawTarget::recordBatch(GrBatch* batch) { batch->bounds().fTop, batch->bounds().fBottom); GrBATCH_INFO(SkTabString(batch->dumpInfo(), 1).c_str()); GrBATCH_INFO("\tOutcome:\n"); - int maxCandidates = SkTMin(kMaxLookback, fBatches.count()); + int maxCandidates = SkTMin(fMaxBatchLookback, fBatches.count()); if (maxCandidates) { int i = 0; while (true) { diff --git a/gfx/skia/skia/src/gpu/GrDrawTarget.h b/gfx/skia/skia/src/gpu/GrDrawTarget.h index 7b0159e5d5..55c11da667 100644 --- a/gfx/skia/skia/src/gpu/GrDrawTarget.h +++ b/gfx/skia/skia/src/gpu/GrDrawTarget.h @@ -34,22 +34,24 @@ //#define ENABLE_MDB 1 +class GrAuditTrail; class GrBatch; class GrClip; class GrCaps; class GrPath; class GrDrawPathBatchBase; -class GrPathRangeDraw; class GrDrawTarget final : public SkRefCnt { public: /** Options for GrDrawTarget behavior. */ struct Options { - Options () : fClipBatchToBounds(false) {} + Options () : fClipBatchToBounds(false), fDrawBatchBounds(false), fMaxBatchLookback(-1) {} bool fClipBatchToBounds; + bool fDrawBatchBounds; + int fMaxBatchLookback; }; - GrDrawTarget(GrRenderTarget*, GrGpu*, GrResourceProvider*, const Options&); + GrDrawTarget(GrRenderTarget*, GrGpu*, GrResourceProvider*, GrAuditTrail*, const Options&); ~GrDrawTarget() override; @@ -111,76 +113,13 @@ public: GrPathRendering::FillType); /** - * Draws a path. Fill must not be a hairline. It will respect the HW - * antialias flag on the GrPipelineBuilder (if possible in the 3D API). + * Draws a path batch. Fill must not be a hairline. It will respect the HW antialias flag on + * the GrPipelineBuilder (if possible in the 3D API). This needs to be separate from drawBatch + * because we install path stencil settings late. * - * TODO: Remove this function and construct the batch outside GrDrawTarget. + * TODO: Figure out a better model that allows us to roll this method into drawBatch. */ - void drawPath(const GrPipelineBuilder&, const SkMatrix& viewMatrix, GrColor color, - const GrPath*, GrPathRendering::FillType); - - /** - * Draws the aggregate path from combining multiple. Note that this will not - * always be equivalent to back-to-back calls to drawPath(). It will respect - * the HW antialias flag on the GrPipelineBuilder (if possible in the 3D API). - * - * TODO: Remove this function and construct the batch outside GrDrawTarget. - * - * @param draw The transforms and indices for the draw. - * This object must only be drawn once. The draw - * may modify its contents. - * @param fill Fill type for drawing all the paths - */ - void drawPathsFromRange(const GrPipelineBuilder&, - const SkMatrix& viewMatrix, - const SkMatrix& localMatrix, - GrColor color, - GrPathRange* range, - GrPathRangeDraw* draw, - GrPathRendering::FillType fill, - const SkRect& bounds); - - /** - * Helper function for drawing rects. - * - * @param rect the rect to draw - * @param localRect optional rect that specifies local coords to map onto - * rect. If nullptr then rect serves as the local coords. - * @param localMatrix Optional local matrix. The local coordinates are specified by localRect, - * or if it is nullptr by rect. This matrix applies to the coordinate implied by - * that rectangle before it is input to GrCoordTransforms that read local - * coordinates - */ - void drawNonAARect(const GrPipelineBuilder& pipelineBuilder, - GrColor color, - const SkMatrix& viewMatrix, - const SkRect& rect); - - void drawNonAARect(const GrPipelineBuilder& pipelineBuilder, - GrColor color, - const SkMatrix& viewMatrix, - const SkRect& rect, - const SkMatrix& localMatrix); - - void drawNonAARect(const GrPipelineBuilder& pipelineBuilder, - GrColor color, - const SkMatrix& viewMatrix, - const SkRect& rect, - const SkRect& localRect); - - void drawNonAARect(const GrPipelineBuilder& ds, - GrColor color, - const SkMatrix& viewM, - const SkIRect& irect) { - SkRect rect = SkRect::Make(irect); - this->drawNonAARect(ds, color, viewM, rect); - } - - void drawAARect(const GrPipelineBuilder& pipelineBuilder, - GrColor color, - const SkMatrix& viewMatrix, - const SkRect& rect, - const SkRect& devRect); + void drawPathBatch(const GrPipelineBuilder& pipelineBuilder, GrDrawPathBatchBase* batch); /** * Clear the passed in render target. Ignores the GrPipelineBuilder and clip. Clears the whole @@ -228,6 +167,8 @@ public: const CMMAccess cmmAccess() { return CMMAccess(this); } + GrAuditTrail* getAuditTrail() const { return fAuditTrail; } + private: friend class GrDrawingManager; // for resetFlag & TopoSortTraits @@ -287,8 +228,6 @@ private: GrXferProcessor::DstTexture*, const SkRect& batchBounds); - void drawPathBatch(const GrPipelineBuilder& pipelineBuilder, GrDrawPathBatchBase* batch, - GrPathRendering::FillType fill); // Check to see if this set of draw commands has been sent out void getPathStencilSettingsForFilltype(GrPathRendering::FillType, const GrStencilAttachment*, @@ -310,7 +249,7 @@ private: GrContext* fContext; GrGpu* fGpu; GrResourceProvider* fResourceProvider; - bool fFlushing; + GrAuditTrail* fAuditTrail; SkDEBUGCODE(int fDebugID;) uint32_t fFlags; @@ -319,6 +258,9 @@ private: SkTDArray fDependencies; GrRenderTarget* fRenderTarget; + bool fDrawBatchBounds; + int fMaxBatchLookback; + typedef SkRefCnt INHERITED; }; diff --git a/gfx/skia/skia/src/gpu/GrDrawingManager.cpp b/gfx/skia/skia/src/gpu/GrDrawingManager.cpp index 13167d4642..90c77a5af0 100644 --- a/gfx/skia/skia/src/gpu/GrDrawingManager.cpp +++ b/gfx/skia/skia/src/gpu/GrDrawingManager.cpp @@ -5,21 +5,24 @@ * found in the LICENSE file. */ -#include "GrAtlasTextContext.h" #include "GrDrawContext.h" #include "GrDrawingManager.h" #include "GrDrawTarget.h" #include "GrResourceProvider.h" #include "GrSoftwarePathRenderer.h" -#include "GrStencilAndCoverTextContext.h" #include "SkTTopoSort.h" +#include "text/GrAtlasTextContext.h" +#include "text/GrStencilAndCoverTextContext.h" void GrDrawingManager::cleanup() { for (int i = 0; i < fDrawTargets.count(); ++i) { fDrawTargets[i]->makeClosed(); // no drawTarget should receive a new command after this fDrawTargets[i]->clearRT(); + // We shouldn't need to do this, but it turns out some clients still hold onto drawtargets + // after a cleanup + fDrawTargets[i]->reset(); fDrawTargets[i]->unref(); } @@ -64,6 +67,11 @@ void GrDrawingManager::reset() { } void GrDrawingManager::flush() { + if (fFlushing) { + return; + } + fFlushing = true; + SkDEBUGCODE(bool result =) SkTTopoSort(&fDrawTargets); SkASSERT(result); @@ -107,6 +115,7 @@ void GrDrawingManager::flush() { #endif fFlushState.reset(); + fFlushing = false; } GrTextContext* GrDrawingManager::textContext(const SkSurfaceProps& props, @@ -153,7 +162,7 @@ GrDrawTarget* GrDrawingManager::newDrawTarget(GrRenderTarget* rt) { #endif GrDrawTarget* dt = new GrDrawTarget(rt, fContext->getGpu(), fContext->resourceProvider(), - fOptionsForDrawTargets); + fContext->getAuditTrail(), fOptionsForDrawTargets); *fDrawTargets.append() = dt; @@ -193,5 +202,5 @@ GrDrawContext* GrDrawingManager::drawContext(GrRenderTarget* rt, return nullptr; } - return new GrDrawContext(this, rt, surfaceProps); + return new GrDrawContext(this, rt, surfaceProps, fContext->getAuditTrail(), fSingleOwner); } diff --git a/gfx/skia/skia/src/gpu/GrDrawingManager.h b/gfx/skia/skia/src/gpu/GrDrawingManager.h index 672f7b08da..df6c71a322 100644 --- a/gfx/skia/skia/src/gpu/GrDrawingManager.h +++ b/gfx/skia/skia/src/gpu/GrDrawingManager.h @@ -16,6 +16,7 @@ class GrContext; class GrDrawContext; +class GrSingleOWner; class GrSoftwarePathRenderer; class GrTextContext; @@ -53,14 +54,17 @@ public: static bool ProgramUnitTest(GrContext* context, int maxStages); private: - GrDrawingManager(GrContext* context, const GrDrawTarget::Options& optionsForDrawTargets) + GrDrawingManager(GrContext* context, const GrDrawTarget::Options& optionsForDrawTargets, + GrSingleOwner* singleOwner) : fContext(context) , fOptionsForDrawTargets(optionsForDrawTargets) + , fSingleOwner(singleOwner) , fAbandoned(false) , fNVPRTextContext(nullptr) , fPathRendererChain(nullptr) , fSoftwarePathRenderer(nullptr) - , fFlushState(context->getGpu(), context->resourceProvider()) { + , fFlushState(context->getGpu(), context->resourceProvider()) + , fFlushing(false) { sk_bzero(fTextContexts, sizeof(fTextContexts)); } @@ -77,6 +81,9 @@ private: GrContext* fContext; GrDrawTarget::Options fOptionsForDrawTargets; + // In debug builds we guard against improper thread handling + GrSingleOwner* fSingleOwner; + bool fAbandoned; SkTDArray fDrawTargets; @@ -87,6 +94,7 @@ private: GrSoftwarePathRenderer* fSoftwarePathRenderer; GrBatchFlushState fFlushState; + bool fFlushing; }; #endif diff --git a/gfx/skia/skia/src/gpu/GrFragmentProcessor.cpp b/gfx/skia/skia/src/gpu/GrFragmentProcessor.cpp index 4cf2e73099..f05b7aa084 100644 --- a/gfx/skia/skia/src/gpu/GrFragmentProcessor.cpp +++ b/gfx/skia/skia/src/gpu/GrFragmentProcessor.cpp @@ -8,10 +8,12 @@ #include "GrFragmentProcessor.h" #include "GrCoordTransform.h" +#include "GrInvariantOutput.h" +#include "GrProcOptInfo.h" #include "glsl/GrGLSLFragmentProcessor.h" #include "glsl/GrGLSLFragmentShaderBuilder.h" -#include "glsl/GrGLSLProgramBuilder.h" #include "glsl/GrGLSLProgramDataManager.h" +#include "glsl/GrGLSLUniformHandler.h" #include "effects/GrConstColorProcessor.h" #include "effects/GrXfermodeFragmentProcessor.h" @@ -226,9 +228,10 @@ const GrFragmentProcessor* GrFragmentProcessor::OverrideInput(const GrFragmentPr GLFP() : fHaveSetColor(false) {} void emitCode(EmitArgs& args) override { const char* colorName; - fColorUni = args.fBuilder->addUniform(GrGLSLProgramBuilder::kFragment_Visibility, - kVec4f_GrSLType, kDefault_GrSLPrecision, - "Color", &colorName); + fColorUni = args.fUniformHandler->addUniform( + GrGLSLUniformHandler::kFragment_Visibility, + kVec4f_GrSLType, kDefault_GrSLPrecision, + "Color", &colorName); this->emitChild(0, colorName, args); } diff --git a/gfx/skia/skia/src/gpu/GrGpu.cpp b/gfx/skia/skia/src/gpu/GrGpu.cpp index 439fe1b70f..2815f612d6 100644 --- a/gfx/skia/skia/src/gpu/GrGpu.cpp +++ b/gfx/skia/skia/src/gpu/GrGpu.cpp @@ -20,6 +20,7 @@ #include "GrRenderTargetPriv.h" #include "GrStencilAttachment.h" #include "GrSurfacePriv.h" +#include "GrTransferBuffer.h" #include "GrVertexBuffer.h" #include "GrVertices.h" @@ -159,6 +160,13 @@ GrTexture* GrGpu::createTexture(const GrSurfaceDesc& origDesc, bool budgeted, GrTexture* GrGpu::wrapBackendTexture(const GrBackendTextureDesc& desc, GrWrapOwnership ownership) { this->handleDirtyContext(); + if (!this->caps()->isConfigTexturable(desc.fConfig)) { + return nullptr; + } + if ((desc.fFlags & kRenderTarget_GrBackendTextureFlag) && + !this->caps()->isConfigRenderable(desc.fConfig, desc.fSampleCnt > 0)) { + return nullptr; + } GrTexture* tex = this->onWrapBackendTexture(desc, ownership); if (nullptr == tex) { return nullptr; @@ -175,6 +183,9 @@ GrTexture* GrGpu::wrapBackendTexture(const GrBackendTextureDesc& desc, GrWrapOwn GrRenderTarget* GrGpu::wrapBackendRenderTarget(const GrBackendRenderTargetDesc& desc, GrWrapOwnership ownership) { + if (!this->caps()->isConfigRenderable(desc.fConfig, desc.fSampleCnt > 0)) { + return nullptr; + } this->handleDirtyContext(); return this->onWrapBackendRenderTarget(desc, ownership); } @@ -197,6 +208,12 @@ GrIndexBuffer* GrGpu::createIndexBuffer(size_t size, bool dynamic) { return ib; } +GrTransferBuffer* GrGpu::createTransferBuffer(size_t size, TransferType type) { + this->handleDirtyContext(); + GrTransferBuffer* tb = this->onCreateTransferBuffer(size, type); + return tb; +} + void GrGpu::clear(const SkIRect& rect, GrColor color, GrRenderTarget* renderTarget) { @@ -317,7 +334,7 @@ bool GrGpu::writePixels(GrSurface* surface, int left, int top, int width, int height, GrPixelConfig config, const void* buffer, size_t rowBytes) { - if (!buffer) { + if (!buffer || !surface) { return false; } @@ -329,6 +346,21 @@ bool GrGpu::writePixels(GrSurface* surface, return false; } +bool GrGpu::transferPixels(GrSurface* surface, + int left, int top, int width, int height, + GrPixelConfig config, GrTransferBuffer* buffer, + size_t offset, size_t rowBytes) { + SkASSERT(buffer); + + this->handleDirtyContext(); + if (this->onTransferPixels(surface, left, top, width, height, config, + buffer, offset, rowBytes)) { + fStats.incTransfersToTexture(); + return true; + } + return false; +} + void GrGpu::resolveRenderTarget(GrRenderTarget* target) { SkASSERT(target); this->handleDirtyContext(); diff --git a/gfx/skia/skia/src/gpu/GrGpu.h b/gfx/skia/skia/src/gpu/GrGpu.h index aeaa595f8b..3eaa3fc035 100644 --- a/gfx/skia/skia/src/gpu/GrGpu.h +++ b/gfx/skia/skia/src/gpu/GrGpu.h @@ -31,6 +31,7 @@ class GrRenderTarget; class GrStencilAttachment; class GrSurface; class GrTexture; +class GrTransferBuffer; class GrVertexBuffer; class GrVertices; @@ -128,6 +129,17 @@ public: */ GrIndexBuffer* createIndexBuffer(size_t size, bool dynamic); + /** + * Creates a transfer buffer. + * + * @param size size in bytes of the index buffer + * @param toGpu true if used to transfer from the cpu to the gpu + * otherwise to be used to transfer from the gpu to the cpu + * + * @return The transfer buffer if successful, otherwise nullptr. + */ + GrTransferBuffer* createTransferBuffer(size_t size, TransferType type); + /** * Resolves MSAA. */ @@ -248,6 +260,25 @@ public: GrPixelConfig config, const void* buffer, size_t rowBytes); + /** + * Updates the pixels in a rectangle of a surface using a GrTransferBuffer + * + * @param surface The surface to write to. + * @param left left edge of the rectangle to write (inclusive) + * @param top top edge of the rectangle to write (inclusive) + * @param width width of rectangle to write in pixels. + * @param height height of rectangle to write in pixels. + * @param config the pixel config of the source buffer + * @param buffer GrTransferBuffer to read pixels from + * @param offset offset from the start of the buffer + * @param rowBytes number of bytes between consecutive rows. Zero + * means rows are tightly packed. + */ + bool transferPixels(GrSurface* surface, + int left, int top, int width, int height, + GrPixelConfig config, GrTransferBuffer* buffer, + size_t offset, size_t rowBytes); + /** * Clear the passed in render target. Ignores the draw state and clip. */ @@ -327,6 +358,7 @@ public: fShaderCompilations = 0; fTextureCreates = 0; fTextureUploads = 0; + fTransfersToTexture = 0; fStencilAttachmentCreates = 0; fNumDraws = 0; } @@ -339,6 +371,8 @@ public: void incTextureCreates() { fTextureCreates++; } int textureUploads() const { return fTextureUploads; } void incTextureUploads() { fTextureUploads++; } + int transfersToTexture() const { return fTransfersToTexture; } + void incTransfersToTexture() { fTransfersToTexture++; } void incStencilAttachmentCreates() { fStencilAttachmentCreates++; } void incNumDraws() { fNumDraws++; } void dump(SkString*); @@ -349,6 +383,7 @@ public: int fShaderCompilations; int fTextureCreates; int fTextureUploads; + int fTransfersToTexture; int fStencilAttachmentCreates; int fNumDraws; #else @@ -358,6 +393,7 @@ public: void incShaderCompilations() {} void incTextureCreates() {} void incTextureUploads() {} + void incTransfersToTexture() {} void incStencilAttachmentCreates() {} void incNumDraws() {} #endif @@ -387,6 +423,8 @@ public: // clears target's entire stencil buffer to 0 virtual void clearStencil(GrRenderTarget* target) = 0; + // draws an outline rectangle for debugging/visualization purposes. + virtual void drawDebugWireRect(GrRenderTarget*, const SkIRect&, GrColor) = 0; // Determines whether a copy of a texture must be made in order to be compatible with // a given GrTextureParams. If so, the width, height and filter used for the copy are @@ -455,6 +493,7 @@ private: GrWrapOwnership) = 0; virtual GrVertexBuffer* onCreateVertexBuffer(size_t size, bool dynamic) = 0; virtual GrIndexBuffer* onCreateIndexBuffer(size_t size, bool dynamic) = 0; + virtual GrTransferBuffer* onCreateTransferBuffer(size_t size, TransferType type) = 0; // overridden by backend-specific derived class to perform the clear. virtual void onClear(GrRenderTarget*, const SkIRect& rect, GrColor color) = 0; @@ -488,6 +527,12 @@ private: GrPixelConfig config, const void* buffer, size_t rowBytes) = 0; + // overridden by backend-specific derived class to perform the surface write + virtual bool onTransferPixels(GrSurface*, + int left, int top, int width, int height, + GrPixelConfig config, GrTransferBuffer* buffer, + size_t offset, size_t rowBytes) = 0; + // overridden by backend-specific derived class to perform the resolve virtual void onResolveRenderTarget(GrRenderTarget* target) = 0; diff --git a/gfx/skia/skia/src/gpu/GrImageIDTextureAdjuster.cpp b/gfx/skia/skia/src/gpu/GrImageIDTextureAdjuster.cpp index c37c022775..2edff60553 100644 --- a/gfx/skia/skia/src/gpu/GrImageIDTextureAdjuster.cpp +++ b/gfx/skia/skia/src/gpu/GrImageIDTextureAdjuster.cpp @@ -12,10 +12,15 @@ #include "SkBitmap.h" #include "SkGrPriv.h" #include "SkImage_Base.h" +#include "SkImageCacherator.h" #include "SkPixelRef.h" +static bool bmp_is_alpha_only(const SkBitmap& bm) { return kAlpha_8_SkColorType == bm.colorType(); } + GrBitmapTextureAdjuster::GrBitmapTextureAdjuster(const SkBitmap* bmp) - : INHERITED(bmp->getTexture(), SkIRect::MakeWH(bmp->width(), bmp->height())) + : INHERITED(bmp->getTexture(), + SkIRect::MakeWH(bmp->width(), bmp->height()), + bmp_is_alpha_only(*bmp)) , fBmp(bmp) {} void GrBitmapTextureAdjuster::makeCopyKey(const CopyParams& params, GrUniqueKey* copyKey) { @@ -38,8 +43,15 @@ void GrBitmapTextureAdjuster::didCacheCopy(const GrUniqueKey& copyKey) { ////////////////////////////////////////////////////////////////////////////// +// SkImage's don't have a way of communicating whether they're alpha-only. So we fallback to +// inspecting the texture. +static bool tex_image_is_alpha_only(const SkImage_Base& img) { + return GrPixelConfigIsAlphaOnly(img.peekTexture()->config()); +} + GrImageTextureAdjuster::GrImageTextureAdjuster(const SkImage_Base* img) - : INHERITED(img->peekTexture(), SkIRect::MakeWH(img->width(), img->height())) + : INHERITED(img->peekTexture(), SkIRect::MakeWH(img->width(), img->height()), + tex_image_is_alpha_only(*img)) , fImageBase(img) {} void GrImageTextureAdjuster::makeCopyKey(const CopyParams& params, GrUniqueKey* copyKey) { @@ -58,7 +70,7 @@ void GrImageTextureAdjuster::didCacheCopy(const GrUniqueKey& copyKey) { ////////////////////////////////////////////////////////////////////////////// GrBitmapTextureMaker::GrBitmapTextureMaker(GrContext* context, const SkBitmap& bitmap) - : INHERITED(context, bitmap.width(), bitmap.height()) + : INHERITED(context, bitmap.width(), bitmap.height(), bmp_is_alpha_only(bitmap)) , fBitmap(bitmap) { SkASSERT(!bitmap.getTexture()); if (!bitmap.isVolatile()) { @@ -96,3 +108,36 @@ void GrBitmapTextureMaker::makeCopyKey(const CopyParams& copyParams, GrUniqueKey void GrBitmapTextureMaker::didCacheCopy(const GrUniqueKey& copyKey) { GrInstallBitmapUniqueKeyInvalidator(copyKey, fBitmap.pixelRef()); } + +////////////////////////////////////////////////////////////////////////////// +static bool cacher_is_alpha_only(const SkImageCacherator& cacher) { + return kAlpha_8_SkColorType == cacher.info().colorType(); +} +GrImageTextureMaker::GrImageTextureMaker(GrContext* context, SkImageCacherator* cacher, + const SkImage* client, SkImage::CachingHint chint) + : INHERITED(context, cacher->info().width(), cacher->info().height(), + cacher_is_alpha_only(*cacher)) + , fCacher(cacher) + , fClient(client) + , fCachingHint(chint) { + if (client) { + GrMakeKeyFromImageID(&fOriginalKey, client->uniqueID(), + SkIRect::MakeWH(this->width(), this->height())); + } +} + +GrTexture* GrImageTextureMaker::refOriginalTexture() { + return fCacher->lockTexture(this->context(), fOriginalKey, fClient, fCachingHint); +} + +void GrImageTextureMaker::makeCopyKey(const CopyParams& stretch, GrUniqueKey* paramsCopyKey) { + if (fOriginalKey.isValid() && SkImage::kAllow_CachingHint == fCachingHint) { + MakeCopyKeyFromOrigKey(fOriginalKey, stretch, paramsCopyKey); + } +} + +void GrImageTextureMaker::didCacheCopy(const GrUniqueKey& copyKey) { + if (fClient) { + as_IB(fClient)->notifyAddedToCache(); + } +} diff --git a/gfx/skia/skia/src/gpu/GrImageIDTextureAdjuster.h b/gfx/skia/skia/src/gpu/GrImageIDTextureAdjuster.h index a20902ed58..d8510feeaa 100644 --- a/gfx/skia/skia/src/gpu/GrImageIDTextureAdjuster.h +++ b/gfx/skia/skia/src/gpu/GrImageIDTextureAdjuster.h @@ -9,9 +9,11 @@ #define GrImageIDTextureAdjuster_DEFINED #include "GrTextureParamsAdjuster.h" +#include "SkImage.h" class SkBitmap; class SkImage_Base; +class SkImageCacherator; /** Implementation for texture-backed SkBitmaps. The bitmap must stay in scope and unmodified while this object exists. */ @@ -66,4 +68,29 @@ private: typedef GrTextureMaker INHERITED; }; +/** This class manages the conversion of generator-backed images to GrTextures. If the caching hint + is kAllow the image's ID is used for the cache key. */ +class GrImageTextureMaker : public GrTextureMaker { +public: + GrImageTextureMaker(GrContext* context, SkImageCacherator* cacher, const SkImage* client, + SkImage::CachingHint chint); + +protected: + // TODO: consider overriding this, for the case where the underlying generator might be + // able to efficiently produce a "stretched" texture natively (e.g. picture-backed) + // GrTexture* generateTextureForParams(const CopyParams&) override; + + GrTexture* refOriginalTexture() override; + void makeCopyKey(const CopyParams& stretch, GrUniqueKey* paramsCopyKey) override; + void didCacheCopy(const GrUniqueKey& copyKey) override; + +private: + SkImageCacherator* fCacher; + const SkImage* fClient; + GrUniqueKey fOriginalKey; + SkImage::CachingHint fCachingHint; + + typedef GrTextureMaker INHERITED; +}; + #endif diff --git a/gfx/skia/skia/src/gpu/GrLayerHoister.cpp b/gfx/skia/skia/src/gpu/GrLayerHoister.cpp index 004e4d0e6e..842ab56dfb 100644 --- a/gfx/skia/skia/src/gpu/GrLayerHoister.cpp +++ b/gfx/skia/skia/src/gpu/GrLayerHoister.cpp @@ -12,7 +12,6 @@ #include "SkBigPicture.h" #include "SkCanvas.h" #include "SkGpuDevice.h" -#include "SkGrPixelRef.h" #include "SkLayerInfo.h" #include "SkRecordDraw.h" #include "SkSurface.h" @@ -277,15 +276,6 @@ void GrLayerHoister::DrawLayersToAtlas(GrContext* context, } } -SkBitmap wrap_texture(GrTexture* texture) { - SkASSERT(texture); - - SkBitmap result; - result.setInfo(texture->surfacePriv().info(kPremul_SkAlphaType)); - result.setPixelRef(new SkGrPixelRef(result.info(), texture))->unref(); - return result; -} - void GrLayerHoister::FilterLayer(GrContext* context, SkGpuDevice* device, const GrHoistedLayer& info) { @@ -311,10 +301,12 @@ void GrLayerHoister::FilterLayer(GrContext* context, // This cache is transient, and is freed (along with all its contained // textures) when it goes out of scope. SkAutoTUnref cache(SkImageFilter::Cache::Create(kDefaultCacheSize)); - SkImageFilter::Context filterContext(totMat, clipBounds, cache, SkImageFilter::kApprox_SizeConstraint); + SkImageFilter::Context filterContext(totMat, clipBounds, cache); SkImageFilter::DeviceProxy proxy(device); - const SkBitmap src = wrap_texture(layer->texture()); + SkBitmap src; + GrWrapTextureInBitmap(layer->texture(), layer->texture()->width(), layer->texture()->height(), + false, &src); if (!layer->filter()->filterImage(&proxy, src, filterContext, &filteredBitmap, &offset)) { // Filtering failed. Press on with the unfiltered version. diff --git a/gfx/skia/skia/src/gpu/GrMemoryPool.cpp b/gfx/skia/skia/src/gpu/GrMemoryPool.cpp index cc26452c19..adf46846c8 100644 --- a/gfx/skia/skia/src/gpu/GrMemoryPool.cpp +++ b/gfx/skia/skia/src/gpu/GrMemoryPool.cpp @@ -21,7 +21,7 @@ GrMemoryPool::GrMemoryPool(size_t preallocSize, size_t minAllocSize) { fMinAllocSize = GrSizeAlignUp(minAllocSize + kPerAllocPad, kAlignment), fPreallocSize = GrSizeAlignUp(preallocSize + kPerAllocPad, kAlignment); fPreallocSize = SkTMax(fPreallocSize, fMinAllocSize); - fSize = fPreallocSize; + fSize = 0; fHead = CreateBlock(fPreallocSize); fTail = fHead; @@ -40,8 +40,8 @@ GrMemoryPool::~GrMemoryPool() { void* GrMemoryPool::allocate(size_t size) { VALIDATE; - size = GrSizeAlignUp(size, kAlignment); size += kPerAllocPad; + size = GrSizeAlignUp(size, kAlignment); if (fTail->fFreeSize < size) { size_t blockSize = size; blockSize = SkTMax(blockSize, fMinAllocSize); @@ -55,11 +55,14 @@ void* GrMemoryPool::allocate(size_t size) { fSize += block->fSize; SkDEBUGCODE(++fAllocBlockCnt); } + SkASSERT(kAssignedMarker == fTail->fBlockSentinal); SkASSERT(fTail->fFreeSize >= size); intptr_t ptr = fTail->fCurrPtr; // We stash a pointer to the block header, just before the allocated space, // so that we can decrement the live count on delete in constant time. - *reinterpret_cast(ptr) = fTail; + AllocHeader* allocData = reinterpret_cast(ptr); + SkDEBUGCODE(allocData->fSentinal = kAssignedMarker); + allocData->fHeader = fTail; ptr += kPerAllocPad; fTail->fPrevPtr = fTail->fCurrPtr; fTail->fCurrPtr += size; @@ -74,7 +77,11 @@ void* GrMemoryPool::allocate(size_t size) { void GrMemoryPool::release(void* p) { VALIDATE; intptr_t ptr = reinterpret_cast(p) - kPerAllocPad; - BlockHeader* block = *reinterpret_cast(ptr); + AllocHeader* allocData = reinterpret_cast(ptr); + SkASSERT(kAssignedMarker == allocData->fSentinal); + SkDEBUGCODE(allocData->fSentinal = kFreedMarker); + BlockHeader* block = allocData->fHeader; + SkASSERT(kAssignedMarker == block->fBlockSentinal); if (1 == block->fLiveCount) { // the head block is special, it is reset rather than deleted if (fHead == block) { @@ -114,6 +121,7 @@ GrMemoryPool::BlockHeader* GrMemoryPool::CreateBlock(size_t size) { reinterpret_cast(sk_malloc_throw(paddedSize)); // we assume malloc gives us aligned memory SkASSERT(!(reinterpret_cast(block) % kAlignment)); + SkDEBUGCODE(block->fBlockSentinal = kAssignedMarker); block->fLiveCount = 0; block->fFreeSize = size; block->fCurrPtr = reinterpret_cast(block) + kHeaderSize; @@ -123,6 +131,8 @@ GrMemoryPool::BlockHeader* GrMemoryPool::CreateBlock(size_t size) { } void GrMemoryPool::DeleteBlock(BlockHeader* block) { + SkASSERT(kAssignedMarker == block->fBlockSentinal); + SkDEBUGCODE(block->fBlockSentinal = kFreedMarker); // FWIW sk_free(block); } @@ -133,6 +143,7 @@ void GrMemoryPool::validate() { SkASSERT(block); int allocCount = 0; do { + SkASSERT(kAssignedMarker == block->fBlockSentinal); allocCount += block->fLiveCount; SkASSERT(prev == block->fPrev); if (prev) { @@ -159,12 +170,16 @@ void GrMemoryPool::validate() { SkASSERT(ptrOffset == kHeaderSize); SkASSERT(userStart == block->fCurrPtr); } else { - SkASSERT(block == *reinterpret_cast(userStart)); + AllocHeader* allocData = reinterpret_cast(userStart); + SkASSERT(allocData->fSentinal == kAssignedMarker || + allocData->fSentinal == kFreedMarker); + SkASSERT(block == allocData->fHeader); } + prev = block; } while ((block = block->fNext)); SkASSERT(allocCount == fAllocationCnt); SkASSERT(prev == fTail); - SkASSERT(fAllocBlockCnt != 0 || fSize == fPreallocSize); + SkASSERT(fAllocBlockCnt != 0 || fSize == 0); #endif } diff --git a/gfx/skia/skia/src/gpu/GrMemoryPool.h b/gfx/skia/skia/src/gpu/GrMemoryPool.h index 4de641d9fc..43826d354a 100644 --- a/gfx/skia/skia/src/gpu/GrMemoryPool.h +++ b/gfx/skia/skia/src/gpu/GrMemoryPool.h @@ -44,7 +44,7 @@ public: bool isEmpty() const { return fTail == fHead && !fHead->fLiveCount; } /** - * Returns the total allocated size of the GrMemoryPool + * Returns the total allocated size of the GrMemoryPool minus any preallocated amount */ size_t size() const { return fSize; } @@ -58,6 +58,9 @@ private: void validate(); struct BlockHeader { +#ifdef SK_DEBUG + uint32_t fBlockSentinal; ///< known value to check for bad back pointers to blocks +#endif BlockHeader* fNext; ///< doubly-linked list of blocks. BlockHeader* fPrev; int fLiveCount; ///< number of outstanding allocations in the @@ -68,11 +71,21 @@ private: size_t fSize; ///< total allocated size of the block }; + static const uint32_t kAssignedMarker = 0xCDCDCDCD; + static const uint32_t kFreedMarker = 0xEFEFEFEF; + + struct AllocHeader { +#ifdef SK_DEBUG + uint32_t fSentinal; ///< known value to check for memory stomping (e.g., (CD)*) +#endif + BlockHeader* fHeader; ///< pointer back to the block header in which an alloc resides + }; + enum { // We assume this alignment is good enough for everybody. kAlignment = 8, kHeaderSize = GR_CT_ALIGN_UP(sizeof(BlockHeader), kAlignment), - kPerAllocPad = GR_CT_ALIGN_UP(sizeof(BlockHeader*), kAlignment), + kPerAllocPad = GR_CT_ALIGN_UP(sizeof(AllocHeader), kAlignment), }; size_t fSize; size_t fPreallocSize; diff --git a/gfx/skia/skia/src/gpu/GrOvalRenderer.cpp b/gfx/skia/skia/src/gpu/GrOvalRenderer.cpp index e13c0d11e4..6cb203c05a 100644 --- a/gfx/skia/skia/src/gpu/GrOvalRenderer.cpp +++ b/gfx/skia/skia/src/gpu/GrOvalRenderer.cpp @@ -19,14 +19,15 @@ #include "SkRRect.h" #include "SkStrokeRec.h" #include "SkTLazy.h" +#include "batches/GrRectBatchFactory.h" #include "batches/GrVertexBatch.h" #include "effects/GrRRectEffect.h" #include "glsl/GrGLSLFragmentShaderBuilder.h" #include "glsl/GrGLSLGeometryProcessor.h" -#include "glsl/GrGLSLProgramBuilder.h" #include "glsl/GrGLSLProgramDataManager.h" #include "glsl/GrGLSLVarying.h" #include "glsl/GrGLSLVertexShaderBuilder.h" +#include "glsl/GrGLSLUniformHandler.h" #include "glsl/GrGLSLUtil.h" // TODO(joshualitt) - Break this file up during GrBatch post implementation cleanup @@ -98,9 +99,9 @@ public: void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{ const CircleEdgeEffect& ce = args.fGP.cast(); - GrGLSLGPBuilder* pb = args.fPB; GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder; GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler; + GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; // emit attributes varyingHandler->emitAttributes(ce); @@ -112,16 +113,17 @@ public: GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder; // setup pass through color if (!ce.colorIgnored()) { - this->setupUniformColor(pb, fragBuilder, args.fOutputColor, &fColorUniform); + this->setupUniformColor(fragBuilder, uniformHandler, args.fOutputColor, + &fColorUniform); } // Setup position - this->setupPosition(pb, vertBuilder, gpArgs, ce.inPosition()->fName); + this->setupPosition(vertBuilder, gpArgs, ce.inPosition()->fName); // emit transforms - this->emitTransforms(args.fPB, - vertBuilder, + this->emitTransforms(vertBuilder, varyingHandler, + uniformHandler, gpArgs->fPositionVar, ce.inPosition()->fName, ce.localMatrix(), @@ -254,9 +256,9 @@ public: void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{ const EllipseEdgeEffect& ee = args.fGP.cast(); - GrGLSLGPBuilder* pb = args.fPB; GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder; GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler; + GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; // emit attributes varyingHandler->emitAttributes(ee); @@ -274,16 +276,17 @@ public: GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder; // setup pass through color if (!ee.colorIgnored()) { - this->setupUniformColor(pb, fragBuilder, args.fOutputColor, &fColorUniform); + this->setupUniformColor(fragBuilder, uniformHandler, args.fOutputColor, + &fColorUniform); } // Setup position - this->setupPosition(pb, vertBuilder, gpArgs, ee.inPosition()->fName); + this->setupPosition(vertBuilder, gpArgs, ee.inPosition()->fName); // emit transforms - this->emitTransforms(args.fPB, - vertBuilder, + this->emitTransforms(vertBuilder, varyingHandler, + uniformHandler, gpArgs->fPositionVar, ee.inPosition()->fName, ee.localMatrix(), @@ -436,9 +439,9 @@ public: void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override { const DIEllipseEdgeEffect& ee = args.fGP.cast(); - GrGLSLGPBuilder* pb = args.fPB; GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder; GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler; + GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; // emit attributes varyingHandler->emitAttributes(ee); @@ -456,21 +459,22 @@ public: GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder; // setup pass through color if (!ee.colorIgnored()) { - this->setupUniformColor(pb, fragBuilder, args.fOutputColor, &fColorUniform); + this->setupUniformColor(fragBuilder, uniformHandler, args.fOutputColor, + &fColorUniform); } // Setup position - this->setupPosition(pb, - vertBuilder, + this->setupPosition(vertBuilder, + uniformHandler, gpArgs, ee.inPosition()->fName, ee.viewMatrix(), &fViewMatrixUniform); // emit transforms - this->emitTransforms(args.fPB, - vertBuilder, + this->emitTransforms(vertBuilder, varyingHandler, + uniformHandler, gpArgs->fPositionVar, ee.inPosition()->fName, args.fTransformsIn, @@ -1490,7 +1494,9 @@ bool GrOvalRenderer::DrawDRRect(GrDrawTarget* target, if (applyAA) { bounds.outset(SK_ScalarHalf, SK_ScalarHalf); } - target->drawNonAARect(pipelineBuilder, color, SkMatrix::I(), bounds, invert); + SkAutoTUnref batch(GrRectBatchFactory::CreateNonAAFill(color, SkMatrix::I(), + bounds, nullptr, &invert)); + target->drawBatch(pipelineBuilder, batch); return true; } diff --git a/gfx/skia/skia/src/gpu/GrPath.h b/gfx/skia/skia/src/gpu/GrPath.h index 2edfd4cb5e..09d317ef2f 100644 --- a/gfx/skia/skia/src/gpu/GrPath.h +++ b/gfx/skia/skia/src/gpu/GrPath.h @@ -10,19 +10,19 @@ #include "GrGpuResource.h" #include "GrStrokeInfo.h" +#include "GrPathRendering.h" #include "SkPath.h" #include "SkRect.h" class GrPath : public GrGpuResource { public: - - /** * Initialize to a path with a fixed stroke. Stroke must not be hairline. */ GrPath(GrGpu* gpu, const SkPath& skPath, const GrStrokeInfo& stroke) : INHERITED(gpu, kCached_LifeCycle) - , fBounds(skPath.getBounds()) + , fBounds(SkRect::MakeEmpty()) + , fFillType(GrPathRendering::kWinding_FillType) #ifdef SK_DEBUG , fSkPath(skPath) , fStroke(stroke) @@ -35,12 +35,15 @@ public: const SkRect& getBounds() const { return fBounds; } + GrPathRendering::FillType getFillType() const { return fFillType; } #ifdef SK_DEBUG bool isEqualTo(const SkPath& path, const GrStrokeInfo& stroke) const; #endif protected: + // Subclass should init these. SkRect fBounds; + GrPathRendering::FillType fFillType; #ifdef SK_DEBUG SkPath fSkPath; GrStrokeInfo fStroke; diff --git a/gfx/skia/skia/src/gpu/GrPathProcessor.cpp b/gfx/skia/skia/src/gpu/GrPathProcessor.cpp index 66f94eaaab..f1faa7d239 100644 --- a/gfx/skia/skia/src/gpu/GrPathProcessor.cpp +++ b/gfx/skia/skia/src/gpu/GrPathProcessor.cpp @@ -11,7 +11,7 @@ #include "glsl/GrGLSLCaps.h" #include "glsl/GrGLSLFragmentShaderBuilder.h" #include "glsl/GrGLSLProcessorTypes.h" -#include "glsl/GrGLSLProgramBuilder.h" +#include "glsl/GrGLSLUniformHandler.h" #include "glsl/GrGLSLVarying.h" class GrGLPathProcessor : public GrGLSLPrimitiveProcessor { @@ -26,7 +26,6 @@ public: } void emitCode(EmitArgs& args) override { - GrGLSLGPBuilder* pb = args.fPB; GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder; const GrPathProcessor& pathProc = args.fGP.cast(); @@ -36,11 +35,12 @@ public: // Setup uniform color if (pathProc.overrides().readsColor()) { const char* stagedLocalVarName; - fColorUniform = pb->addUniform(GrGLSLProgramBuilder::kFragment_Visibility, - kVec4f_GrSLType, - kDefault_GrSLPrecision, - "Color", - &stagedLocalVarName); + fColorUniform = args.fUniformHandler->addUniform( + GrGLSLUniformHandler::kFragment_Visibility, + kVec4f_GrSLType, + kDefault_GrSLPrecision, + "Color", + &stagedLocalVarName); fragBuilder->codeAppendf("%s = %s;", args.fOutputColor, stagedLocalVarName); } diff --git a/gfx/skia/skia/src/gpu/GrPathUtils.cpp b/gfx/skia/skia/src/gpu/GrPathUtils.cpp index 21d115569b..f97a61e1f6 100644 --- a/gfx/skia/skia/src/gpu/GrPathUtils.cpp +++ b/gfx/skia/skia/src/gpu/GrPathUtils.cpp @@ -133,10 +133,10 @@ uint32_t GrPathUtils::generateCubicPoints(const SkPoint& p0, if (pointsLeft < 2 || (p1.distanceToLineSegmentBetweenSqd(p0, p3) < tolSqd && p2.distanceToLineSegmentBetweenSqd(p0, p3) < tolSqd)) { - (*points)[0] = p3; - *points += 1; - return 1; - } + (*points)[0] = p3; + *points += 1; + return 1; + } SkPoint q[] = { { SkScalarAve(p0.fX, p1.fX), SkScalarAve(p0.fY, p1.fY) }, { SkScalarAve(p1.fX, p2.fX), SkScalarAve(p1.fY, p2.fY) }, @@ -408,8 +408,8 @@ void convert_noninflect_cubic_to_quads(const SkPoint p[4], SkVector ab = p[1] - p[0]; SkVector dc = p[2] - p[3]; - if (ab.isZero()) { - if (dc.isZero()) { + if (ab.lengthSqd() < SK_ScalarNearlyZero) { + if (dc.lengthSqd() < SK_ScalarNearlyZero) { SkPoint* degQuad = quads->push_back_n(3); degQuad[0] = p[0]; degQuad[1] = p[0]; @@ -418,7 +418,7 @@ void convert_noninflect_cubic_to_quads(const SkPoint p[4], } ab = p[2] - p[0]; } - if (dc.isZero()) { + if (dc.lengthSqd() < SK_ScalarNearlyZero) { dc = p[1] - p[3]; } diff --git a/gfx/skia/skia/src/gpu/GrPipeline.cpp b/gfx/skia/skia/src/gpu/GrPipeline.cpp index 02edd4201e..e1c733a676 100644 --- a/gfx/skia/skia/src/gpu/GrPipeline.cpp +++ b/gfx/skia/skia/src/gpu/GrPipeline.cpp @@ -28,29 +28,30 @@ GrPipeline* GrPipeline::CreateAt(void* memory, const CreateArgs& args, builder.hasMixedSamples(), &args.fDstTexture, *args.fCaps)); + if (!xferProcessor) { + return nullptr; + } } else { + // This may return nullptr in the common case of src-over implemented using hw blending. xferProcessor.reset(GrPorterDuffXPFactory::CreateSrcOverXferProcessor( *args.fCaps, args.fOpts, builder.hasMixedSamples(), &args.fDstTexture)); } - - if (!xferProcessor) { - return nullptr; - } - - GrColor overrideColor = GrColor_ILLEGAL; + GrColor overrideColor = GrColor_ILLEGAL; if (args.fOpts.fColorPOI.firstEffectiveProcessorIndex() != 0) { overrideColor = args.fOpts.fColorPOI.inputColorToFirstEffectiveProccesor(); } GrXferProcessor::OptFlags optFlags = GrXferProcessor::kNone_OptFlags; - optFlags = xferProcessor->getOptimizations(args.fOpts, - builder.getStencil().doesWrite(), - &overrideColor, - *args.fCaps); + const GrXferProcessor* xpForOpts = xferProcessor ? xferProcessor.get() : + &GrPorterDuffXPFactory::SimpleSrcOverXP(); + optFlags = xpForOpts->getOptimizations(args.fOpts, + builder.getStencil().doesWrite(), + &overrideColor, + *args.fCaps); // When path rendering the stencil settings are not always set on the GrPipelineBuilder // so we must check the draw type. In cases where we will skip drawing we simply return a @@ -167,14 +168,12 @@ void GrPipeline::addDependenciesTo(GrRenderTarget* rt) const { add_dependencies_for_processor(fFragmentProcessors[i].get(), rt); } - if (fXferProcessor.get()) { - const GrXferProcessor* xfer = fXferProcessor.get(); + const GrXferProcessor& xfer = this->getXferProcessor(); - for (int i = 0; i < xfer->numTextures(); ++i) { - GrTexture* texture = xfer->textureAccess(i).getTexture(); - SkASSERT(rt->getLastDrawTarget()); - rt->getLastDrawTarget()->addDependency(texture); - } + for (int i = 0; i < xfer.numTextures(); ++i) { + GrTexture* texture = xfer.textureAccess(i).getTexture(); + SkASSERT(rt->getLastDrawTarget()); + rt->getLastDrawTarget()->addDependency(texture); } } @@ -185,7 +184,7 @@ void GrPipeline::adjustProgramFromOptimizations(const GrPipelineBuilder& pipelin int* firstColorProcessorIdx, int* firstCoverageProcessorIdx) { fIgnoresCoverage = SkToBool(flags & GrXferProcessor::kIgnoreCoverage_OptFlag); - fReadsFragPosition = fXferProcessor->willReadFragmentPosition(); + fReadsFragPosition = this->getXferProcessor().willReadFragmentPosition(); if ((flags & GrXferProcessor::kIgnoreColor_OptFlag) || (flags & GrXferProcessor::kOverrideColor_OptFlag)) { @@ -221,8 +220,11 @@ bool GrPipeline::AreEqual(const GrPipeline& a, const GrPipeline& b, return false; } - if (!a.getXferProcessor()->isEqual(*b.getXferProcessor())) { - return false; + // Most of the time both are nullptr + if (a.fXferProcessor.get() || b.fXferProcessor.get()) { + if (!a.getXferProcessor().isEqual(b.getXferProcessor())) { + return false; + } } for (int i = 0; i < a.numFragmentProcessors(); i++) { diff --git a/gfx/skia/skia/src/gpu/GrPipeline.h b/gfx/skia/skia/src/gpu/GrPipeline.h index fbfe119ac0..60d0cab1ab 100644 --- a/gfx/skia/skia/src/gpu/GrPipeline.h +++ b/gfx/skia/skia/src/gpu/GrPipeline.h @@ -103,7 +103,15 @@ public: } int numFragmentProcessors() const { return fFragmentProcessors.count(); } - const GrXferProcessor* getXferProcessor() const { return fXferProcessor.get(); } + const GrXferProcessor& getXferProcessor() const { + if (fXferProcessor.get()) { + return *fXferProcessor.get(); + } else { + // A null xp member means the common src-over case. GrXferProcessor's ref'ing + // mechanism is not thread safe so we do not hold a ref on this global. + return GrPorterDuffXPFactory::SimpleSrcOverXP(); + } + } const GrFragmentProcessor& getColorFragmentProcessor(int idx) const { SkASSERT(idx < this->numColorFragmentProcessors()); @@ -136,7 +144,7 @@ public: bool snapVerticesToPixelCenters() const { return SkToBool(fFlags & kSnapVertices_Flag); } GrXferBarrierType xferBarrierType(const GrCaps& caps) const { - return fXferProcessor->xferBarrierType(fRenderTarget.get(), caps); + return this->getXferProcessor().xferBarrierType(fRenderTarget.get(), caps); } /** diff --git a/gfx/skia/skia/src/gpu/GrProgramDesc.h b/gfx/skia/skia/src/gpu/GrProgramDesc.h index 13dd14935d..63e060ee62 100644 --- a/gfx/skia/skia/src/gpu/GrProgramDesc.h +++ b/gfx/skia/skia/src/gpu/GrProgramDesc.h @@ -70,15 +70,16 @@ public: } struct KeyHeader { - uint8_t fFragPosKey; // set by GrGLShaderBuilder if there are - // effects that read the fragment position. - // Otherwise, 0. + // Set by GrGLShaderBuilder if there are effects that read the fragment position. Otherwise, + // 0. + uint8_t fFragPosKey; + // Set to uniquely idenitify any swizzling of the shader's output color(s). + uint8_t fOutputSwizzle; uint8_t fSnapVerticesToPixelCenters; int8_t fColorEffectCnt; int8_t fCoverageEffectCnt; uint8_t fIgnoresCoverage; }; - GR_STATIC_ASSERT(sizeof(KeyHeader) == 5); int numColorEffects() const { return this->header().fColorEffectCnt; diff --git a/gfx/skia/skia/src/gpu/GrRecordReplaceDraw.cpp b/gfx/skia/skia/src/gpu/GrRecordReplaceDraw.cpp index 28f27dfc1c..1fe6040789 100644 --- a/gfx/skia/skia/src/gpu/GrRecordReplaceDraw.cpp +++ b/gfx/skia/skia/src/gpu/GrRecordReplaceDraw.cpp @@ -10,16 +10,11 @@ #include "GrRecordReplaceDraw.h" #include "SkBigPicture.h" #include "SkCanvasPriv.h" -#include "SkGrPixelRef.h" +#include "SkGr.h" #include "SkImage.h" #include "SkRecordDraw.h" #include "SkRecords.h" -static inline void wrap_texture(GrTexture* texture, int width, int height, SkBitmap* result) { - SkImageInfo info = SkImageInfo::MakeN32Premul(width, height); - result->setInfo(info); - result->setPixelRef(new SkGrPixelRef(info, texture))->unref(); -} static inline void draw_replacement_bitmap(GrCachedLayer* layer, SkCanvas* canvas) { @@ -30,10 +25,11 @@ static inline void draw_replacement_bitmap(GrCachedLayer* layer, SkCanvas* canva } SkBitmap bm; - wrap_texture(layer->texture(), - !layer->isAtlased() ? layer->rect().width() : layer->texture()->width(), - !layer->isAtlased() ? layer->rect().height() : layer->texture()->height(), - &bm); + GrWrapTextureInBitmap(layer->texture(), + !layer->isAtlased() ? layer->rect().width() : layer->texture()->width(), + !layer->isAtlased() ? layer->rect().height() : layer->texture()->height(), + false, + &bm); canvas->save(); canvas->setMatrix(SkMatrix::I()); diff --git a/gfx/skia/skia/include/gpu/GrRect.h b/gfx/skia/skia/src/gpu/GrRect.h similarity index 100% rename from gfx/skia/skia/include/gpu/GrRect.h rename to gfx/skia/skia/src/gpu/GrRect.h diff --git a/gfx/skia/skia/src/gpu/GrResourceCache.h b/gfx/skia/skia/src/gpu/GrResourceCache.h index a495f565b2..adbfb7919f 100644 --- a/gfx/skia/skia/src/gpu/GrResourceCache.h +++ b/gfx/skia/skia/src/gpu/GrResourceCache.h @@ -60,7 +60,7 @@ public: // purged. Large values disable the feature (as the ring buffer of flush timestamps would be // large). This is currently the default until we decide to enable this feature // of the cache by default. - static const int kDefaultMaxUnusedFlushes = 1024; + static const int kDefaultMaxUnusedFlushes = 64; /** Used to access functionality needed by GrGpuResource for lifetime management. */ class ResourceAccess; diff --git a/gfx/skia/skia/src/gpu/GrResourceProvider.cpp b/gfx/skia/skia/src/gpu/GrResourceProvider.cpp index 38213e6266..19fa1cfbb6 100644 --- a/gfx/skia/skia/src/gpu/GrResourceProvider.cpp +++ b/gfx/skia/skia/src/gpu/GrResourceProvider.cpp @@ -19,7 +19,8 @@ GR_DECLARE_STATIC_UNIQUE_KEY(gQuadIndexBufferKey); -GrResourceProvider::GrResourceProvider(GrGpu* gpu, GrResourceCache* cache) : INHERITED(gpu, cache) { +GrResourceProvider::GrResourceProvider(GrGpu* gpu, GrResourceCache* cache, GrSingleOwner* owner) + : INHERITED(gpu, cache, owner) { GR_DEFINE_STATIC_UNIQUE_KEY(gQuadIndexBufferKey); fQuadIndexBufferKey = gQuadIndexBufferKey; } @@ -146,6 +147,16 @@ GrVertexBuffer* GrResourceProvider::createVertexBuffer(size_t size, BufferUsage return this->gpu()->createVertexBuffer(size, dynamic); } +GrTransferBuffer* GrResourceProvider::createTransferBuffer(size_t size, TransferType type, + uint32_t flags) { + if (this->isAbandoned()) { + return nullptr; + } + + //bool noPendingIO = SkToBool(flags & kNoPendingIO_Flag); + return this->gpu()->createTransferBuffer(size, type); +} + GrBatchAtlas* GrResourceProvider::createAtlas(GrPixelConfig config, int width, int height, int numPlotsX, int numPlotsY, diff --git a/gfx/skia/skia/src/gpu/GrResourceProvider.h b/gfx/skia/skia/src/gpu/GrResourceProvider.h index 6c5737191b..783c7c743b 100644 --- a/gfx/skia/skia/src/gpu/GrResourceProvider.h +++ b/gfx/skia/skia/src/gpu/GrResourceProvider.h @@ -17,6 +17,7 @@ class GrBatchAtlas; class GrIndexBuffer; class GrPath; class GrRenderTarget; +class GrSingleOwner; class GrStencilAttachment; class GrStrokeInfo; class GrVertexBuffer; @@ -35,7 +36,7 @@ class SkTypeface; */ class GrResourceProvider : protected GrTextureProvider { public: - GrResourceProvider(GrGpu* gpu, GrResourceCache* cache); + GrResourceProvider(GrGpu* gpu, GrResourceCache* cache, GrSingleOwner* owner); template T* findAndRefTByUniqueKey(const GrUniqueKey& key) { return static_cast(this->findAndRefResourceByUniqueKey(key)); @@ -112,6 +113,7 @@ public: }; GrIndexBuffer* createIndexBuffer(size_t size, BufferUsage, uint32_t flags); GrVertexBuffer* createVertexBuffer(size_t size, BufferUsage, uint32_t flags); + GrTransferBuffer* createTransferBuffer(size_t size, TransferType, uint32_t flags); GrTexture* createApproxTexture(const GrSurfaceDesc& desc, uint32_t flags) { SkASSERT(0 == flags || kNoPendingIO_Flag == flags); diff --git a/gfx/skia/skia/src/gpu/GrSWMaskHelper.cpp b/gfx/skia/skia/src/gpu/GrSWMaskHelper.cpp index 6692e3d833..34e4495286 100644 --- a/gfx/skia/skia/src/gpu/GrSWMaskHelper.cpp +++ b/gfx/skia/skia/src/gpu/GrSWMaskHelper.cpp @@ -7,17 +7,16 @@ #include "GrSWMaskHelper.h" -#include "GrPipelineBuilder.h" #include "GrCaps.h" #include "GrDrawTarget.h" #include "GrGpu.h" +#include "GrPipelineBuilder.h" #include "SkData.h" #include "SkDistanceFieldGen.h" #include "SkStrokeRec.h" -// TODO: try to remove this #include -#include "GrContext.h" +#include "batches/GrRectBatchFactory.h" namespace { @@ -372,5 +371,7 @@ void GrSWMaskHelper::DrawToTargetWithPathMask(GrTexture* texture, GrTextureParams::kNone_FilterMode, kDevice_GrCoordSet))->unref(); - target->drawNonAARect(*pipelineBuilder, color, SkMatrix::I(), dstRect, invert); + SkAutoTUnref batch(GrRectBatchFactory::CreateNonAAFill(color, SkMatrix::I(), + dstRect, nullptr, &invert)); + target->drawBatch(*pipelineBuilder, batch); } diff --git a/gfx/skia/skia/src/gpu/GrSoftwarePathRenderer.cpp b/gfx/skia/skia/src/gpu/GrSoftwarePathRenderer.cpp index a136886a64..af77c205b5 100644 --- a/gfx/skia/skia/src/gpu/GrSoftwarePathRenderer.cpp +++ b/gfx/skia/skia/src/gpu/GrSoftwarePathRenderer.cpp @@ -10,6 +10,7 @@ #include "GrContext.h" #include "GrSWMaskHelper.h" #include "GrVertexBuffer.h" +#include "batches/GrRectBatchFactory.h" //////////////////////////////////////////////////////////////////////////////// bool GrSoftwarePathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const { @@ -65,6 +66,17 @@ bool get_path_and_clip_bounds(const GrPipelineBuilder* pipelineBuilder, } //////////////////////////////////////////////////////////////////////////////// +static void draw_non_aa_rect(GrDrawTarget* drawTarget, + const GrPipelineBuilder& pipelineBuilder, + GrColor color, + const SkMatrix& viewMatrix, + const SkRect& rect, + const SkMatrix& localMatrix) { + SkAutoTUnref batch(GrRectBatchFactory::CreateNonAAFill(color, viewMatrix, rect, + nullptr, &localMatrix)); + drawTarget->drawBatch(pipelineBuilder, batch); +} + void draw_around_inv_path(GrDrawTarget* target, GrPipelineBuilder* pipelineBuilder, GrColor color, @@ -80,22 +92,22 @@ void draw_around_inv_path(GrDrawTarget* target, if (devClipBounds.fTop < devPathBounds.fTop) { rect.iset(devClipBounds.fLeft, devClipBounds.fTop, devClipBounds.fRight, devPathBounds.fTop); - target->drawNonAARect(*pipelineBuilder, color, SkMatrix::I(), rect, invert); + draw_non_aa_rect(target, *pipelineBuilder, color, SkMatrix::I(), rect, invert); } if (devClipBounds.fLeft < devPathBounds.fLeft) { rect.iset(devClipBounds.fLeft, devPathBounds.fTop, devPathBounds.fLeft, devPathBounds.fBottom); - target->drawNonAARect(*pipelineBuilder, color, SkMatrix::I(), rect, invert); + draw_non_aa_rect(target, *pipelineBuilder, color, SkMatrix::I(), rect, invert); } if (devClipBounds.fRight > devPathBounds.fRight) { rect.iset(devPathBounds.fRight, devPathBounds.fTop, devClipBounds.fRight, devPathBounds.fBottom); - target->drawNonAARect(*pipelineBuilder, color, SkMatrix::I(), rect, invert); + draw_non_aa_rect(target, *pipelineBuilder, color, SkMatrix::I(), rect, invert); } if (devClipBounds.fBottom > devPathBounds.fBottom) { rect.iset(devClipBounds.fLeft, devPathBounds.fBottom, devClipBounds.fRight, devClipBounds.fBottom); - target->drawNonAARect(*pipelineBuilder, color, SkMatrix::I(), rect, invert); + draw_non_aa_rect(target, *pipelineBuilder, color, SkMatrix::I(), rect, invert); } } @@ -104,6 +116,7 @@ void draw_around_inv_path(GrDrawTarget* target, //////////////////////////////////////////////////////////////////////////////// // return true on success; false on failure bool GrSoftwarePathRenderer::onDrawPath(const DrawPathArgs& args) { + GR_AUDIT_TRAIL_AUTO_FRAME(args.fTarget->getAuditTrail(), "GrSoftwarePathRenderer::onDrawPath"); if (nullptr == fContext) { return false; } diff --git a/gfx/skia/skia/src/gpu/GrSwizzle.h b/gfx/skia/skia/src/gpu/GrSwizzle.h new file mode 100644 index 0000000000..48748803dd --- /dev/null +++ b/gfx/skia/skia/src/gpu/GrSwizzle.h @@ -0,0 +1,136 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef GrSwizzle_DEFINED +#define GrSwizzle_DEFINED + +#include "GrTypes.h" +#include "GrColor.h" + +/** Represents a rgba swizzle. It can be converted either into a string or a eight bit int. + Currently there is no way to specify an arbitrary swizzle, just some static swizzles and an + assignment operator. That could be relaxed. */ +class GrSwizzle { +public: + GrSwizzle() { *this = RGBA(); } + + GrSwizzle(const GrSwizzle& that) { *this = that; } + + GrSwizzle& operator=(const GrSwizzle& that) { + memcpy(this, &that, sizeof(GrSwizzle)); + return *this; + } + + /** Recreates a GrSwizzle from the output of asKey() */ + void setFromKey(uint8_t key) { + fKey = key; + for (int i = 0; i < 4; ++i) { + fSwiz[i] = IdxToChar(key & 3); + key >>= 2; + } + SkASSERT(fSwiz[4] == 0); + } + + bool operator==(const GrSwizzle& that) const { return this->asUInt() == that.asUInt(); } + + bool operator!=(const GrSwizzle& that) const { return !(*this == that); } + + /** Compact representation of the swizzle suitable for a key. */ + uint8_t asKey() const { return fKey; } + + /** 4 char null terminated string consisting only of chars 'r', 'g', 'b', 'a'. */ + const char* c_str() const { return fSwiz; } + + /** Applies this swizzle to the input color and returns the swizzled color. */ + GrColor applyTo(GrColor color) const { + int idx; + uint32_t key = fKey; + // Index of the input color that should be mapped to output r. + idx = (key & 3); + uint32_t outR = (color >> idx * 8) & 0xFF; + key >>= 2; + idx = (key & 3); + uint32_t outG = (color >> idx * 8) & 0xFF; + key >>= 2; + idx = (key & 3); + uint32_t outB = (color >> idx * 8) & 0xFF; + key >>= 2; + idx = (key & 3); + uint32_t outA = (color >> idx * 8) & 0xFF; + return GrColorPackRGBA(outR, outG, outB, outA); + } + + static const GrSwizzle& RGBA() { + static GrSwizzle gRGBA("rgba"); + return gRGBA; + } + + static const GrSwizzle& AAAA() { + static GrSwizzle gAAAA("aaaa"); + return gAAAA; + } + + static const GrSwizzle& RRRR() { + static GrSwizzle gRRRR("rrrr"); + return gRRRR; + } + + static const GrSwizzle& BGRA() { + static GrSwizzle gBGRA("bgra"); + return gBGRA; + } + +private: + char fSwiz[5]; + uint8_t fKey; + + static int CharToIdx(char c) { + switch (c) { + case 'r': + return (GrColor_SHIFT_R / 8); + case 'g': + return (GrColor_SHIFT_G / 8); + case 'b': + return (GrColor_SHIFT_B / 8); + case 'a': + return (GrColor_SHIFT_A / 8); + default: + SkFAIL("Invalid swizzle char"); + return 0; + } + } + + static /* constexpr */ char IToC(int idx) { + return (8*idx) == GrColor_SHIFT_R ? 'r' : + (8*idx) == GrColor_SHIFT_G ? 'g' : + (8*idx) == GrColor_SHIFT_B ? 'b' : 'a'; + } + + static char IdxToChar(int c) { + // Hopefully this array gets computed at compile time. + static const char gStr[4] = { IToC(0), IToC(1), IToC(2), IToC(3) }; + return gStr[c]; + } + + explicit GrSwizzle(const char* str) { + SkASSERT(strlen(str) == 4); + fSwiz[0] = str[0]; + fSwiz[1] = str[1]; + fSwiz[2] = str[2]; + fSwiz[3] = str[3]; + fSwiz[4] = 0; + fKey = SkToU8(CharToIdx(fSwiz[0]) | (CharToIdx(fSwiz[1]) << 2) | + (CharToIdx(fSwiz[2]) << 4) | (CharToIdx(fSwiz[3]) << 6)); + } + + uint32_t* asUIntPtr() { return SkTCast(fSwiz); } + uint32_t asUInt() const { return *SkTCast(fSwiz); } + + GR_STATIC_ASSERT(sizeof(char[4]) == sizeof(uint32_t)); +}; + +#endif diff --git a/gfx/skia/skia/src/gpu/GrTessellator.cpp b/gfx/skia/skia/src/gpu/GrTessellator.cpp new file mode 100644 index 0000000000..ea130cf15f --- /dev/null +++ b/gfx/skia/skia/src/gpu/GrTessellator.cpp @@ -0,0 +1,1470 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "GrTessellator.h" + +#include "GrBatchFlushState.h" +#include "GrBatchTest.h" +#include "GrDefaultGeoProcFactory.h" +#include "GrPathUtils.h" +#include "GrVertices.h" +#include "GrResourceCache.h" +#include "GrResourceProvider.h" +#include "SkGeometry.h" +#include "SkChunkAlloc.h" + +#include "batches/GrVertexBatch.h" + +#include + +/* + * There are six stages to the algorithm: + * + * 1) Linearize the path contours into piecewise linear segments (path_to_contours()). + * 2) Build a mesh of edges connecting the vertices (build_edges()). + * 3) Sort the vertices in Y (and secondarily in X) (merge_sort()). + * 4) Simplify the mesh by inserting new vertices at intersecting edges (simplify()). + * 5) Tessellate the simplified mesh into monotone polygons (tessellate()). + * 6) Triangulate the monotone polygons directly into a vertex buffer (polys_to_triangles()). + * + * The vertex sorting in step (3) is a merge sort, since it plays well with the linked list + * of vertices (and the necessity of inserting new vertices on intersection). + * + * Stages (4) and (5) use an active edge list, which a list of all edges for which the + * sweep line has crossed the top vertex, but not the bottom vertex. It's sorted + * left-to-right based on the point where both edges are active (when both top vertices + * have been seen, so the "lower" top vertex of the two). If the top vertices are equal + * (shared), it's sorted based on the last point where both edges are active, so the + * "upper" bottom vertex. + * + * The most complex step is the simplification (4). It's based on the Bentley-Ottman + * line-sweep algorithm, but due to floating point inaccuracy, the intersection points are + * not exact and may violate the mesh topology or active edge list ordering. We + * accommodate this by adjusting the topology of the mesh and AEL to match the intersection + * points. This occurs in three ways: + * + * A) Intersections may cause a shortened edge to no longer be ordered with respect to its + * neighbouring edges at the top or bottom vertex. This is handled by merging the + * edges (merge_collinear_edges()). + * B) Intersections may cause an edge to violate the left-to-right ordering of the + * active edge list. This is handled by splitting the neighbour edge on the + * intersected vertex (cleanup_active_edges()). + * C) Shortening an edge may cause an active edge to become inactive or an inactive edge + * to become active. This is handled by removing or inserting the edge in the active + * edge list (fix_active_state()). + * + * The tessellation steps (5) and (6) are based on "Triangulating Simple Polygons and + * Equivalent Problems" (Fournier and Montuno); also a line-sweep algorithm. Note that it + * currently uses a linked list for the active edge list, rather than a 2-3 tree as the + * paper describes. The 2-3 tree gives O(lg N) lookups, but insertion and removal also + * become O(lg N). In all the test cases, it was found that the cost of frequent O(lg N) + * insertions and removals was greater than the cost of infrequent O(N) lookups with the + * linked list implementation. With the latter, all removals are O(1), and most insertions + * are O(1), since we know the adjacent edge in the active edge list based on the topology. + * Only type 2 vertices (see paper) require the O(N) lookups, and these are much less + * frequent. There may be other data structures worth investigating, however. + * + * Note that the orientation of the line sweep algorithms is determined by the aspect ratio of the + * path bounds. When the path is taller than it is wide, we sort vertices based on increasing Y + * coordinate, and secondarily by increasing X coordinate. When the path is wider than it is tall, + * we sort by increasing X coordinate, but secondarily by *decreasing* Y coordinate. This is so + * that the "left" and "right" orientation in the code remains correct (edges to the left are + * increasing in Y; edges to the right are decreasing in Y). That is, the setting rotates 90 + * degrees counterclockwise, rather that transposing. + */ + +#define LOGGING_ENABLED 0 + +#if LOGGING_ENABLED +#define LOG printf +#else +#define LOG(...) +#endif + +#define ALLOC_NEW(Type, args, alloc) new (alloc.allocThrow(sizeof(Type))) Type args + +namespace { + +struct Vertex; +struct Edge; +struct Poly; + +template +void insert(T* t, T* prev, T* next, T** head, T** tail) { + t->*Prev = prev; + t->*Next = next; + if (prev) { + prev->*Next = t; + } else if (head) { + *head = t; + } + if (next) { + next->*Prev = t; + } else if (tail) { + *tail = t; + } +} + +template +void remove(T* t, T** head, T** tail) { + if (t->*Prev) { + t->*Prev->*Next = t->*Next; + } else if (head) { + *head = t->*Next; + } + if (t->*Next) { + t->*Next->*Prev = t->*Prev; + } else if (tail) { + *tail = t->*Prev; + } + t->*Prev = t->*Next = nullptr; +} + +/** + * Vertices are used in three ways: first, the path contours are converted into a + * circularly-linked list of Vertices for each contour. After edge construction, the same Vertices + * are re-ordered by the merge sort according to the sweep_lt comparator (usually, increasing + * in Y) using the same fPrev/fNext pointers that were used for the contours, to avoid + * reallocation. Finally, MonotonePolys are built containing a circularly-linked list of + * Vertices. (Currently, those Vertices are newly-allocated for the MonotonePolys, since + * an individual Vertex from the path mesh may belong to multiple + * MonotonePolys, so the original Vertices cannot be re-used. + */ + +struct Vertex { + Vertex(const SkPoint& point) + : fPoint(point), fPrev(nullptr), fNext(nullptr) + , fFirstEdgeAbove(nullptr), fLastEdgeAbove(nullptr) + , fFirstEdgeBelow(nullptr), fLastEdgeBelow(nullptr) + , fProcessed(false) +#if LOGGING_ENABLED + , fID (-1.0f) +#endif + {} + SkPoint fPoint; // Vertex position + Vertex* fPrev; // Linked list of contours, then Y-sorted vertices. + Vertex* fNext; // " + Edge* fFirstEdgeAbove; // Linked list of edges above this vertex. + Edge* fLastEdgeAbove; // " + Edge* fFirstEdgeBelow; // Linked list of edges below this vertex. + Edge* fLastEdgeBelow; // " + bool fProcessed; // Has this vertex been seen in simplify()? +#if LOGGING_ENABLED + float fID; // Identifier used for logging. +#endif +}; + +/***************************************************************************************/ + +typedef bool (*CompareFunc)(const SkPoint& a, const SkPoint& b); + +struct Comparator { + CompareFunc sweep_lt; + CompareFunc sweep_gt; +}; + +bool sweep_lt_horiz(const SkPoint& a, const SkPoint& b) { + return a.fX == b.fX ? a.fY > b.fY : a.fX < b.fX; +} + +bool sweep_lt_vert(const SkPoint& a, const SkPoint& b) { + return a.fY == b.fY ? a.fX < b.fX : a.fY < b.fY; +} + +bool sweep_gt_horiz(const SkPoint& a, const SkPoint& b) { + return a.fX == b.fX ? a.fY < b.fY : a.fX > b.fX; +} + +bool sweep_gt_vert(const SkPoint& a, const SkPoint& b) { + return a.fY == b.fY ? a.fX > b.fX : a.fY > b.fY; +} + +inline SkPoint* emit_vertex(Vertex* v, SkPoint* data) { + *data++ = v->fPoint; + return data; +} + +SkPoint* emit_triangle(Vertex* v0, Vertex* v1, Vertex* v2, SkPoint* data) { +#if WIREFRAME + data = emit_vertex(v0, data); + data = emit_vertex(v1, data); + data = emit_vertex(v1, data); + data = emit_vertex(v2, data); + data = emit_vertex(v2, data); + data = emit_vertex(v0, data); +#else + data = emit_vertex(v0, data); + data = emit_vertex(v1, data); + data = emit_vertex(v2, data); +#endif + return data; +} + +struct EdgeList { + EdgeList() : fHead(nullptr), fTail(nullptr) {} + Edge* fHead; + Edge* fTail; +}; + +/** + * An Edge joins a top Vertex to a bottom Vertex. Edge ordering for the list of "edges above" and + * "edge below" a vertex as well as for the active edge list is handled by isLeftOf()/isRightOf(). + * Note that an Edge will give occasionally dist() != 0 for its own endpoints (because floating + * point). For speed, that case is only tested by the callers which require it (e.g., + * cleanup_active_edges()). Edges also handle checking for intersection with other edges. + * Currently, this converts the edges to the parametric form, in order to avoid doing a division + * until an intersection has been confirmed. This is slightly slower in the "found" case, but + * a lot faster in the "not found" case. + * + * The coefficients of the line equation stored in double precision to avoid catastrphic + * cancellation in the isLeftOf() and isRightOf() checks. Using doubles ensures that the result is + * correct in float, since it's a polynomial of degree 2. The intersect() function, being + * degree 5, is still subject to catastrophic cancellation. We deal with that by assuming its + * output may be incorrect, and adjusting the mesh topology to match (see comment at the top of + * this file). + */ + +struct Edge { + Edge(Vertex* top, Vertex* bottom, int winding) + : fWinding(winding) + , fTop(top) + , fBottom(bottom) + , fLeft(nullptr) + , fRight(nullptr) + , fPrevEdgeAbove(nullptr) + , fNextEdgeAbove(nullptr) + , fPrevEdgeBelow(nullptr) + , fNextEdgeBelow(nullptr) + , fLeftPoly(nullptr) + , fRightPoly(nullptr) { + recompute(); + } + int fWinding; // 1 == edge goes downward; -1 = edge goes upward. + Vertex* fTop; // The top vertex in vertex-sort-order (sweep_lt). + Vertex* fBottom; // The bottom vertex in vertex-sort-order. + Edge* fLeft; // The linked list of edges in the active edge list. + Edge* fRight; // " + Edge* fPrevEdgeAbove; // The linked list of edges in the bottom Vertex's "edges above". + Edge* fNextEdgeAbove; // " + Edge* fPrevEdgeBelow; // The linked list of edges in the top Vertex's "edges below". + Edge* fNextEdgeBelow; // " + Poly* fLeftPoly; // The Poly to the left of this edge, if any. + Poly* fRightPoly; // The Poly to the right of this edge, if any. + double fDX; // The line equation for this edge, in implicit form. + double fDY; // fDY * x + fDX * y + fC = 0, for point (x, y) on the line. + double fC; + double dist(const SkPoint& p) const { + return fDY * p.fX - fDX * p.fY + fC; + } + bool isRightOf(Vertex* v) const { + return dist(v->fPoint) < 0.0; + } + bool isLeftOf(Vertex* v) const { + return dist(v->fPoint) > 0.0; + } + void recompute() { + fDX = static_cast(fBottom->fPoint.fX) - fTop->fPoint.fX; + fDY = static_cast(fBottom->fPoint.fY) - fTop->fPoint.fY; + fC = static_cast(fTop->fPoint.fY) * fBottom->fPoint.fX - + static_cast(fTop->fPoint.fX) * fBottom->fPoint.fY; + } + bool intersect(const Edge& other, SkPoint* p) { + LOG("intersecting %g -> %g with %g -> %g\n", + fTop->fID, fBottom->fID, + other.fTop->fID, other.fBottom->fID); + if (fTop == other.fTop || fBottom == other.fBottom) { + return false; + } + double denom = fDX * other.fDY - fDY * other.fDX; + if (denom == 0.0) { + return false; + } + double dx = static_cast(fTop->fPoint.fX) - other.fTop->fPoint.fX; + double dy = static_cast(fTop->fPoint.fY) - other.fTop->fPoint.fY; + double sNumer = dy * other.fDX - dx * other.fDY; + double tNumer = dy * fDX - dx * fDY; + // If (sNumer / denom) or (tNumer / denom) is not in [0..1], exit early. + // This saves us doing the divide below unless absolutely necessary. + if (denom > 0.0 ? (sNumer < 0.0 || sNumer > denom || tNumer < 0.0 || tNumer > denom) + : (sNumer > 0.0 || sNumer < denom || tNumer > 0.0 || tNumer < denom)) { + return false; + } + double s = sNumer / denom; + SkASSERT(s >= 0.0 && s <= 1.0); + p->fX = SkDoubleToScalar(fTop->fPoint.fX + s * fDX); + p->fY = SkDoubleToScalar(fTop->fPoint.fY + s * fDY); + return true; + } + bool isActive(EdgeList* activeEdges) const { + return activeEdges && (fLeft || fRight || activeEdges->fHead == this); + } +}; + +/***************************************************************************************/ + +struct Poly { + Poly(int winding) + : fWinding(winding) + , fHead(nullptr) + , fTail(nullptr) + , fActive(nullptr) + , fNext(nullptr) + , fPartner(nullptr) + , fCount(0) + { +#if LOGGING_ENABLED + static int gID = 0; + fID = gID++; + LOG("*** created Poly %d\n", fID); +#endif + } + typedef enum { kNeither_Side, kLeft_Side, kRight_Side } Side; + struct MonotonePoly { + MonotonePoly() + : fSide(kNeither_Side) + , fHead(nullptr) + , fTail(nullptr) + , fPrev(nullptr) + , fNext(nullptr) {} + Side fSide; + Vertex* fHead; + Vertex* fTail; + MonotonePoly* fPrev; + MonotonePoly* fNext; + bool addVertex(Vertex* v, Side side, SkChunkAlloc& alloc) { + Vertex* newV = ALLOC_NEW(Vertex, (v->fPoint), alloc); + bool done = false; + if (fSide == kNeither_Side) { + fSide = side; + } else { + done = side != fSide; + } + if (fHead == nullptr) { + fHead = fTail = newV; + } else if (fSide == kRight_Side) { + newV->fPrev = fTail; + fTail->fNext = newV; + fTail = newV; + } else { + newV->fNext = fHead; + fHead->fPrev = newV; + fHead = newV; + } + return done; + } + + SkPoint* emit(SkPoint* data) { + Vertex* first = fHead; + Vertex* v = first->fNext; + while (v != fTail) { + SkASSERT(v && v->fPrev && v->fNext); + Vertex* prev = v->fPrev; + Vertex* curr = v; + Vertex* next = v->fNext; + double ax = static_cast(curr->fPoint.fX) - prev->fPoint.fX; + double ay = static_cast(curr->fPoint.fY) - prev->fPoint.fY; + double bx = static_cast(next->fPoint.fX) - curr->fPoint.fX; + double by = static_cast(next->fPoint.fY) - curr->fPoint.fY; + if (ax * by - ay * bx >= 0.0) { + data = emit_triangle(prev, curr, next, data); + v->fPrev->fNext = v->fNext; + v->fNext->fPrev = v->fPrev; + if (v->fPrev == first) { + v = v->fNext; + } else { + v = v->fPrev; + } + } else { + v = v->fNext; + } + } + return data; + } + }; + Poly* addVertex(Vertex* v, Side side, SkChunkAlloc& alloc) { + LOG("addVertex() to %d at %g (%g, %g), %s side\n", fID, v->fID, v->fPoint.fX, v->fPoint.fY, + side == kLeft_Side ? "left" : side == kRight_Side ? "right" : "neither"); + Poly* partner = fPartner; + Poly* poly = this; + if (partner) { + fPartner = partner->fPartner = nullptr; + } + if (!fActive) { + fActive = ALLOC_NEW(MonotonePoly, (), alloc); + } + if (fActive->addVertex(v, side, alloc)) { + if (fTail) { + fActive->fPrev = fTail; + fTail->fNext = fActive; + fTail = fActive; + } else { + fHead = fTail = fActive; + } + if (partner) { + partner->addVertex(v, side, alloc); + poly = partner; + } else { + Vertex* prev = fActive->fSide == Poly::kLeft_Side ? + fActive->fHead->fNext : fActive->fTail->fPrev; + fActive = ALLOC_NEW(MonotonePoly, , alloc); + fActive->addVertex(prev, Poly::kNeither_Side, alloc); + fActive->addVertex(v, side, alloc); + } + } + fCount++; + return poly; + } + void end(Vertex* v, SkChunkAlloc& alloc) { + LOG("end() %d at %g, %g\n", fID, v->fPoint.fX, v->fPoint.fY); + if (fPartner) { + fPartner = fPartner->fPartner = nullptr; + } + addVertex(v, fActive->fSide == kLeft_Side ? kRight_Side : kLeft_Side, alloc); + } + SkPoint* emit(SkPoint *data) { + if (fCount < 3) { + return data; + } + LOG("emit() %d, size %d\n", fID, fCount); + for (MonotonePoly* m = fHead; m != nullptr; m = m->fNext) { + data = m->emit(data); + } + return data; + } + int fWinding; + MonotonePoly* fHead; + MonotonePoly* fTail; + MonotonePoly* fActive; + Poly* fNext; + Poly* fPartner; + int fCount; +#if LOGGING_ENABLED + int fID; +#endif +}; + +/***************************************************************************************/ + +bool coincident(const SkPoint& a, const SkPoint& b) { + return a == b; +} + +Poly* new_poly(Poly** head, Vertex* v, int winding, SkChunkAlloc& alloc) { + Poly* poly = ALLOC_NEW(Poly, (winding), alloc); + poly->addVertex(v, Poly::kNeither_Side, alloc); + poly->fNext = *head; + *head = poly; + return poly; +} + +Vertex* append_point_to_contour(const SkPoint& p, Vertex* prev, Vertex** head, + SkChunkAlloc& alloc) { + Vertex* v = ALLOC_NEW(Vertex, (p), alloc); +#if LOGGING_ENABLED + static float gID = 0.0f; + v->fID = gID++; +#endif + if (prev) { + prev->fNext = v; + v->fPrev = prev; + } else { + *head = v; + } + return v; +} + +Vertex* generate_quadratic_points(const SkPoint& p0, + const SkPoint& p1, + const SkPoint& p2, + SkScalar tolSqd, + Vertex* prev, + Vertex** head, + int pointsLeft, + SkChunkAlloc& alloc) { + SkScalar d = p1.distanceToLineSegmentBetweenSqd(p0, p2); + if (pointsLeft < 2 || d < tolSqd || !SkScalarIsFinite(d)) { + return append_point_to_contour(p2, prev, head, alloc); + } + + const SkPoint q[] = { + { SkScalarAve(p0.fX, p1.fX), SkScalarAve(p0.fY, p1.fY) }, + { SkScalarAve(p1.fX, p2.fX), SkScalarAve(p1.fY, p2.fY) }, + }; + const SkPoint r = { SkScalarAve(q[0].fX, q[1].fX), SkScalarAve(q[0].fY, q[1].fY) }; + + pointsLeft >>= 1; + prev = generate_quadratic_points(p0, q[0], r, tolSqd, prev, head, pointsLeft, alloc); + prev = generate_quadratic_points(r, q[1], p2, tolSqd, prev, head, pointsLeft, alloc); + return prev; +} + +Vertex* generate_cubic_points(const SkPoint& p0, + const SkPoint& p1, + const SkPoint& p2, + const SkPoint& p3, + SkScalar tolSqd, + Vertex* prev, + Vertex** head, + int pointsLeft, + SkChunkAlloc& alloc) { + SkScalar d1 = p1.distanceToLineSegmentBetweenSqd(p0, p3); + SkScalar d2 = p2.distanceToLineSegmentBetweenSqd(p0, p3); + if (pointsLeft < 2 || (d1 < tolSqd && d2 < tolSqd) || + !SkScalarIsFinite(d1) || !SkScalarIsFinite(d2)) { + return append_point_to_contour(p3, prev, head, alloc); + } + const SkPoint q[] = { + { SkScalarAve(p0.fX, p1.fX), SkScalarAve(p0.fY, p1.fY) }, + { SkScalarAve(p1.fX, p2.fX), SkScalarAve(p1.fY, p2.fY) }, + { SkScalarAve(p2.fX, p3.fX), SkScalarAve(p2.fY, p3.fY) } + }; + const SkPoint r[] = { + { SkScalarAve(q[0].fX, q[1].fX), SkScalarAve(q[0].fY, q[1].fY) }, + { SkScalarAve(q[1].fX, q[2].fX), SkScalarAve(q[1].fY, q[2].fY) } + }; + const SkPoint s = { SkScalarAve(r[0].fX, r[1].fX), SkScalarAve(r[0].fY, r[1].fY) }; + pointsLeft >>= 1; + prev = generate_cubic_points(p0, q[0], r[0], s, tolSqd, prev, head, pointsLeft, alloc); + prev = generate_cubic_points(s, r[1], q[2], p3, tolSqd, prev, head, pointsLeft, alloc); + return prev; +} + +// Stage 1: convert the input path to a set of linear contours (linked list of Vertices). + +void path_to_contours(const SkPath& path, SkScalar tolerance, const SkRect& clipBounds, + Vertex** contours, SkChunkAlloc& alloc, bool *isLinear) { + SkScalar toleranceSqd = tolerance * tolerance; + + SkPoint pts[4]; + bool done = false; + *isLinear = true; + SkPath::Iter iter(path, false); + Vertex* prev = nullptr; + Vertex* head = nullptr; + if (path.isInverseFillType()) { + SkPoint quad[4]; + clipBounds.toQuad(quad); + for (int i = 3; i >= 0; i--) { + prev = append_point_to_contour(quad[i], prev, &head, alloc); + } + head->fPrev = prev; + prev->fNext = head; + *contours++ = head; + head = prev = nullptr; + } + SkAutoConicToQuads converter; + while (!done) { + SkPath::Verb verb = iter.next(pts); + switch (verb) { + case SkPath::kConic_Verb: { + SkScalar weight = iter.conicWeight(); + const SkPoint* quadPts = converter.computeQuads(pts, weight, toleranceSqd); + for (int i = 0; i < converter.countQuads(); ++i) { + int pointsLeft = GrPathUtils::quadraticPointCount(quadPts, tolerance); + prev = generate_quadratic_points(quadPts[0], quadPts[1], quadPts[2], + toleranceSqd, prev, &head, pointsLeft, alloc); + quadPts += 2; + } + *isLinear = false; + break; + } + case SkPath::kMove_Verb: + if (head) { + head->fPrev = prev; + prev->fNext = head; + *contours++ = head; + } + head = prev = nullptr; + prev = append_point_to_contour(pts[0], prev, &head, alloc); + break; + case SkPath::kLine_Verb: { + prev = append_point_to_contour(pts[1], prev, &head, alloc); + break; + } + case SkPath::kQuad_Verb: { + int pointsLeft = GrPathUtils::quadraticPointCount(pts, tolerance); + prev = generate_quadratic_points(pts[0], pts[1], pts[2], toleranceSqd, prev, + &head, pointsLeft, alloc); + *isLinear = false; + break; + } + case SkPath::kCubic_Verb: { + int pointsLeft = GrPathUtils::cubicPointCount(pts, tolerance); + prev = generate_cubic_points(pts[0], pts[1], pts[2], pts[3], + toleranceSqd, prev, &head, pointsLeft, alloc); + *isLinear = false; + break; + } + case SkPath::kClose_Verb: + if (head) { + head->fPrev = prev; + prev->fNext = head; + *contours++ = head; + } + head = prev = nullptr; + break; + case SkPath::kDone_Verb: + if (head) { + head->fPrev = prev; + prev->fNext = head; + *contours++ = head; + } + done = true; + break; + } + } +} + +inline bool apply_fill_type(SkPath::FillType fillType, int winding) { + switch (fillType) { + case SkPath::kWinding_FillType: + return winding != 0; + case SkPath::kEvenOdd_FillType: + return (winding & 1) != 0; + case SkPath::kInverseWinding_FillType: + return winding == 1; + case SkPath::kInverseEvenOdd_FillType: + return (winding & 1) == 1; + default: + SkASSERT(false); + return false; + } +} + +Edge* new_edge(Vertex* prev, Vertex* next, SkChunkAlloc& alloc, Comparator& c) { + int winding = c.sweep_lt(prev->fPoint, next->fPoint) ? 1 : -1; + Vertex* top = winding < 0 ? next : prev; + Vertex* bottom = winding < 0 ? prev : next; + return ALLOC_NEW(Edge, (top, bottom, winding), alloc); +} + +void remove_edge(Edge* edge, EdgeList* edges) { + LOG("removing edge %g -> %g\n", edge->fTop->fID, edge->fBottom->fID); + SkASSERT(edge->isActive(edges)); + remove(edge, &edges->fHead, &edges->fTail); +} + +void insert_edge(Edge* edge, Edge* prev, EdgeList* edges) { + LOG("inserting edge %g -> %g\n", edge->fTop->fID, edge->fBottom->fID); + SkASSERT(!edge->isActive(edges)); + Edge* next = prev ? prev->fRight : edges->fHead; + insert(edge, prev, next, &edges->fHead, &edges->fTail); +} + +void find_enclosing_edges(Vertex* v, EdgeList* edges, Edge** left, Edge** right) { + if (v->fFirstEdgeAbove) { + *left = v->fFirstEdgeAbove->fLeft; + *right = v->fLastEdgeAbove->fRight; + return; + } + Edge* next = nullptr; + Edge* prev; + for (prev = edges->fTail; prev != nullptr; prev = prev->fLeft) { + if (prev->isLeftOf(v)) { + break; + } + next = prev; + } + *left = prev; + *right = next; + return; +} + +void find_enclosing_edges(Edge* edge, EdgeList* edges, Comparator& c, Edge** left, Edge** right) { + Edge* prev = nullptr; + Edge* next; + for (next = edges->fHead; next != nullptr; next = next->fRight) { + if ((c.sweep_gt(edge->fTop->fPoint, next->fTop->fPoint) && next->isRightOf(edge->fTop)) || + (c.sweep_gt(next->fTop->fPoint, edge->fTop->fPoint) && edge->isLeftOf(next->fTop)) || + (c.sweep_lt(edge->fBottom->fPoint, next->fBottom->fPoint) && + next->isRightOf(edge->fBottom)) || + (c.sweep_lt(next->fBottom->fPoint, edge->fBottom->fPoint) && + edge->isLeftOf(next->fBottom))) { + break; + } + prev = next; + } + *left = prev; + *right = next; + return; +} + +void fix_active_state(Edge* edge, EdgeList* activeEdges, Comparator& c) { + if (edge->isActive(activeEdges)) { + if (edge->fBottom->fProcessed || !edge->fTop->fProcessed) { + remove_edge(edge, activeEdges); + } + } else if (edge->fTop->fProcessed && !edge->fBottom->fProcessed) { + Edge* left; + Edge* right; + find_enclosing_edges(edge, activeEdges, c, &left, &right); + insert_edge(edge, left, activeEdges); + } +} + +void insert_edge_above(Edge* edge, Vertex* v, Comparator& c) { + if (edge->fTop->fPoint == edge->fBottom->fPoint || + c.sweep_gt(edge->fTop->fPoint, edge->fBottom->fPoint)) { + return; + } + LOG("insert edge (%g -> %g) above vertex %g\n", edge->fTop->fID, edge->fBottom->fID, v->fID); + Edge* prev = nullptr; + Edge* next; + for (next = v->fFirstEdgeAbove; next; next = next->fNextEdgeAbove) { + if (next->isRightOf(edge->fTop)) { + break; + } + prev = next; + } + insert( + edge, prev, next, &v->fFirstEdgeAbove, &v->fLastEdgeAbove); +} + +void insert_edge_below(Edge* edge, Vertex* v, Comparator& c) { + if (edge->fTop->fPoint == edge->fBottom->fPoint || + c.sweep_gt(edge->fTop->fPoint, edge->fBottom->fPoint)) { + return; + } + LOG("insert edge (%g -> %g) below vertex %g\n", edge->fTop->fID, edge->fBottom->fID, v->fID); + Edge* prev = nullptr; + Edge* next; + for (next = v->fFirstEdgeBelow; next; next = next->fNextEdgeBelow) { + if (next->isRightOf(edge->fBottom)) { + break; + } + prev = next; + } + insert( + edge, prev, next, &v->fFirstEdgeBelow, &v->fLastEdgeBelow); +} + +void remove_edge_above(Edge* edge) { + LOG("removing edge (%g -> %g) above vertex %g\n", edge->fTop->fID, edge->fBottom->fID, + edge->fBottom->fID); + remove( + edge, &edge->fBottom->fFirstEdgeAbove, &edge->fBottom->fLastEdgeAbove); +} + +void remove_edge_below(Edge* edge) { + LOG("removing edge (%g -> %g) below vertex %g\n", edge->fTop->fID, edge->fBottom->fID, + edge->fTop->fID); + remove( + edge, &edge->fTop->fFirstEdgeBelow, &edge->fTop->fLastEdgeBelow); +} + +void erase_edge_if_zero_winding(Edge* edge, EdgeList* edges) { + if (edge->fWinding != 0) { + return; + } + LOG("erasing edge (%g -> %g)\n", edge->fTop->fID, edge->fBottom->fID); + remove_edge_above(edge); + remove_edge_below(edge); + if (edge->isActive(edges)) { + remove_edge(edge, edges); + } +} + +void merge_collinear_edges(Edge* edge, EdgeList* activeEdges, Comparator& c); + +void set_top(Edge* edge, Vertex* v, EdgeList* activeEdges, Comparator& c) { + remove_edge_below(edge); + edge->fTop = v; + edge->recompute(); + insert_edge_below(edge, v, c); + fix_active_state(edge, activeEdges, c); + merge_collinear_edges(edge, activeEdges, c); +} + +void set_bottom(Edge* edge, Vertex* v, EdgeList* activeEdges, Comparator& c) { + remove_edge_above(edge); + edge->fBottom = v; + edge->recompute(); + insert_edge_above(edge, v, c); + fix_active_state(edge, activeEdges, c); + merge_collinear_edges(edge, activeEdges, c); +} + +void merge_edges_above(Edge* edge, Edge* other, EdgeList* activeEdges, Comparator& c) { + if (coincident(edge->fTop->fPoint, other->fTop->fPoint)) { + LOG("merging coincident above edges (%g, %g) -> (%g, %g)\n", + edge->fTop->fPoint.fX, edge->fTop->fPoint.fY, + edge->fBottom->fPoint.fX, edge->fBottom->fPoint.fY); + other->fWinding += edge->fWinding; + erase_edge_if_zero_winding(other, activeEdges); + edge->fWinding = 0; + erase_edge_if_zero_winding(edge, activeEdges); + } else if (c.sweep_lt(edge->fTop->fPoint, other->fTop->fPoint)) { + other->fWinding += edge->fWinding; + erase_edge_if_zero_winding(other, activeEdges); + set_bottom(edge, other->fTop, activeEdges, c); + } else { + edge->fWinding += other->fWinding; + erase_edge_if_zero_winding(edge, activeEdges); + set_bottom(other, edge->fTop, activeEdges, c); + } +} + +void merge_edges_below(Edge* edge, Edge* other, EdgeList* activeEdges, Comparator& c) { + if (coincident(edge->fBottom->fPoint, other->fBottom->fPoint)) { + LOG("merging coincident below edges (%g, %g) -> (%g, %g)\n", + edge->fTop->fPoint.fX, edge->fTop->fPoint.fY, + edge->fBottom->fPoint.fX, edge->fBottom->fPoint.fY); + other->fWinding += edge->fWinding; + erase_edge_if_zero_winding(other, activeEdges); + edge->fWinding = 0; + erase_edge_if_zero_winding(edge, activeEdges); + } else if (c.sweep_lt(edge->fBottom->fPoint, other->fBottom->fPoint)) { + edge->fWinding += other->fWinding; + erase_edge_if_zero_winding(edge, activeEdges); + set_top(other, edge->fBottom, activeEdges, c); + } else { + other->fWinding += edge->fWinding; + erase_edge_if_zero_winding(other, activeEdges); + set_top(edge, other->fBottom, activeEdges, c); + } +} + +void merge_collinear_edges(Edge* edge, EdgeList* activeEdges, Comparator& c) { + if (edge->fPrevEdgeAbove && (edge->fTop == edge->fPrevEdgeAbove->fTop || + !edge->fPrevEdgeAbove->isLeftOf(edge->fTop))) { + merge_edges_above(edge, edge->fPrevEdgeAbove, activeEdges, c); + } else if (edge->fNextEdgeAbove && (edge->fTop == edge->fNextEdgeAbove->fTop || + !edge->isLeftOf(edge->fNextEdgeAbove->fTop))) { + merge_edges_above(edge, edge->fNextEdgeAbove, activeEdges, c); + } + if (edge->fPrevEdgeBelow && (edge->fBottom == edge->fPrevEdgeBelow->fBottom || + !edge->fPrevEdgeBelow->isLeftOf(edge->fBottom))) { + merge_edges_below(edge, edge->fPrevEdgeBelow, activeEdges, c); + } else if (edge->fNextEdgeBelow && (edge->fBottom == edge->fNextEdgeBelow->fBottom || + !edge->isLeftOf(edge->fNextEdgeBelow->fBottom))) { + merge_edges_below(edge, edge->fNextEdgeBelow, activeEdges, c); + } +} + +void split_edge(Edge* edge, Vertex* v, EdgeList* activeEdges, Comparator& c, SkChunkAlloc& alloc); + +void cleanup_active_edges(Edge* edge, EdgeList* activeEdges, Comparator& c, SkChunkAlloc& alloc) { + Vertex* top = edge->fTop; + Vertex* bottom = edge->fBottom; + if (edge->fLeft) { + Vertex* leftTop = edge->fLeft->fTop; + Vertex* leftBottom = edge->fLeft->fBottom; + if (c.sweep_gt(top->fPoint, leftTop->fPoint) && !edge->fLeft->isLeftOf(top)) { + split_edge(edge->fLeft, edge->fTop, activeEdges, c, alloc); + } else if (c.sweep_gt(leftTop->fPoint, top->fPoint) && !edge->isRightOf(leftTop)) { + split_edge(edge, leftTop, activeEdges, c, alloc); + } else if (c.sweep_lt(bottom->fPoint, leftBottom->fPoint) && + !edge->fLeft->isLeftOf(bottom)) { + split_edge(edge->fLeft, bottom, activeEdges, c, alloc); + } else if (c.sweep_lt(leftBottom->fPoint, bottom->fPoint) && !edge->isRightOf(leftBottom)) { + split_edge(edge, leftBottom, activeEdges, c, alloc); + } + } + if (edge->fRight) { + Vertex* rightTop = edge->fRight->fTop; + Vertex* rightBottom = edge->fRight->fBottom; + if (c.sweep_gt(top->fPoint, rightTop->fPoint) && !edge->fRight->isRightOf(top)) { + split_edge(edge->fRight, top, activeEdges, c, alloc); + } else if (c.sweep_gt(rightTop->fPoint, top->fPoint) && !edge->isLeftOf(rightTop)) { + split_edge(edge, rightTop, activeEdges, c, alloc); + } else if (c.sweep_lt(bottom->fPoint, rightBottom->fPoint) && + !edge->fRight->isRightOf(bottom)) { + split_edge(edge->fRight, bottom, activeEdges, c, alloc); + } else if (c.sweep_lt(rightBottom->fPoint, bottom->fPoint) && + !edge->isLeftOf(rightBottom)) { + split_edge(edge, rightBottom, activeEdges, c, alloc); + } + } +} + +void split_edge(Edge* edge, Vertex* v, EdgeList* activeEdges, Comparator& c, SkChunkAlloc& alloc) { + LOG("splitting edge (%g -> %g) at vertex %g (%g, %g)\n", + edge->fTop->fID, edge->fBottom->fID, + v->fID, v->fPoint.fX, v->fPoint.fY); + if (c.sweep_lt(v->fPoint, edge->fTop->fPoint)) { + set_top(edge, v, activeEdges, c); + } else if (c.sweep_gt(v->fPoint, edge->fBottom->fPoint)) { + set_bottom(edge, v, activeEdges, c); + } else { + Edge* newEdge = ALLOC_NEW(Edge, (v, edge->fBottom, edge->fWinding), alloc); + insert_edge_below(newEdge, v, c); + insert_edge_above(newEdge, edge->fBottom, c); + set_bottom(edge, v, activeEdges, c); + cleanup_active_edges(edge, activeEdges, c, alloc); + fix_active_state(newEdge, activeEdges, c); + merge_collinear_edges(newEdge, activeEdges, c); + } +} + +void merge_vertices(Vertex* src, Vertex* dst, Vertex** head, Comparator& c, SkChunkAlloc& alloc) { + LOG("found coincident verts at %g, %g; merging %g into %g\n", src->fPoint.fX, src->fPoint.fY, + src->fID, dst->fID); + for (Edge* edge = src->fFirstEdgeAbove; edge;) { + Edge* next = edge->fNextEdgeAbove; + set_bottom(edge, dst, nullptr, c); + edge = next; + } + for (Edge* edge = src->fFirstEdgeBelow; edge;) { + Edge* next = edge->fNextEdgeBelow; + set_top(edge, dst, nullptr, c); + edge = next; + } + remove(src, head, nullptr); +} + +Vertex* check_for_intersection(Edge* edge, Edge* other, EdgeList* activeEdges, Comparator& c, + SkChunkAlloc& alloc) { + SkPoint p; + if (!edge || !other) { + return nullptr; + } + if (edge->intersect(*other, &p)) { + Vertex* v; + LOG("found intersection, pt is %g, %g\n", p.fX, p.fY); + if (p == edge->fTop->fPoint || c.sweep_lt(p, edge->fTop->fPoint)) { + split_edge(other, edge->fTop, activeEdges, c, alloc); + v = edge->fTop; + } else if (p == edge->fBottom->fPoint || c.sweep_gt(p, edge->fBottom->fPoint)) { + split_edge(other, edge->fBottom, activeEdges, c, alloc); + v = edge->fBottom; + } else if (p == other->fTop->fPoint || c.sweep_lt(p, other->fTop->fPoint)) { + split_edge(edge, other->fTop, activeEdges, c, alloc); + v = other->fTop; + } else if (p == other->fBottom->fPoint || c.sweep_gt(p, other->fBottom->fPoint)) { + split_edge(edge, other->fBottom, activeEdges, c, alloc); + v = other->fBottom; + } else { + Vertex* nextV = edge->fTop; + while (c.sweep_lt(p, nextV->fPoint)) { + nextV = nextV->fPrev; + } + while (c.sweep_lt(nextV->fPoint, p)) { + nextV = nextV->fNext; + } + Vertex* prevV = nextV->fPrev; + if (coincident(prevV->fPoint, p)) { + v = prevV; + } else if (coincident(nextV->fPoint, p)) { + v = nextV; + } else { + v = ALLOC_NEW(Vertex, (p), alloc); + LOG("inserting between %g (%g, %g) and %g (%g, %g)\n", + prevV->fID, prevV->fPoint.fX, prevV->fPoint.fY, + nextV->fID, nextV->fPoint.fX, nextV->fPoint.fY); +#if LOGGING_ENABLED + v->fID = (nextV->fID + prevV->fID) * 0.5f; +#endif + v->fPrev = prevV; + v->fNext = nextV; + prevV->fNext = v; + nextV->fPrev = v; + } + split_edge(edge, v, activeEdges, c, alloc); + split_edge(other, v, activeEdges, c, alloc); + } + return v; + } + return nullptr; +} + +void sanitize_contours(Vertex** contours, int contourCnt) { + for (int i = 0; i < contourCnt; ++i) { + SkASSERT(contours[i]); + for (Vertex* v = contours[i];;) { + if (coincident(v->fPrev->fPoint, v->fPoint)) { + LOG("vertex %g,%g coincident; removing\n", v->fPoint.fX, v->fPoint.fY); + if (v->fPrev == v) { + contours[i] = nullptr; + break; + } + v->fPrev->fNext = v->fNext; + v->fNext->fPrev = v->fPrev; + if (contours[i] == v) { + contours[i] = v->fNext; + } + v = v->fPrev; + } else { + v = v->fNext; + if (v == contours[i]) break; + } + } + } +} + +void merge_coincident_vertices(Vertex** vertices, Comparator& c, SkChunkAlloc& alloc) { + for (Vertex* v = (*vertices)->fNext; v != nullptr; v = v->fNext) { + if (c.sweep_lt(v->fPoint, v->fPrev->fPoint)) { + v->fPoint = v->fPrev->fPoint; + } + if (coincident(v->fPrev->fPoint, v->fPoint)) { + merge_vertices(v->fPrev, v, vertices, c, alloc); + } + } +} + +// Stage 2: convert the contours to a mesh of edges connecting the vertices. + +Vertex* build_edges(Vertex** contours, int contourCnt, Comparator& c, SkChunkAlloc& alloc) { + Vertex* vertices = nullptr; + Vertex* prev = nullptr; + for (int i = 0; i < contourCnt; ++i) { + for (Vertex* v = contours[i]; v != nullptr;) { + Vertex* vNext = v->fNext; + Edge* edge = new_edge(v->fPrev, v, alloc, c); + if (edge->fWinding > 0) { + insert_edge_below(edge, v->fPrev, c); + insert_edge_above(edge, v, c); + } else { + insert_edge_below(edge, v, c); + insert_edge_above(edge, v->fPrev, c); + } + merge_collinear_edges(edge, nullptr, c); + if (prev) { + prev->fNext = v; + v->fPrev = prev; + } else { + vertices = v; + } + prev = v; + v = vNext; + if (v == contours[i]) break; + } + } + if (prev) { + prev->fNext = vertices->fPrev = nullptr; + } + return vertices; +} + +// Stage 3: sort the vertices by increasing sweep direction. + +Vertex* sorted_merge(Vertex* a, Vertex* b, Comparator& c); + +void front_back_split(Vertex* v, Vertex** pFront, Vertex** pBack) { + Vertex* fast; + Vertex* slow; + if (!v || !v->fNext) { + *pFront = v; + *pBack = nullptr; + } else { + slow = v; + fast = v->fNext; + + while (fast != nullptr) { + fast = fast->fNext; + if (fast != nullptr) { + slow = slow->fNext; + fast = fast->fNext; + } + } + + *pFront = v; + *pBack = slow->fNext; + slow->fNext->fPrev = nullptr; + slow->fNext = nullptr; + } +} + +void merge_sort(Vertex** head, Comparator& c) { + if (!*head || !(*head)->fNext) { + return; + } + + Vertex* a; + Vertex* b; + front_back_split(*head, &a, &b); + + merge_sort(&a, c); + merge_sort(&b, c); + + *head = sorted_merge(a, b, c); +} + +inline void append_vertex(Vertex* v, Vertex** head, Vertex** tail) { + insert(v, *tail, nullptr, head, tail); +} + +inline void append_vertex_list(Vertex* v, Vertex** head, Vertex** tail) { + insert(v, *tail, v->fNext, head, tail); +} + +Vertex* sorted_merge(Vertex* a, Vertex* b, Comparator& c) { + Vertex* head = nullptr; + Vertex* tail = nullptr; + + while (a && b) { + if (c.sweep_lt(a->fPoint, b->fPoint)) { + Vertex* next = a->fNext; + append_vertex(a, &head, &tail); + a = next; + } else { + Vertex* next = b->fNext; + append_vertex(b, &head, &tail); + b = next; + } + } + if (a) { + append_vertex_list(a, &head, &tail); + } + if (b) { + append_vertex_list(b, &head, &tail); + } + return head; +} + +// Stage 4: Simplify the mesh by inserting new vertices at intersecting edges. + +void simplify(Vertex* vertices, Comparator& c, SkChunkAlloc& alloc) { + LOG("simplifying complex polygons\n"); + EdgeList activeEdges; + for (Vertex* v = vertices; v != nullptr; v = v->fNext) { + if (!v->fFirstEdgeAbove && !v->fFirstEdgeBelow) { + continue; + } +#if LOGGING_ENABLED + LOG("\nvertex %g: (%g,%g)\n", v->fID, v->fPoint.fX, v->fPoint.fY); +#endif + Edge* leftEnclosingEdge = nullptr; + Edge* rightEnclosingEdge = nullptr; + bool restartChecks; + do { + restartChecks = false; + find_enclosing_edges(v, &activeEdges, &leftEnclosingEdge, &rightEnclosingEdge); + if (v->fFirstEdgeBelow) { + for (Edge* edge = v->fFirstEdgeBelow; edge != nullptr; edge = edge->fNextEdgeBelow) { + if (check_for_intersection(edge, leftEnclosingEdge, &activeEdges, c, alloc)) { + restartChecks = true; + break; + } + if (check_for_intersection(edge, rightEnclosingEdge, &activeEdges, c, alloc)) { + restartChecks = true; + break; + } + } + } else { + if (Vertex* pv = check_for_intersection(leftEnclosingEdge, rightEnclosingEdge, + &activeEdges, c, alloc)) { + if (c.sweep_lt(pv->fPoint, v->fPoint)) { + v = pv; + } + restartChecks = true; + } + + } + } while (restartChecks); + for (Edge* e = v->fFirstEdgeAbove; e; e = e->fNextEdgeAbove) { + remove_edge(e, &activeEdges); + } + Edge* leftEdge = leftEnclosingEdge; + for (Edge* e = v->fFirstEdgeBelow; e; e = e->fNextEdgeBelow) { + insert_edge(e, leftEdge, &activeEdges); + leftEdge = e; + } + v->fProcessed = true; + } +} + +// Stage 5: Tessellate the simplified mesh into monotone polygons. + +Poly* tessellate(Vertex* vertices, SkChunkAlloc& alloc) { + LOG("tessellating simple polygons\n"); + EdgeList activeEdges; + Poly* polys = nullptr; + for (Vertex* v = vertices; v != nullptr; v = v->fNext) { + if (!v->fFirstEdgeAbove && !v->fFirstEdgeBelow) { + continue; + } +#if LOGGING_ENABLED + LOG("\nvertex %g: (%g,%g)\n", v->fID, v->fPoint.fX, v->fPoint.fY); +#endif + Edge* leftEnclosingEdge = nullptr; + Edge* rightEnclosingEdge = nullptr; + find_enclosing_edges(v, &activeEdges, &leftEnclosingEdge, &rightEnclosingEdge); + Poly* leftPoly = nullptr; + Poly* rightPoly = nullptr; + if (v->fFirstEdgeAbove) { + leftPoly = v->fFirstEdgeAbove->fLeftPoly; + rightPoly = v->fLastEdgeAbove->fRightPoly; + } else { + leftPoly = leftEnclosingEdge ? leftEnclosingEdge->fRightPoly : nullptr; + rightPoly = rightEnclosingEdge ? rightEnclosingEdge->fLeftPoly : nullptr; + } +#if LOGGING_ENABLED + LOG("edges above:\n"); + for (Edge* e = v->fFirstEdgeAbove; e; e = e->fNextEdgeAbove) { + LOG("%g -> %g, lpoly %d, rpoly %d\n", e->fTop->fID, e->fBottom->fID, + e->fLeftPoly ? e->fLeftPoly->fID : -1, e->fRightPoly ? e->fRightPoly->fID : -1); + } + LOG("edges below:\n"); + for (Edge* e = v->fFirstEdgeBelow; e; e = e->fNextEdgeBelow) { + LOG("%g -> %g, lpoly %d, rpoly %d\n", e->fTop->fID, e->fBottom->fID, + e->fLeftPoly ? e->fLeftPoly->fID : -1, e->fRightPoly ? e->fRightPoly->fID : -1); + } +#endif + if (v->fFirstEdgeAbove) { + if (leftPoly) { + leftPoly = leftPoly->addVertex(v, Poly::kRight_Side, alloc); + } + if (rightPoly) { + rightPoly = rightPoly->addVertex(v, Poly::kLeft_Side, alloc); + } + for (Edge* e = v->fFirstEdgeAbove; e != v->fLastEdgeAbove; e = e->fNextEdgeAbove) { + Edge* leftEdge = e; + Edge* rightEdge = e->fNextEdgeAbove; + SkASSERT(rightEdge->isRightOf(leftEdge->fTop)); + remove_edge(leftEdge, &activeEdges); + if (leftEdge->fRightPoly) { + leftEdge->fRightPoly->end(v, alloc); + } + if (rightEdge->fLeftPoly && rightEdge->fLeftPoly != leftEdge->fRightPoly) { + rightEdge->fLeftPoly->end(v, alloc); + } + } + remove_edge(v->fLastEdgeAbove, &activeEdges); + if (!v->fFirstEdgeBelow) { + if (leftPoly && rightPoly && leftPoly != rightPoly) { + SkASSERT(leftPoly->fPartner == nullptr && rightPoly->fPartner == nullptr); + rightPoly->fPartner = leftPoly; + leftPoly->fPartner = rightPoly; + } + } + } + if (v->fFirstEdgeBelow) { + if (!v->fFirstEdgeAbove) { + if (leftPoly && leftPoly == rightPoly) { + // Split the poly. + if (leftPoly->fActive->fSide == Poly::kLeft_Side) { + leftPoly = new_poly(&polys, leftEnclosingEdge->fTop, leftPoly->fWinding, + alloc); + leftPoly->addVertex(v, Poly::kRight_Side, alloc); + rightPoly->addVertex(v, Poly::kLeft_Side, alloc); + leftEnclosingEdge->fRightPoly = leftPoly; + } else { + rightPoly = new_poly(&polys, rightEnclosingEdge->fTop, rightPoly->fWinding, + alloc); + rightPoly->addVertex(v, Poly::kLeft_Side, alloc); + leftPoly->addVertex(v, Poly::kRight_Side, alloc); + rightEnclosingEdge->fLeftPoly = rightPoly; + } + } else { + if (leftPoly) { + leftPoly = leftPoly->addVertex(v, Poly::kRight_Side, alloc); + } + if (rightPoly) { + rightPoly = rightPoly->addVertex(v, Poly::kLeft_Side, alloc); + } + } + } + Edge* leftEdge = v->fFirstEdgeBelow; + leftEdge->fLeftPoly = leftPoly; + insert_edge(leftEdge, leftEnclosingEdge, &activeEdges); + for (Edge* rightEdge = leftEdge->fNextEdgeBelow; rightEdge; + rightEdge = rightEdge->fNextEdgeBelow) { + insert_edge(rightEdge, leftEdge, &activeEdges); + int winding = leftEdge->fLeftPoly ? leftEdge->fLeftPoly->fWinding : 0; + winding += leftEdge->fWinding; + if (winding != 0) { + Poly* poly = new_poly(&polys, v, winding, alloc); + leftEdge->fRightPoly = rightEdge->fLeftPoly = poly; + } + leftEdge = rightEdge; + } + v->fLastEdgeBelow->fRightPoly = rightPoly; + } +#if LOGGING_ENABLED + LOG("\nactive edges:\n"); + for (Edge* e = activeEdges.fHead; e != nullptr; e = e->fRight) { + LOG("%g -> %g, lpoly %d, rpoly %d\n", e->fTop->fID, e->fBottom->fID, + e->fLeftPoly ? e->fLeftPoly->fID : -1, e->fRightPoly ? e->fRightPoly->fID : -1); + } +#endif + } + return polys; +} + +// This is a driver function which calls stages 2-5 in turn. + +Poly* contours_to_polys(Vertex** contours, int contourCnt, const SkRect& pathBounds, + SkChunkAlloc& alloc) { + Comparator c; + if (pathBounds.width() > pathBounds.height()) { + c.sweep_lt = sweep_lt_horiz; + c.sweep_gt = sweep_gt_horiz; + } else { + c.sweep_lt = sweep_lt_vert; + c.sweep_gt = sweep_gt_vert; + } +#if LOGGING_ENABLED + for (int i = 0; i < contourCnt; ++i) { + Vertex* v = contours[i]; + SkASSERT(v); + LOG("path.moveTo(%20.20g, %20.20g);\n", v->fPoint.fX, v->fPoint.fY); + for (v = v->fNext; v != contours[i]; v = v->fNext) { + LOG("path.lineTo(%20.20g, %20.20g);\n", v->fPoint.fX, v->fPoint.fY); + } + } +#endif + sanitize_contours(contours, contourCnt); + Vertex* vertices = build_edges(contours, contourCnt, c, alloc); + if (!vertices) { + return nullptr; + } + + // Sort vertices in Y (secondarily in X). + merge_sort(&vertices, c); + merge_coincident_vertices(&vertices, c, alloc); +#if LOGGING_ENABLED + for (Vertex* v = vertices; v != nullptr; v = v->fNext) { + static float gID = 0.0f; + v->fID = gID++; + } +#endif + simplify(vertices, c, alloc); + return tessellate(vertices, alloc); +} + +Poly* path_to_polys(const SkPath& path, SkScalar tolerance, const SkRect& clipBounds, + int contourCnt, SkChunkAlloc& alloc, bool* isLinear) { + SkPath::FillType fillType = path.getFillType(); + if (SkPath::IsInverseFillType(fillType)) { + contourCnt++; + } + SkAutoTDeleteArray contours(new Vertex* [contourCnt]); + + path_to_contours(path, tolerance, clipBounds, contours.get(), alloc, isLinear); + return contours_to_polys(contours.get(), contourCnt, path.getBounds(), alloc); +} + +void get_contour_count_and_size_estimate(const SkPath& path, SkScalar tolerance, int* contourCnt, + int* sizeEstimate) { + int maxPts = GrPathUtils::worstCasePointCount(path, contourCnt, tolerance); + if (maxPts <= 0) { + *contourCnt = 0; + return; + } + if (maxPts > ((int)SK_MaxU16 + 1)) { + SkDebugf("Path not rendered, too many verts (%d)\n", maxPts); + *contourCnt = 0; + return; + } + // For the initial size of the chunk allocator, estimate based on the point count: + // one vertex per point for the initial passes, plus two for the vertices in the + // resulting Polys, since the same point may end up in two Polys. Assume minimal + // connectivity of one Edge per Vertex (will grow for intersections). + *sizeEstimate = maxPts * (3 * sizeof(Vertex) + sizeof(Edge)); +} + +int count_points(Poly* polys, SkPath::FillType fillType) { + int count = 0; + for (Poly* poly = polys; poly; poly = poly->fNext) { + if (apply_fill_type(fillType, poly->fWinding) && poly->fCount >= 3) { + count += (poly->fCount - 2) * (TESSELLATOR_WIREFRAME ? 6 : 3); + } + } + return count; +} + +} // namespace + +namespace GrTessellator { + +// Stage 6: Triangulate the monotone polygons into a vertex buffer. + +int PathToTriangles(const SkPath& path, SkScalar tolerance, const SkRect& clipBounds, + GrResourceProvider* resourceProvider, + SkAutoTUnref& vertexBuffer, bool canMapVB, bool* isLinear) { + int contourCnt; + int sizeEstimate; + get_contour_count_and_size_estimate(path, tolerance, &contourCnt, &sizeEstimate); + if (contourCnt <= 0) { + *isLinear = true; + return 0; + } + SkChunkAlloc alloc(sizeEstimate); + Poly* polys = path_to_polys(path, tolerance, clipBounds, contourCnt, alloc, isLinear); + SkPath::FillType fillType = path.getFillType(); + int count = count_points(polys, fillType); + if (0 == count) { + return 0; + } + + size_t size = count * sizeof(SkPoint); + if (!vertexBuffer.get() || vertexBuffer->gpuMemorySize() < size) { + vertexBuffer.reset(resourceProvider->createVertexBuffer( + size, GrResourceProvider::kStatic_BufferUsage, 0)); + } + if (!vertexBuffer.get()) { + SkDebugf("Could not allocate vertices\n"); + return 0; + } + SkPoint* verts; + if (canMapVB) { + verts = static_cast(vertexBuffer->map()); + } else { + verts = new SkPoint[count]; + } + SkPoint* end = verts; + for (Poly* poly = polys; poly; poly = poly->fNext) { + if (apply_fill_type(fillType, poly->fWinding)) { + end = poly->emit(end); + } + } + int actualCount = static_cast(end - verts); + LOG("actual count: %d\n", actualCount); + SkASSERT(actualCount <= count); + if (canMapVB) { + vertexBuffer->unmap(); + } else { + vertexBuffer->updateData(verts, actualCount * sizeof(SkPoint)); + delete[] verts; + } + + return actualCount; +} + +int PathToVertices(const SkPath& path, SkScalar tolerance, const SkRect& clipBounds, + GrTessellator::WindingVertex** verts) { + int contourCnt; + int sizeEstimate; + get_contour_count_and_size_estimate(path, tolerance, &contourCnt, &sizeEstimate); + if (contourCnt <= 0) { + return 0; + } + SkChunkAlloc alloc(sizeEstimate); + bool isLinear; + Poly* polys = path_to_polys(path, tolerance, clipBounds, contourCnt, alloc, &isLinear); + SkPath::FillType fillType = path.getFillType(); + int count = count_points(polys, fillType); + if (0 == count) { + *verts = nullptr; + return 0; + } + + *verts = new GrTessellator::WindingVertex[count]; + GrTessellator::WindingVertex* vertsEnd = *verts; + SkPoint* points = new SkPoint[count]; + SkPoint* pointsEnd = points; + for (Poly* poly = polys; poly; poly = poly->fNext) { + if (apply_fill_type(fillType, poly->fWinding)) { + SkPoint* start = pointsEnd; + pointsEnd = poly->emit(pointsEnd); + while (start != pointsEnd) { + vertsEnd->fPos = *start; + vertsEnd->fWinding = poly->fWinding; + ++start; + ++vertsEnd; + } + } + } + int actualCount = static_cast(vertsEnd - *verts); + SkASSERT(actualCount <= count); + SkASSERT(pointsEnd - points == actualCount); + delete[] points; + return actualCount; +} + +} // namespace diff --git a/gfx/skia/skia/src/gpu/GrTessellator.h b/gfx/skia/skia/src/gpu/GrTessellator.h new file mode 100644 index 0000000000..4304920fbc --- /dev/null +++ b/gfx/skia/skia/src/gpu/GrTessellator.h @@ -0,0 +1,40 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef GrTessellator_DEFINED +#define GrTessellator_DEFINED + +#include "SkPath.h" +#include "GrResourceProvider.h" + +/** + * Provides utility functions for converting paths to a collection of triangles. + */ + +#define TESSELLATOR_WIREFRAME 0 + +namespace GrTessellator { + +struct WindingVertex { + SkPoint fPos; + int fWinding; +}; + +// Triangulates a path to an array of vertices. Each triangle is represented as a set of three +// WindingVertex entries, each of which contains the position and winding count (which is the same +// for all three vertices of a triangle). The 'verts' out parameter is set to point to the resultant +// vertex array. CALLER IS RESPONSIBLE for deleting this buffer to avoid a memory leak! +int PathToVertices(const SkPath& path, SkScalar tolerance, const SkRect& clipBounds, + WindingVertex** verts); + +int PathToTriangles(const SkPath& path, SkScalar tolerance, const SkRect& clipBounds, + GrResourceProvider* resourceProvider, + SkAutoTUnref& vertexBuffer, bool canMapVB, bool* isLinear); + +} + +#endif diff --git a/gfx/skia/skia/src/gpu/GrTest.cpp b/gfx/skia/skia/src/gpu/GrTest.cpp index b1c135474d..8174d76779 100644 --- a/gfx/skia/skia/src/gpu/GrTest.cpp +++ b/gfx/skia/skia/src/gpu/GrTest.cpp @@ -9,16 +9,19 @@ #include "GrTest.h" #include "GrBatchAtlas.h" -#include "GrBatchFontCache.h" #include "GrContextOptions.h" #include "GrDrawContext.h" #include "GrDrawingManager.h" #include "GrGpuResourceCacheAccess.h" #include "GrResourceCache.h" -#include "GrTextBlobCache.h" + +#include "SkGpuDevice.h" #include "SkGrPriv.h" #include "SkString.h" +#include "text/GrBatchFontCache.h" +#include "text/GrTextBlobCache.h" + namespace GrTest { void SetupAlwaysEvictAtlas(GrContext* context) { // These sizes were selected because they allow each atlas to hold a single plot and will thus @@ -137,41 +140,40 @@ void GrContext::printGpuStats() const { SkDebugf("%s", out.c_str()); } -void GrContext::drawFontCache(const SkRect& rect, GrMaskFormat format, const SkPaint& paint, - GrRenderTarget* target) { +GrTexture* GrContext::getFontAtlasTexture(GrMaskFormat format) { GrBatchFontCache* cache = this->getBatchFontCache(); - GrTexture* atlas = cache->getTexture(format); - - SkAutoTUnref drawContext(this->drawContext(target)); - // TODO: add drawContext method to encapsulate this. + return cache->getTexture(format); +} +void SkGpuDevice::drawTexture(GrTexture* tex, const SkRect& dst, const SkPaint& paint) { GrPaint grPaint; SkMatrix mat; mat.reset(); - if (!SkPaintToGrPaint(this, paint, mat, &grPaint)) { + if (!SkPaintToGrPaint(this->context(), paint, mat, &grPaint)) { return; } SkMatrix textureMat; textureMat.reset(); - // TODO: use setScaleTranslate() - textureMat[SkMatrix::kMScaleX] = 1.0f/rect.width(); - textureMat[SkMatrix::kMScaleY] = 1.0f/rect.height(); - textureMat[SkMatrix::kMTransX] = -rect.fLeft/rect.width(); - textureMat[SkMatrix::kMTransY] = -rect.fTop/rect.height(); + textureMat[SkMatrix::kMScaleX] = 1.0f/dst.width(); + textureMat[SkMatrix::kMScaleY] = 1.0f/dst.height(); + textureMat[SkMatrix::kMTransX] = -dst.fLeft/dst.width(); + textureMat[SkMatrix::kMTransY] = -dst.fTop/dst.height(); - grPaint.addColorTextureProcessor(atlas, textureMat); + grPaint.addColorTextureProcessor(tex, textureMat); GrClip clip; - drawContext->drawRect(clip, grPaint, mat, rect); + fDrawContext->drawRect(clip, grPaint, mat, dst); } + #if GR_GPU_STATS void GrGpu::Stats::dump(SkString* out) { out->appendf("Render Target Binds: %d\n", fRenderTargetBinds); out->appendf("Shader Compilations: %d\n", fShaderCompilations); out->appendf("Textures Created: %d\n", fTextureCreates); out->appendf("Texture Uploads: %d\n", fTextureUploads); + out->appendf("Transfers to Texture: %d\n", fTransfersToTexture); out->appendf("Stencil Buffer Creates: %d\n", fStencilAttachmentCreates); out->appendf("Number of draws: %d\n", fNumDraws); } @@ -181,6 +183,7 @@ void GrGpu::Stats::dumpKeyValuePairs(SkTArray* keys, SkTArray* keys->push_back(SkString("shader_compilations")); values->push_back(fShaderCompilations); keys->push_back(SkString("textures_created")); values->push_back(fTextureCreates); keys->push_back(SkString("texture_uploads")); values->push_back(fTextureUploads); + keys->push_back(SkString("transfers_to_texture")); values->push_back(fTransfersToTexture); keys->push_back(SkString("stencil_buffer_creates")); values->push_back(fStencilAttachmentCreates); keys->push_back(SkString("number_of_draws")); values->push_back(fNumDraws); } @@ -247,6 +250,25 @@ void GrResourceCache::dumpStatsKeyValuePairs(SkTArray* keys, void GrResourceCache::changeTimestamp(uint32_t newTimestamp) { fTimestamp = newTimestamp; } +/////////////////////////////////////////////////////////////////////////////// + +#define ASSERT_SINGLE_OWNER \ + SkDEBUGCODE(GrSingleOwner::AutoEnforce debug_SingleOwner(fSingleOwner);) +#define RETURN_IF_ABANDONED if (fDrawingManager->abandoned()) { return; } + +void GrDrawContext::internal_drawBatch(const GrPipelineBuilder& pipelineBuilder, + GrDrawBatch* batch) { + ASSERT_SINGLE_OWNER + RETURN_IF_ABANDONED + SkDEBUGCODE(this->validate();) + GR_AUDIT_TRAIL_AUTO_FRAME(fAuditTrail, "GrDrawContext::internal_drawBatch"); + + this->getDrawTarget()->drawBatch(pipelineBuilder, batch); +} + +#undef ASSERT_SINGLE_OWNER +#undef RETURN_IF_ABANDONED + /////////////////////////////////////////////////////////////////////////////// // Code for the mock context. It's built on a mock GrGpu class that does nothing. //// @@ -255,10 +277,19 @@ void GrResourceCache::changeTimestamp(uint32_t newTimestamp) { fTimestamp = newT class GrPipeline; +class MockCaps : public GrCaps { +public: + explicit MockCaps(const GrContextOptions& options) : INHERITED(options) {} + bool isConfigTexturable(GrPixelConfig config) const override { return false; } + bool isConfigRenderable(GrPixelConfig config, bool withMSAA) const override { return false; } +private: + typedef GrCaps INHERITED; +}; + class MockGpu : public GrGpu { public: MockGpu(GrContext* context, const GrContextOptions& options) : INHERITED(context) { - fCaps.reset(new GrCaps(options)); + fCaps.reset(new MockCaps(options)); } ~MockGpu() override {} @@ -284,6 +315,8 @@ public: return false; } + void drawDebugWireRect(GrRenderTarget*, const SkIRect&, GrColor) override {}; + private: void onResetContext(uint32_t resetBits) override {} @@ -311,6 +344,8 @@ private: GrIndexBuffer* onCreateIndexBuffer(size_t size, bool dynamic) override { return nullptr; } + GrTransferBuffer* onCreateTransferBuffer(size_t, TransferType) override { return nullptr; } + void onClear(GrRenderTarget*, const SkIRect& rect, GrColor color) override {} void onClearStencilClip(GrRenderTarget*, const SkIRect& rect, bool insideClip) override {} @@ -332,6 +367,13 @@ private: return false; } + bool onTransferPixels(GrSurface* surface, + int left, int top, int width, int height, + GrPixelConfig config, GrTransferBuffer* buffer, + size_t offset, size_t rowBytes) override { + return false; + } + void onResolveRenderTarget(GrRenderTarget* target) override { return; } GrStencilAttachment* createStencilAttachmentForRenderTarget(const GrRenderTarget*, diff --git a/gfx/skia/skia/src/gpu/GrTest.h b/gfx/skia/skia/src/gpu/GrTest.h index 297f51c113..75eb53ac64 100644 --- a/gfx/skia/skia/src/gpu/GrTest.h +++ b/gfx/skia/skia/src/gpu/GrTest.h @@ -21,7 +21,8 @@ namespace GrTest { void SetupAlwaysEvictAtlas(GrContext*); }; -/** Allows a test to temporarily draw to a GrDrawTarget owned by a GrContext. Tests that use this +/** TODO Please do not use this if you can avoid it. We are in the process of deleting it. + Allows a test to temporarily draw to a GrDrawTarget owned by a GrContext. Tests that use this should be careful not to mix using the GrDrawTarget directly and drawing via SkCanvas or GrContext. In the future this object may provide some guards to prevent this. */ class GrTestTarget { diff --git a/gfx/skia/skia/src/gpu/GrTextureAccess.cpp b/gfx/skia/skia/src/gpu/GrTextureAccess.cpp index 7e1eda6211..277c0e3833 100644 --- a/gfx/skia/skia/src/gpu/GrTextureAccess.cpp +++ b/gfx/skia/skia/src/gpu/GrTextureAccess.cpp @@ -9,12 +9,7 @@ #include "GrColor.h" #include "GrTexture.h" -GrTextureAccess::GrTextureAccess() { -#ifdef SK_DEBUG - memcpy(fSwizzle, "void", 5); - fSwizzleMask = 0xbeeffeed; -#endif -} +GrTextureAccess::GrTextureAccess() {} GrTextureAccess::GrTextureAccess(GrTexture* texture, const GrTextureParams& params) { this->reset(texture, params); @@ -26,49 +21,12 @@ GrTextureAccess::GrTextureAccess(GrTexture* texture, this->reset(texture, filterMode, tileXAndY); } -GrTextureAccess::GrTextureAccess(GrTexture* texture, - const char* swizzle, - const GrTextureParams& params) { - this->reset(texture, swizzle, params); -} - -GrTextureAccess::GrTextureAccess(GrTexture* texture, - const char* swizzle, - GrTextureParams::FilterMode filterMode, - SkShader::TileMode tileXAndY) { - this->reset(texture, swizzle, filterMode, tileXAndY); -} - -void GrTextureAccess::reset(GrTexture* texture, - const char* swizzle, - const GrTextureParams& params) { - SkASSERT(texture); - SkASSERT(strlen(swizzle) >= 1 && strlen(swizzle) <= 4); - - fParams = params; - fTexture.set(SkRef(texture), kRead_GrIOType); - this->setSwizzle(swizzle); -} - -void GrTextureAccess::reset(GrTexture* texture, - const char* swizzle, - GrTextureParams::FilterMode filterMode, - SkShader::TileMode tileXAndY) { - SkASSERT(texture); - SkASSERT(strlen(swizzle) >= 1 && strlen(swizzle) <= 4); - - fParams.reset(tileXAndY, filterMode); - fTexture.set(SkRef(texture), kRead_GrIOType); - this->setSwizzle(swizzle); -} void GrTextureAccess::reset(GrTexture* texture, const GrTextureParams& params) { SkASSERT(texture); fTexture.set(SkRef(texture), kRead_GrIOType); fParams = params; - memcpy(fSwizzle, "rgba", 5); - fSwizzleMask = kRGBA_GrColorComponentFlags; } void GrTextureAccess::reset(GrTexture* texture, @@ -77,31 +35,4 @@ void GrTextureAccess::reset(GrTexture* texture, SkASSERT(texture); fTexture.set(SkRef(texture), kRead_GrIOType); fParams.reset(tileXAndY, filterMode); - memcpy(fSwizzle, "rgba", 5); - fSwizzleMask = kRGBA_GrColorComponentFlags; -} - -void GrTextureAccess::setSwizzle(const char* swizzle) { - fSwizzleMask = 0; - memset(fSwizzle, '\0', 5); - for (int i = 0; i < 4 && '\0' != swizzle[i]; ++i) { - fSwizzle[i] = swizzle[i]; - switch (swizzle[i]) { - case 'r': - fSwizzleMask |= kR_GrColorComponentFlag; - break; - case 'g': - fSwizzleMask |= kG_GrColorComponentFlag; - break; - case 'b': - fSwizzleMask |= kB_GrColorComponentFlag; - break; - case 'a': - fSwizzleMask |= kA_GrColorComponentFlag; - break; - default: - SkFAIL("Unexpected swizzle string character."); - break; - } - } } diff --git a/gfx/skia/skia/src/gpu/GrTextureParamsAdjuster.cpp b/gfx/skia/skia/src/gpu/GrTextureParamsAdjuster.cpp index 2aedad57ef..336ab6b10c 100644 --- a/gfx/skia/skia/src/gpu/GrTextureParamsAdjuster.cpp +++ b/gfx/skia/skia/src/gpu/GrTextureParamsAdjuster.cpp @@ -121,8 +121,10 @@ static GrTexture* copy_on_gpu(GrTexture* inputTexture, const SkIRect* subset, return copy.detach(); } -GrTextureAdjuster::GrTextureAdjuster(GrTexture* original, const SkIRect& contentArea) - : INHERITED(contentArea.width(), contentArea.height()) +GrTextureAdjuster::GrTextureAdjuster(GrTexture* original, + const SkIRect& contentArea, + bool isAlphaOnly) + : INHERITED(contentArea.width(), contentArea.height(), isAlphaOnly) , fOriginal(original) { SkASSERT(SkIRect::MakeWH(original->width(), original->height()).contains(contentArea)); if (contentArea.fLeft > 0 || contentArea.fTop > 0 || diff --git a/gfx/skia/skia/src/gpu/GrTextureParamsAdjuster.h b/gfx/skia/skia/src/gpu/GrTextureParamsAdjuster.h index ed9f142af0..103bcbf720 100644 --- a/gfx/skia/skia/src/gpu/GrTextureParamsAdjuster.h +++ b/gfx/skia/skia/src/gpu/GrTextureParamsAdjuster.h @@ -70,9 +70,13 @@ public: int width() const { return fWidth; } int height() const { return fHeight; } + bool isAlphaOnly() const { return fIsAlphaOnly; } protected: - GrTextureProducer(int width, int height) : fWidth(width), fHeight(height) {} + GrTextureProducer(int width, int height, bool isAlphaOnly) + : fWidth(width) + , fHeight(height) + , fIsAlphaOnly(isAlphaOnly) {} /** Helper for creating a key for a copy from an original key. */ static void MakeCopyKeyFromOrigKey(const GrUniqueKey& origKey, @@ -104,8 +108,9 @@ protected: virtual void didCacheCopy(const GrUniqueKey& copyKey) = 0; private: - const int fWidth; - const int fHeight; + const int fWidth; + const int fHeight; + const bool fIsAlphaOnly; typedef SkNoncopyable INHERITED; }; @@ -133,11 +138,11 @@ public: protected: /** The whole texture is content. */ - explicit GrTextureAdjuster(GrTexture* original) - : INHERITED(original->width(), original->height()) + explicit GrTextureAdjuster(GrTexture* original, bool isAlphaOnly) + : INHERITED(original->width(), original->height(), isAlphaOnly) , fOriginal(original) {} - GrTextureAdjuster(GrTexture* original, const SkIRect& contentArea); + GrTextureAdjuster(GrTexture* original, const SkIRect& contentArea, bool isAlphaOnly); GrTexture* originalTexture() const { return fOriginal; } @@ -170,8 +175,8 @@ public: const GrTextureParams::FilterMode* filterOrNullForBicubic) override; protected: - GrTextureMaker(GrContext* context, int width, int height) - : INHERITED(width, height) + GrTextureMaker(GrContext* context, int width, int height, bool isAlphaOnly) + : INHERITED(width, height, isAlphaOnly) , fContext(context) {} /** diff --git a/gfx/skia/skia/src/gpu/GrTextureProvider.cpp b/gfx/skia/skia/src/gpu/GrTextureProvider.cpp index cb652f7a7f..1c2f36535a 100644 --- a/gfx/skia/skia/src/gpu/GrTextureProvider.cpp +++ b/gfx/skia/skia/src/gpu/GrTextureProvider.cpp @@ -10,6 +10,10 @@ #include "GrTexturePriv.h" #include "GrResourceCache.h" #include "GrGpu.h" +#include "../private/GrSingleOwner.h" + +#define ASSERT_SINGLE_OWNER \ + SkDEBUGCODE(GrSingleOwner::AutoEnforce debug_SingleOwner(fSingleOwner);) enum ScratchTextureFlags { kExact_ScratchTextureFlag = 0x1, @@ -17,8 +21,18 @@ enum ScratchTextureFlags { kNoCreate_ScratchTextureFlag = 0x4, }; +GrTextureProvider::GrTextureProvider(GrGpu* gpu, GrResourceCache* cache, GrSingleOwner* singleOwner) + : fCache(cache) + , fGpu(gpu) +#ifdef SK_DEBUG + , fSingleOwner(singleOwner) +#endif + { +} + GrTexture* GrTextureProvider::createTexture(const GrSurfaceDesc& desc, bool budgeted, const void* srcData, size_t rowBytes) { + ASSERT_SINGLE_OWNER if (this->isAbandoned()) { return nullptr; } @@ -44,11 +58,13 @@ GrTexture* GrTextureProvider::createTexture(const GrSurfaceDesc& desc, bool budg } GrTexture* GrTextureProvider::createApproxTexture(const GrSurfaceDesc& desc) { + ASSERT_SINGLE_OWNER return this->internalCreateApproxTexture(desc, 0); } GrTexture* GrTextureProvider::internalCreateApproxTexture(const GrSurfaceDesc& desc, uint32_t scratchFlags) { + ASSERT_SINGLE_OWNER if (this->isAbandoned()) { return nullptr; } @@ -62,6 +78,7 @@ GrTexture* GrTextureProvider::internalCreateApproxTexture(const GrSurfaceDesc& d GrTexture* GrTextureProvider::refScratchTexture(const GrSurfaceDesc& inDesc, uint32_t flags) { + ASSERT_SINGLE_OWNER SkASSERT(!this->isAbandoned()); SkASSERT(!GrPixelConfigIsCompressed(inDesc.fConfig)); @@ -108,6 +125,7 @@ GrTexture* GrTextureProvider::refScratchTexture(const GrSurfaceDesc& inDesc, GrTexture* GrTextureProvider::wrapBackendTexture(const GrBackendTextureDesc& desc, GrWrapOwnership ownership) { + ASSERT_SINGLE_OWNER if (this->isAbandoned()) { return nullptr; } @@ -115,11 +133,13 @@ GrTexture* GrTextureProvider::wrapBackendTexture(const GrBackendTextureDesc& des } GrRenderTarget* GrTextureProvider::wrapBackendRenderTarget(const GrBackendRenderTargetDesc& desc) { + ASSERT_SINGLE_OWNER return this->isAbandoned() ? nullptr : fGpu->wrapBackendRenderTarget(desc, - kBorrow_GrWrapOwnership); + kBorrow_GrWrapOwnership); } void GrTextureProvider::assignUniqueKeyToResource(const GrUniqueKey& key, GrGpuResource* resource) { + ASSERT_SINGLE_OWNER if (this->isAbandoned() || !resource) { return; } @@ -127,9 +147,22 @@ void GrTextureProvider::assignUniqueKeyToResource(const GrUniqueKey& key, GrGpuR } bool GrTextureProvider::existsResourceWithUniqueKey(const GrUniqueKey& key) const { + ASSERT_SINGLE_OWNER return this->isAbandoned() ? false : fCache->hasUniqueKey(key); } GrGpuResource* GrTextureProvider::findAndRefResourceByUniqueKey(const GrUniqueKey& key) { + ASSERT_SINGLE_OWNER return this->isAbandoned() ? nullptr : fCache->findAndRefUniqueResource(key); } + +GrTexture* GrTextureProvider::findAndRefTextureByUniqueKey(const GrUniqueKey& key) { + ASSERT_SINGLE_OWNER + GrGpuResource* resource = this->findAndRefResourceByUniqueKey(key); + if (resource) { + GrTexture* texture = static_cast(resource)->asTexture(); + SkASSERT(texture); + return texture; + } + return NULL; +} diff --git a/gfx/skia/skia/src/gpu/GrTracing.h b/gfx/skia/skia/src/gpu/GrTracing.h index d758c0329d..3dbc0ef17e 100644 --- a/gfx/skia/skia/src/gpu/GrTracing.h +++ b/gfx/skia/skia/src/gpu/GrTracing.h @@ -58,74 +58,64 @@ private: /** * GR_CREATE_TRACE_MARKER will place begin and end trace markers for both * cpu and gpu (if gpu tracing enabled) for the current scope. - * marker is of type const char* and target is of type GrDrawTarget* + * name is of type const char* and target is of type GrDrawTarget* */ -#define GR_CREATE_TRACE_MARKER(name, target) \ - INTERNAL_GR_CREATE_TRACE_MARKER_SCOPED(name, target) +#define GR_CREATE_TRACE_MARKER(name, target) \ + /* Chromium tracing */ \ + static int SK_MACRO_APPEND_LINE(name_counter) = 0; \ + bool SK_MACRO_APPEND_LINE(gpuTracingEnabled); \ + TRACE_EVENT_CATEGORY_GROUP_ENABLED(TRACE_DISABLED_BY_DEFAULT("skia.gpu"), \ + &SK_MACRO_APPEND_LINE(gpuTracingEnabled)); \ + if (SK_MACRO_APPEND_LINE(gpuTracingEnabled)) { \ + INTERNAL_GR_CREATE_TRACE_MARKER_SCOPED(name, SK_MACRO_APPEND_LINE(name_counter), target) \ + } \ + INTERNAL_TRACE_EVENT_ADD_SCOPED(TRACE_DISABLED_BY_DEFAULT("skia.gpu"), name, \ + "id", SK_MACRO_APPEND_LINE(name_counter)); -#define GR_CREATE_TRACE_MARKER1(name, target, arg1_name, arg1_val) \ - INTERNAL_GR_CREATE_TRACE_MARKER_SCOPED(name, target, arg1_name, arg1_val) - -#define INTERNAL_GR_CREATE_TRACE_MARKER_SCOPED(name, target, ...) \ +#define INTERNAL_GR_CREATE_TRACE_MARKER_SCOPED(name, name_counter, target) \ static const char* SK_MACRO_APPEND_LINE(static_name) = name; \ - static int SK_MACRO_APPEND_LINE(name_counter) = 0; \ INTERNAL_GR_CREATE_TRACE_MARKER(SK_MACRO_APPEND_LINE(static_name), \ - SK_MACRO_APPEND_LINE(name_counter), \ - target, ##__VA_ARGS__) \ - sk_atomic_inc(&SK_MACRO_APPEND_LINE(name_counter)); + name_counter, \ + target) \ + sk_atomic_inc(&name_counter); #define INTERNAL_GR_CREATE_TRACE_MARKER(name, name_counter, target, ...) \ GR_CREATE_GPU_TRACE_MARKER(name, name_counter, target) \ - INTERNAL_TRACE_EVENT_ADD_SCOPED(TRACE_DISABLED_BY_DEFAULT("skia.gpu"),name, \ - "id", name_counter, ##__VA_ARGS__); -#if GR_FORCE_GPU_TRACE_DEBUGGING -#define GR_CREATE_GPU_TRACE_MARKER(name, name_counter, target) \ - GrGpuTraceMarkerGenerator SK_MACRO_APPEND_LINE(TMG)(target); \ - SK_MACRO_APPEND_LINE(TMG).initialize(name, &name_counter); -#else -#define GR_CREATE_GPU_TRACE_MARKER(name, name_counter, target) \ - GrGpuTraceMarkerGenerator SK_MACRO_APPEND_LINE(TMG)(target); \ - bool SK_MACRO_APPEND_LINE(gpuTracingEnabled); \ - TRACE_EVENT_CATEGORY_GROUP_ENABLED(TRACE_DISABLED_BY_DEFAULT("skia.gpu"), \ - &SK_MACRO_APPEND_LINE(gpuTracingEnabled)); \ - if (SK_MACRO_APPEND_LINE(gpuTracingEnabled)) { \ - SK_MACRO_APPEND_LINE(TMG).initialize(name, &name_counter); \ - } -#endif +#define GR_CREATE_GPU_TRACE_MARKER(name, name_counter, target) \ + GrGpuTraceMarkerGenerator SK_MACRO_APPEND_LINE(TMG)(target); \ + SK_MACRO_APPEND_LINE(TMG).initialize(name, &name_counter); \ -#define GR_CREATE_TRACE_MARKER_CONTEXT(name, context) \ - INTERNAL_GR_CREATE_TRACE_MARKER_SCOPED_C(name, context) +/** + * Context level GrTracing macros, classname and op are const char*, context is GrContext + * TODO can we just have one set of macros? Probably. + */ +#define GR_CREATE_TRACE_MARKER_CONTEXT(classname, op, context) \ + /* Chromium tracing */ \ + static int SK_MACRO_APPEND_LINE(name_counter) = 0; \ + bool SK_MACRO_APPEND_LINE(gpuTracingEnabled); \ + TRACE_EVENT_CATEGORY_GROUP_ENABLED(TRACE_DISABLED_BY_DEFAULT("skia.gpu"), \ + &SK_MACRO_APPEND_LINE(gpuTracingEnabled)); \ + if (SK_MACRO_APPEND_LINE(gpuTracingEnabled)) { \ + INTERNAL_GR_CREATE_TRACE_MARKER_SCOPED_C(classname "::" op, \ + SK_MACRO_APPEND_LINE(name_counter), context) \ + } \ + GR_AUDIT_TRAIL_AUTO_FRAME(context->getAuditTrail(), classname "::" op); \ + INTERNAL_TRACE_EVENT_ADD_SCOPED(TRACE_DISABLED_BY_DEFAULT("skia.gpu"), classname "::" op, \ + "id", SK_MACRO_APPEND_LINE(name_counter)); -#define GR_CREATE_TRACE_MARKER_CONTEXT1(name, context, arg1_name, arg1_val) \ - INTERNAL_GR_CREATE_TRACE_MARKER_SCOPED_C(name, context, arg1_name, arg1_val) - -#define INTERNAL_GR_CREATE_TRACE_MARKER_SCOPED_C(name, context, ...) \ +#define INTERNAL_GR_CREATE_TRACE_MARKER_SCOPED_C(name, name_counter, context) \ static const char* SK_MACRO_APPEND_LINE(static_name) = name; \ - static int SK_MACRO_APPEND_LINE(name_counter) = 0; \ INTERNAL_GR_CREATE_TRACE_MARKER_C(SK_MACRO_APPEND_LINE(static_name), \ - SK_MACRO_APPEND_LINE(name_counter), \ - context, ##__VA_ARGS__) \ - sk_atomic_inc(&SK_MACRO_APPEND_LINE(name_counter)); + name_counter, \ + context) \ + sk_atomic_inc(&name_counter); #define INTERNAL_GR_CREATE_TRACE_MARKER_C(name, name_counter, context, ...) \ GR_CREATE_GPU_TRACE_MARKER_C(name, name_counter, context) \ - INTERNAL_TRACE_EVENT_ADD_SCOPED(TRACE_DISABLED_BY_DEFAULT("skia.gpu"),name, \ - "id", name_counter, ##__VA_ARGS__); -#if GR_FORCE_GPU_TRACE_DEBUGGING -#define GR_CREATE_GPU_TRACE_MARKER_C(name, name_counter, context) \ - GrGpuTraceMarkerGeneratorContext SK_MACRO_APPEND_LINE(TMG)(context); \ - SK_MACRO_APPEND_LINE(TMG).initialize(name, &name_counter); -#else -#define GR_CREATE_GPU_TRACE_MARKER_C(name, name_counter, context) \ - GrGpuTraceMarkerGeneratorContext SK_MACRO_APPEND_LINE(TMG)(context); \ - bool SK_MACRO_APPEND_LINE(gpuTracingEnabled); \ - TRACE_EVENT_CATEGORY_GROUP_ENABLED(TRACE_DISABLED_BY_DEFAULT("skia.gpu"), \ - &SK_MACRO_APPEND_LINE(gpuTracingEnabled)); \ - if (SK_MACRO_APPEND_LINE(gpuTracingEnabled)) { \ - SK_MACRO_APPEND_LINE(TMG).initialize(name, &name_counter); \ - } -#endif +#define GR_CREATE_GPU_TRACE_MARKER_C(name, name_counter, context) \ + GrGpuTraceMarkerGeneratorContext SK_MACRO_APPEND_LINE(TMG)(context); \ + SK_MACRO_APPEND_LINE(TMG).initialize(name, &name_counter); \ #endif diff --git a/gfx/skia/skia/src/gpu/GrTransferBuffer.h b/gfx/skia/skia/src/gpu/GrTransferBuffer.h new file mode 100644 index 0000000000..bd80666fd6 --- /dev/null +++ b/gfx/skia/skia/src/gpu/GrTransferBuffer.h @@ -0,0 +1,76 @@ + +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + + +#ifndef GrTransferBuffer_DEFINED +#define GrTransferBuffer_DEFINED + +#include "GrGpuResource.h" + +class GrTransferBuffer : public GrGpuResource { +public: + /** + * Maps the buffer to be written by the CPU. + * + * The previous content of the buffer is invalidated. It is an error + * to transfer to or from the buffer while it is mapped. It is an error to + * call map on an already mapped buffer. Must be matched by an unmap() call. + * Currently only one map at a time is supported (no nesting of map/unmap). + * + * Note that buffer mapping does not go through GrContext and therefore is + * not serialized with other operations. + * + * @return a pointer to the data or nullptr if the map fails. + */ + void* map() { return (fMapPtr = this->onMap()); } + + /** + * Unmaps the buffer. + * + * The pointer returned by the previous map call will no longer be valid. + */ + void unmap() { + SkASSERT(fMapPtr); + this->onUnmap(); + fMapPtr = nullptr; + } + + /** + * Returns the same ptr that map() returned at time of map or nullptr if the + * is not mapped. + * + * @return ptr to mapped buffer data or nullptr if buffer is not mapped. + */ + void* mapPtr() const { return fMapPtr; } + + /** + Queries whether the buffer has been mapped. + + @return true if the buffer is mapped, false otherwise. + */ + bool isMapped() const { return SkToBool(fMapPtr); } + +protected: + GrTransferBuffer(GrGpu* gpu, size_t gpuMemorySize) + : INHERITED(gpu, kUncached_LifeCycle) + , fGpuMemorySize(gpuMemorySize) { + } + +private: + virtual size_t onGpuMemorySize() const { return fGpuMemorySize; } + + virtual void* onMap() = 0; + virtual void onUnmap() = 0; + + void* fMapPtr; + size_t fGpuMemorySize; + + typedef GrGpuResource INHERITED; +}; + +#endif diff --git a/gfx/skia/skia/src/gpu/SkGpuDevice.cpp b/gfx/skia/skia/src/gpu/SkGpuDevice.cpp index 17e4468acb..0359b57ca5 100644 --- a/gfx/skia/skia/src/gpu/SkGpuDevice.cpp +++ b/gfx/skia/skia/src/gpu/SkGpuDevice.cpp @@ -10,15 +10,12 @@ #include "GrBlurUtils.h" #include "GrContext.h" #include "SkDraw.h" -#include "GrDrawContext.h" -#include "GrFontScaler.h" #include "GrGpu.h" #include "GrGpuResourcePriv.h" #include "GrImageIDTextureAdjuster.h" #include "GrLayerHoister.h" #include "GrRecordReplaceDraw.h" #include "GrStrokeInfo.h" -#include "GrTextContext.h" #include "GrTracing.h" #include "SkCanvasPriv.h" #include "SkErrorInternals.h" @@ -27,6 +24,7 @@ #include "SkGr.h" #include "SkGrPriv.h" #include "SkImage_Base.h" +#include "SkImageCacherator.h" #include "SkImageFilter.h" #include "SkLayerInfo.h" #include "SkMaskFilter.h" @@ -48,9 +46,13 @@ #include "effects/GrDashingEffect.h" #include "effects/GrSimpleTextureEffect.h" #include "effects/GrTextureDomain.h" +#include "text/GrTextContext.h" #if SK_SUPPORT_GPU +#define ASSERT_SINGLE_OWNER \ + SkDEBUGCODE(GrSingleOwner::AutoEnforce debug_SingleOwner(fContext->debugSingleOwner());) + enum { kDefaultImageFilterCacheSize = 32 * 1024 * 1024 }; #if 0 @@ -171,13 +173,11 @@ SkGpuDevice* SkGpuDevice::Create(GrContext* context, SkSurface::Budgeted budgete SkGpuDevice::SkGpuDevice(GrRenderTarget* rt, int width, int height, const SkSurfaceProps* props, unsigned flags) : INHERITED(SkSurfacePropsCopyOrDefault(props)) -{ - fContext = SkRef(rt->getContext()); + , fContext(SkRef(rt->getContext())) + , fRenderTarget(SkRef(rt)) { fNeedClear = SkToBool(flags & kNeedClear_Flag); fOpaque = SkToBool(flags & kIsOpaque_Flag); - fRenderTarget = SkRef(rt); - SkAlphaType at = fOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType; SkImageInfo info = rt->surfacePriv().info(at).makeWH(width, height); SkPixelRef* pr = new SkGrPixelRef(info, rt); @@ -226,15 +226,11 @@ GrRenderTarget* SkGpuDevice::CreateRenderTarget(GrContext* context, SkSurface::B return texture->asRenderTarget(); } -SkGpuDevice::~SkGpuDevice() { - fRenderTarget->unref(); - fContext->unref(); -} - /////////////////////////////////////////////////////////////////////////////// bool SkGpuDevice::onReadPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRowBytes, int x, int y) { + ASSERT_SINGLE_OWNER DO_DEFERRED_CLEAR(); // TODO: teach fRenderTarget to take ImageInfo directly to specify the src pixels @@ -253,6 +249,7 @@ bool SkGpuDevice::onReadPixels(const SkImageInfo& dstInfo, void* dstPixels, size bool SkGpuDevice::onWritePixels(const SkImageInfo& info, const void* pixels, size_t rowBytes, int x, int y) { + ASSERT_SINGLE_OWNER // TODO: teach fRenderTarget to take ImageInfo directly to specify the src pixels GrPixelConfig config = SkImageInfo2GrPixelConfig(info); if (kUnknown_GrPixelConfig == config) { @@ -271,11 +268,13 @@ bool SkGpuDevice::onWritePixels(const SkImageInfo& info, const void* pixels, siz } const SkBitmap& SkGpuDevice::onAccessBitmap() { + ASSERT_SINGLE_OWNER DO_DEFERRED_CLEAR(); return fLegacyBitmap; } bool SkGpuDevice::onAccessPixels(SkPixmap* pmap) { + ASSERT_SINGLE_OWNER DO_DEFERRED_CLEAR(); // For compatibility with clients the know we're backed w/ a bitmap, and want to inspect its // genID. When we can hide/remove that fact, we can eliminate this call to notify. @@ -285,6 +284,7 @@ bool SkGpuDevice::onAccessPixels(SkPixmap* pmap) { } void SkGpuDevice::onAttachToCanvas(SkCanvas* canvas) { + ASSERT_SINGLE_OWNER INHERITED::onAttachToCanvas(canvas); // Canvas promises that this ptr is valid until onDetachFromCanvas is called @@ -292,6 +292,7 @@ void SkGpuDevice::onAttachToCanvas(SkCanvas* canvas) { } void SkGpuDevice::onDetachFromCanvas() { + ASSERT_SINGLE_OWNER INHERITED::onDetachFromCanvas(); fClip.reset(); fClipStack.reset(nullptr); @@ -300,6 +301,7 @@ void SkGpuDevice::onDetachFromCanvas() { // call this every draw call, to ensure that the context reflects our state, // and not the state from some other canvas/device void SkGpuDevice::prepareDraw(const SkDraw& draw) { + ASSERT_SINGLE_OWNER SkASSERT(fClipStack.get()); SkASSERT(draw.fClipStack && draw.fClipStack == fClipStack); @@ -310,19 +312,22 @@ void SkGpuDevice::prepareDraw(const SkDraw& draw) { } GrRenderTarget* SkGpuDevice::accessRenderTarget() { + ASSERT_SINGLE_OWNER DO_DEFERRED_CLEAR(); return fRenderTarget; } void SkGpuDevice::clearAll() { + ASSERT_SINGLE_OWNER GrColor color = 0; - GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice::clearAll", fContext); + GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "clearAll", fContext); SkIRect rect = SkIRect::MakeWH(this->width(), this->height()); fDrawContext->clear(&rect, color, true); fNeedClear = false; } void SkGpuDevice::replaceRenderTarget(bool shouldRetainContent) { + ASSERT_SINGLE_OWNER // Caller must have accessed the render target, because it knows the rt must be replaced. SkASSERT(!fNeedClear); @@ -346,8 +351,7 @@ void SkGpuDevice::replaceRenderTarget(bool shouldRetainContent) { SkASSERT(fRenderTarget != newRT); - fRenderTarget->unref(); - fRenderTarget = newRT.detach(); + fRenderTarget.reset(newRT.detach()); #ifdef SK_DEBUG SkImageInfo info = fRenderTarget->surfacePriv().info(fOpaque ? kOpaque_SkAlphaType : @@ -363,8 +367,9 @@ void SkGpuDevice::replaceRenderTarget(bool shouldRetainContent) { /////////////////////////////////////////////////////////////////////////////// void SkGpuDevice::drawPaint(const SkDraw& draw, const SkPaint& paint) { + ASSERT_SINGLE_OWNER CHECK_SHOULD_DRAW(draw); - GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice::drawPaint", fContext); + GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "drawPaint", fContext); GrPaint grPaint; if (!SkPaintToGrPaint(this->context(), paint, *draw.fMatrix, &grPaint)) { @@ -405,6 +410,8 @@ static bool needs_antialiasing(SkCanvas::PointMode mode, size_t count, const SkP void SkGpuDevice::drawPoints(const SkDraw& draw, SkCanvas::PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint) { + ASSERT_SINGLE_OWNER + GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "drawPoints", fContext); CHECK_FOR_ANNOTATION(paint); CHECK_SHOULD_DRAW(draw); @@ -455,7 +462,8 @@ void SkGpuDevice::drawPoints(const SkDraw& draw, SkCanvas::PointMode mode, /////////////////////////////////////////////////////////////////////////////// void SkGpuDevice::drawRect(const SkDraw& draw, const SkRect& rect, const SkPaint& paint) { - GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice::drawRect", fContext); + ASSERT_SINGLE_OWNER + GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "drawRect", fContext); CHECK_FOR_ANNOTATION(paint); CHECK_SHOULD_DRAW(draw); @@ -476,10 +484,6 @@ void SkGpuDevice::drawRect(const SkDraw& draw, const SkRect& rect, const SkPaint usePath = true; } - if (!usePath && paint.isAntiAlias() && !draw.fMatrix->rectStaysRect()) { - usePath = true; - } - if (usePath) { SkPath path; path.setIsVolatile(true); @@ -505,7 +509,8 @@ void SkGpuDevice::drawRect(const SkDraw& draw, const SkRect& rect, const SkPaint void SkGpuDevice::drawRRect(const SkDraw& draw, const SkRRect& rect, const SkPaint& paint) { - GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice::drawRRect", fContext); + ASSERT_SINGLE_OWNER + GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "drawRRect", fContext); CHECK_FOR_ANNOTATION(paint); CHECK_SHOULD_DRAW(draw); @@ -568,7 +573,8 @@ void SkGpuDevice::drawRRect(const SkDraw& draw, const SkRRect& rect, void SkGpuDevice::drawDRRect(const SkDraw& draw, const SkRRect& outer, const SkRRect& inner, const SkPaint& paint) { - GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice::drawDRRect", fContext); + ASSERT_SINGLE_OWNER + GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "drawDRRect", fContext); CHECK_FOR_ANNOTATION(paint); CHECK_SHOULD_DRAW(draw); @@ -600,7 +606,8 @@ void SkGpuDevice::drawDRRect(const SkDraw& draw, const SkRRect& outer, ///////////////////////////////////////////////////////////////////////////// void SkGpuDevice::drawOval(const SkDraw& draw, const SkRect& oval, const SkPaint& paint) { - GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice::drawOval", fContext); + ASSERT_SINGLE_OWNER + GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "drawOval", fContext); CHECK_FOR_ANNOTATION(paint); CHECK_SHOULD_DRAW(draw); @@ -634,16 +641,10 @@ void SkGpuDevice::drawOval(const SkDraw& draw, const SkRect& oval, const SkPaint /////////////////////////////////////////////////////////////////////////////// -static SkBitmap wrap_texture(GrTexture* texture, int width, int height) { - SkBitmap result; - result.setInfo(SkImageInfo::MakeN32Premul(width, height)); - result.setPixelRef(new SkGrPixelRef(result.info(), texture))->unref(); - return result; -} - void SkGpuDevice::drawPath(const SkDraw& draw, const SkPath& origSrcPath, const SkPaint& paint, const SkMatrix* prePathMatrix, bool pathIsMutable) { + ASSERT_SINGLE_OWNER if (!origSrcPath.isInverseFillType() && !paint.getPathEffect() && !prePathMatrix) { bool isClosed; SkRect rect; @@ -664,7 +665,7 @@ void SkGpuDevice::drawPath(const SkDraw& draw, const SkPath& origSrcPath, CHECK_FOR_ANNOTATION(paint); CHECK_SHOULD_DRAW(draw); - GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice::drawPath", fContext); + GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "drawPath", fContext); GrBlurUtils::drawPathWithMaskFilter(fContext, fDrawContext, fClip, origSrcPath, paint, @@ -736,6 +737,7 @@ bool SkGpuDevice::shouldTileImageID(uint32_t imageID, const SkIRect& imageRect, int maxTileSize, int* tileSize, SkIRect* clippedSubset) const { + ASSERT_SINGLE_OWNER // if it's larger than the max tile size, then we have no choice but tiling. if (imageRect.width() > maxTileSize || imageRect.height() > maxTileSize) { determine_clipped_src_rect(fRenderTarget, fClip, viewMatrix, imageRect.size(), @@ -782,6 +784,7 @@ bool SkGpuDevice::shouldTileBitmap(const SkBitmap& bitmap, int maxTileSize, int* tileSize, SkIRect* clippedSrcRect) const { + ASSERT_SINGLE_OWNER // if bitmap is explictly texture backed then just use the texture if (bitmap.getTexture()) { return false; @@ -794,6 +797,7 @@ bool SkGpuDevice::shouldTileBitmap(const SkBitmap& bitmap, bool SkGpuDevice::shouldTileImage(const SkImage* image, const SkRect* srcRectPtr, SkCanvas::SrcRectConstraint constraint, SkFilterQuality quality, const SkMatrix& viewMatrix) const { + ASSERT_SINGLE_OWNER // if image is explictly texture backed then just use the texture if (as_IB(image)->peekTexture()) { return false; @@ -828,15 +832,15 @@ void SkGpuDevice::drawBitmap(const SkDraw& origDraw, const SkBitmap& bitmap, const SkMatrix& m, const SkPaint& paint) { + ASSERT_SINGLE_OWNER CHECK_SHOULD_DRAW(origDraw); - bool alphaOnly = kAlpha_8_SkColorType == bitmap.colorType(); SkMatrix viewMatrix; viewMatrix.setConcat(*origDraw.fMatrix, m); if (bitmap.getTexture()) { GrBitmapTextureAdjuster adjuster(&bitmap); // We can use kFast here because we know texture-backed bitmaps don't support extractSubset. - this->drawTextureProducer(&adjuster, alphaOnly, nullptr, nullptr, - SkCanvas::kFast_SrcRectConstraint, viewMatrix, fClip, paint); + this->drawTextureProducer(&adjuster, nullptr, nullptr, SkCanvas::kFast_SrcRectConstraint, + viewMatrix, fClip, paint); return; } int maxTileSize = fContext->caps()->maxTileSize(); @@ -881,8 +885,8 @@ void SkGpuDevice::drawBitmap(const SkDraw& origDraw, } } GrBitmapTextureMaker maker(fContext, bitmap); - this->drawTextureProducer(&maker, alphaOnly, nullptr, nullptr, - SkCanvas::kStrict_SrcRectConstraint, viewMatrix, fClip, paint); + this->drawTextureProducer(&maker, nullptr, nullptr, SkCanvas::kStrict_SrcRectConstraint, + viewMatrix, fClip, paint); } // This method outsets 'iRect' by 'outset' all around and then clamps its extents to @@ -929,6 +933,7 @@ void SkGpuDevice::drawTiledBitmap(const SkBitmap& bitmap, SkCanvas::SrcRectConstraint constraint, int tileSize, bool bicubic) { + ASSERT_SINGLE_OWNER // The following pixel lock is technically redundant, but it is desirable // to lock outside of the tile loop to prevent redecoding the whole image // at each tile in cases where 'bitmap' holds an SkDiscardablePixelRef that @@ -1121,13 +1126,15 @@ bool SkGpuDevice::filterTexture(GrContext* context, GrTexture* texture, const SkImageFilter* filter, const SkImageFilter::Context& ctx, SkBitmap* result, SkIPoint* offset) { + ASSERT_SINGLE_OWNER SkASSERT(filter); SkImageFilter::DeviceProxy proxy(this); if (filter->canFilterImageGPU()) { - return filter->filterImageGPU(&proxy, wrap_texture(texture, width, height), - ctx, result, offset); + SkBitmap bm; + GrWrapTextureInBitmap(texture, width, height, false, &bm); + return filter->filterImageGPU(&proxy, bm, ctx, result, offset); } else { return false; } @@ -1135,6 +1142,7 @@ bool SkGpuDevice::filterTexture(GrContext* context, GrTexture* texture, void SkGpuDevice::drawSprite(const SkDraw& draw, const SkBitmap& bitmap, int left, int top, const SkPaint& paint) { + ASSERT_SINGLE_OWNER // drawSprite is defined to be in device coords. CHECK_SHOULD_DRAW(draw); @@ -1163,11 +1171,15 @@ void SkGpuDevice::drawSprite(const SkDraw& draw, const SkBitmap& bitmap, SkIPoint offset = SkIPoint::Make(0, 0); SkMatrix matrix(*draw.fMatrix); matrix.postTranslate(SkIntToScalar(-left), SkIntToScalar(-top)); +#ifdef SK_SUPPORT_SRC_BOUNDS_BLOAT_FOR_IMAGEFILTERS SkIRect clipBounds = SkIRect::MakeWH(bitmap.width(), bitmap.height()); +#else + SkIRect clipBounds = draw.fClip->getBounds().makeOffset(-left, -top); +#endif SkAutoTUnref cache(getImageFilterCache()); // This cache is transient, and is freed (along with all its contained // textures) when it goes out of scope. - SkImageFilter::Context ctx(matrix, clipBounds, cache, SkImageFilter::kApprox_SizeConstraint); + SkImageFilter::Context ctx(matrix, clipBounds, cache); if (this->filterTexture(fContext, texture, w, h, filter, ctx, &filteredBitmap, &offset)) { texture = (GrTexture*) filteredBitmap.getTexture(); @@ -1210,12 +1222,12 @@ void SkGpuDevice::drawSprite(const SkDraw& draw, const SkBitmap& bitmap, void SkGpuDevice::drawBitmapRect(const SkDraw& draw, const SkBitmap& bitmap, const SkRect* src, const SkRect& origDst, const SkPaint& paint, SkCanvas::SrcRectConstraint constraint) { - bool alphaOnly = kAlpha_8_SkColorType == bitmap.colorType(); + ASSERT_SINGLE_OWNER + CHECK_SHOULD_DRAW(draw); if (bitmap.getTexture()) { - CHECK_SHOULD_DRAW(draw); GrBitmapTextureAdjuster adjuster(&bitmap); - this->drawTextureProducer(&adjuster, alphaOnly, src, &origDst, constraint, *draw.fMatrix, - fClip, paint); + this->drawTextureProducer(&adjuster, src, &origDst, constraint, *draw.fMatrix, fClip, + paint); return; } // The src rect is inferred to be the bmp bounds if not provided. Otherwise, the src rect must @@ -1292,13 +1304,14 @@ void SkGpuDevice::drawBitmapRect(const SkDraw& draw, const SkBitmap& bitmap, } } GrBitmapTextureMaker maker(fContext, bitmap); - this->drawTextureProducer(&maker, alphaOnly, src, dst, constraint, *draw.fMatrix, fClip, paint); + this->drawTextureProducer(&maker, src, dst, constraint, *draw.fMatrix, fClip, paint); } void SkGpuDevice::drawDevice(const SkDraw& draw, SkBaseDevice* device, int x, int y, const SkPaint& paint) { + ASSERT_SINGLE_OWNER // clear of the source device must occur before CHECK_SHOULD_DRAW - GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice::drawDevice", fContext); + GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "drawDevice", fContext); SkGpuDevice* dev = static_cast(device); // TODO: If the source device covers the whole of this device, we could @@ -1326,11 +1339,15 @@ void SkGpuDevice::drawDevice(const SkDraw& draw, SkBaseDevice* device, SkIPoint offset = SkIPoint::Make(0, 0); SkMatrix matrix(*draw.fMatrix); matrix.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y)); +#ifdef SK_SUPPORT_SRC_BOUNDS_BLOAT_FOR_IMAGEFILTERS SkIRect clipBounds = SkIRect::MakeWH(devTex->width(), devTex->height()); +#else + SkIRect clipBounds = draw.fClip->getBounds().makeOffset(-x, -y); +#endif // This cache is transient, and is freed (along with all its contained // textures) when it goes out of scope. SkAutoTUnref cache(getImageFilterCache()); - SkImageFilter::Context ctx(matrix, clipBounds, cache, SkImageFilter::kApprox_SizeConstraint); + SkImageFilter::Context ctx(matrix, clipBounds, cache); if (this->filterTexture(fContext, devTex, device->width(), device->height(), filter, ctx, &filteredBitmap, &offset)) { devTex = filteredBitmap.getTexture(); @@ -1371,12 +1388,14 @@ void SkGpuDevice::drawDevice(const SkDraw& draw, SkBaseDevice* device, } bool SkGpuDevice::canHandleImageFilter(const SkImageFilter* filter) { + ASSERT_SINGLE_OWNER return filter->canFilterImageGPU(); } bool SkGpuDevice::filterImage(const SkImageFilter* filter, const SkBitmap& src, const SkImageFilter::Context& ctx, SkBitmap* result, SkIPoint* offset) { + ASSERT_SINGLE_OWNER // want explicitly our impl, so guard against a subclass of us overriding it if (!this->SkGpuDevice::canHandleImageFilter(filter)) { return false; @@ -1399,86 +1418,69 @@ bool SkGpuDevice::filterImage(const SkImageFilter* filter, const SkBitmap& src, filter, ctx, result, offset); } -static bool wrap_as_bm(GrContext* ctx, const SkImage* image, SkBitmap* bm) { - // TODO: It is wrong to assume these texture params here. - SkAutoTUnref tex(as_IB(image)->asTextureRef(ctx, GrTextureParams::ClampNoFilter())); - if (tex) { - GrWrapTextureInBitmap(tex, image->width(), image->height(), image->isOpaque(), bm); - return true; - } else { - return as_IB(image)->getROPixels(bm); - } -} - void SkGpuDevice::drawImage(const SkDraw& draw, const SkImage* image, SkScalar x, SkScalar y, const SkPaint& paint) { - SkBitmap bm; - if (GrTexture* tex = as_IB(image)->peekTexture()) { + ASSERT_SINGLE_OWNER + SkMatrix viewMatrix = *draw.fMatrix; + viewMatrix.preTranslate(x, y); + if (as_IB(image)->peekTexture()) { CHECK_SHOULD_DRAW(draw); - SkMatrix viewMatrix = *draw.fMatrix; - viewMatrix.preTranslate(x, y); - bool alphaOnly = GrPixelConfigIsAlphaOnly(tex->config()); GrImageTextureAdjuster adjuster(as_IB(image)); - this->drawTextureProducer(&adjuster, alphaOnly, nullptr, nullptr, - SkCanvas::kFast_SrcRectConstraint, viewMatrix, fClip, paint); + this->drawTextureProducer(&adjuster, nullptr, nullptr, SkCanvas::kFast_SrcRectConstraint, + viewMatrix, fClip, paint); return; } else { + SkBitmap bm; if (this->shouldTileImage(image, nullptr, SkCanvas::kFast_SrcRectConstraint, paint.getFilterQuality(), *draw.fMatrix)) { // only support tiling as bitmap at the moment, so force raster-version if (!as_IB(image)->getROPixels(&bm)) { return; } - } else { - if (!wrap_as_bm(this->context(), image, &bm)) { - return; - } + this->drawBitmap(draw, bm, SkMatrix::MakeTrans(x, y), paint); + } else if (SkImageCacherator* cacher = as_IB(image)->peekCacherator()) { + CHECK_SHOULD_DRAW(draw); + GrImageTextureMaker maker(fContext, cacher, image, SkImage::kAllow_CachingHint); + this->drawTextureProducer(&maker, nullptr, nullptr, SkCanvas::kFast_SrcRectConstraint, + viewMatrix, fClip, paint); + } else if (as_IB(image)->getROPixels(&bm)) { + this->drawBitmap(draw, bm, SkMatrix::MakeTrans(x, y), paint); } } - this->drawBitmap(draw, bm, SkMatrix::MakeTrans(x, y), paint); } void SkGpuDevice::drawImageRect(const SkDraw& draw, const SkImage* image, const SkRect* src, const SkRect& dst, const SkPaint& paint, SkCanvas::SrcRectConstraint constraint) { - if (GrTexture* tex = as_IB(image)->peekTexture()) { + ASSERT_SINGLE_OWNER + if (as_IB(image)->peekTexture()) { CHECK_SHOULD_DRAW(draw); GrImageTextureAdjuster adjuster(as_IB(image)); - bool alphaOnly = GrPixelConfigIsAlphaOnly(tex->config()); - this->drawTextureProducer(&adjuster, alphaOnly, src, &dst, constraint, *draw.fMatrix, - fClip, paint); + this->drawTextureProducer(&adjuster, src, &dst, constraint, *draw.fMatrix, fClip, paint); return; } SkBitmap bm; - SkMatrix viewMatrix = *draw.fMatrix; - viewMatrix.preScale(dst.width() / (src ? src->width() : image->width()), - dst.height() / (src ? src->height() : image->height())); - if (this->shouldTileImage(image, src, constraint, paint.getFilterQuality(), viewMatrix)) { + SkMatrix totalMatrix = *draw.fMatrix; + totalMatrix.preScale(dst.width() / (src ? src->width() : image->width()), + dst.height() / (src ? src->height() : image->height())); + if (this->shouldTileImage(image, src, constraint, paint.getFilterQuality(), totalMatrix)) { // only support tiling as bitmap at the moment, so force raster-version if (!as_IB(image)->getROPixels(&bm)) { return; } - } else { - if (!wrap_as_bm(this->context(), image, &bm)) { - return; - } + this->drawBitmapRect(draw, bm, src, dst, paint, constraint); + } else if (SkImageCacherator* cacher = as_IB(image)->peekCacherator()) { + CHECK_SHOULD_DRAW(draw); + GrImageTextureMaker maker(fContext, cacher, image, SkImage::kAllow_CachingHint); + this->drawTextureProducer(&maker, src, &dst, constraint, *draw.fMatrix, fClip, paint); + } else if (as_IB(image)->getROPixels(&bm)) { + this->drawBitmapRect(draw, bm, src, dst, paint, constraint); } - this->drawBitmapRect(draw, bm, src, dst, paint, constraint); } -void SkGpuDevice::drawImageNine(const SkDraw& draw, const SkImage* image, - const SkIRect& center, const SkRect& dst, const SkPaint& paint) { - // TODO write native implementation - SkBitmap bitmap; - if (!wrap_as_bm(this->context(), image, &bitmap)) { - return; - } - return this->drawBitmapNine(draw, bitmap, center, dst, paint); -} - -void SkGpuDevice::drawBitmapNine(const SkDraw& draw, const SkBitmap& bitmap, const SkIRect& center, - const SkRect& dst, const SkPaint& paint) { - GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice::drawBitmapNine", fContext); +void SkGpuDevice::drawProducerNine(const SkDraw& draw, GrTextureProducer* producer, + const SkIRect& center, const SkRect& dst, const SkPaint& paint) { + GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "drawProducerNine", fContext); CHECK_FOR_ANNOTATION(paint); CHECK_SHOULD_DRAW(draw); @@ -1487,42 +1489,62 @@ void SkGpuDevice::drawBitmapNine(const SkDraw& draw, const SkBitmap& bitmap, con fRenderTarget->isUnifiedMultisampled(); bool doBicubic; GrTextureParams::FilterMode textureFilterMode = - GrSkFilterQualityToGrFilterMode(paint.getFilterQuality(), *draw.fMatrix, SkMatrix::I(), - &doBicubic); - - // TODO handle bilerp(vie texture domains), MSAA(via snapping) + GrSkFilterQualityToGrFilterMode(paint.getFilterQuality(), *draw.fMatrix, SkMatrix::I(), + &doBicubic); if (useFallback || doBicubic || GrTextureParams::kNone_FilterMode != textureFilterMode) { - SkNinePatchIter iter(bitmap.width(), bitmap.height(), center, dst); + SkNinePatchIter iter(producer->width(), producer->height(), center, dst); SkRect srcR, dstR; while (iter.next(&srcR, &dstR)) { - this->drawBitmapRect(draw, bitmap, &srcR, dstR, paint, - SkCanvas::kStrict_SrcRectConstraint); + this->drawTextureProducer(producer, &srcR, &dstR, SkCanvas::kStrict_SrcRectConstraint, + *draw.fMatrix, fClip, paint); } return; } - GrTextureParams params = GrTextureParams::ClampNoFilter(); - - SkAutoTUnref texture(GrRefCachedBitmapTexture(this->context(), bitmap, params)); - if (nullptr == texture) { - return; - } - - SkMatrix texMatrix; - texMatrix.setIDiv(texture->width(), texture->height()); - - SkAutoTUnref fp(GrSimpleTextureEffect::Create(texture, texMatrix, - params)); - + static const GrTextureParams::FilterMode kMode = GrTextureParams::kNone_FilterMode; + SkAutoTUnref fp( + producer->createFragmentProcessor(SkMatrix::I(), + SkRect::MakeIWH(producer->width(), producer->height()), + GrTextureProducer::kNo_FilterConstraint, true, + &kMode)); GrPaint grPaint; if (!SkPaintToGrPaintWithTexture(this->context(), paint, *draw.fMatrix, fp, - kAlpha_8_SkColorType == bitmap.colorType(), &grPaint)) { + producer->isAlphaOnly(), &grPaint)) { return; } - fDrawContext->drawImageNine(fClip, grPaint, *draw.fMatrix, bitmap.width(), bitmap.height(), - center, dst); + fDrawContext->drawImageNine(fClip, grPaint, *draw.fMatrix, producer->width(), + producer->height(), center, dst); +} + +void SkGpuDevice::drawImageNine(const SkDraw& draw, const SkImage* image, + const SkIRect& center, const SkRect& dst, const SkPaint& paint) { + ASSERT_SINGLE_OWNER + if (as_IB(image)->peekTexture()) { + GrImageTextureAdjuster adjuster(as_IB(image)); + this->drawProducerNine(draw, &adjuster, center, dst, paint); + } else { + SkBitmap bm; + if (SkImageCacherator* cacher = as_IB(image)->peekCacherator()) { + GrImageTextureMaker maker(fContext, cacher, image, SkImage::kAllow_CachingHint); + this->drawProducerNine(draw, &maker, center, dst, paint); + } else if (as_IB(image)->getROPixels(&bm)) { + this->drawBitmapNine(draw, bm, center, dst, paint); + } + } +} + +void SkGpuDevice::drawBitmapNine(const SkDraw& draw, const SkBitmap& bitmap, const SkIRect& center, + const SkRect& dst, const SkPaint& paint) { + ASSERT_SINGLE_OWNER + if (bitmap.getTexture()) { + GrBitmapTextureAdjuster adjuster(&bitmap); + this->drawProducerNine(draw, &adjuster, center, dst, paint); + } else { + GrBitmapTextureMaker maker(fContext, bitmap); + this->drawProducerNine(draw, &maker, center, dst, paint); + } } /////////////////////////////////////////////////////////////////////////////// @@ -1540,8 +1562,9 @@ void SkGpuDevice::drawVertices(const SkDraw& draw, SkCanvas::VertexMode vmode, SkXfermode* xmode, const uint16_t indices[], int indexCount, const SkPaint& paint) { + ASSERT_SINGLE_OWNER CHECK_SHOULD_DRAW(draw); - GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice::drawVertices", fContext); + GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "drawVertices", fContext); // If both textures and vertex-colors are nullptr, strokes hairlines with the paint's color. if ((nullptr == texs || nullptr == paint.getShader()) && nullptr == colors) { @@ -1667,13 +1690,14 @@ void SkGpuDevice::drawVertices(const SkDraw& draw, SkCanvas::VertexMode vmode, void SkGpuDevice::drawAtlas(const SkDraw& draw, const SkImage* atlas, const SkRSXform xform[], const SkRect texRect[], const SkColor colors[], int count, SkXfermode::Mode mode, const SkPaint& paint) { + ASSERT_SINGLE_OWNER if (paint.isAntiAlias()) { this->INHERITED::drawAtlas(draw, atlas, xform, texRect, colors, count, mode, paint); return; } CHECK_SHOULD_DRAW(draw); - GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice::drawText", fContext); + GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "drawText", fContext); SkPaint p(paint); p.setShader(atlas->newShader(SkShader::kClamp_TileMode, SkShader::kClamp_TileMode))->unref(); @@ -1699,8 +1723,9 @@ void SkGpuDevice::drawAtlas(const SkDraw& draw, const SkImage* atlas, const SkRS void SkGpuDevice::drawText(const SkDraw& draw, const void* text, size_t byteLength, SkScalar x, SkScalar y, const SkPaint& paint) { + ASSERT_SINGLE_OWNER CHECK_SHOULD_DRAW(draw); - GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice::drawText", fContext); + GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "drawText", fContext); GrPaint grPaint; if (!SkPaintToGrPaint(this->context(), paint, *draw.fMatrix, &grPaint)) { @@ -1716,7 +1741,8 @@ void SkGpuDevice::drawText(const SkDraw& draw, const void* text, void SkGpuDevice::drawPosText(const SkDraw& draw, const void* text, size_t byteLength, const SkScalar pos[], int scalarsPerPos, const SkPoint& offset, const SkPaint& paint) { - GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice::drawPosText", fContext); + ASSERT_SINGLE_OWNER + GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "drawPosText", fContext); CHECK_SHOULD_DRAW(draw); GrPaint grPaint; @@ -1733,7 +1759,8 @@ void SkGpuDevice::drawPosText(const SkDraw& draw, const void* text, size_t byteL void SkGpuDevice::drawTextBlob(const SkDraw& draw, const SkTextBlob* blob, SkScalar x, SkScalar y, const SkPaint& paint, SkDrawFilter* drawFilter) { - GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice::drawTextBlob", fContext); + ASSERT_SINGLE_OWNER + GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "drawTextBlob", fContext); CHECK_SHOULD_DRAW(draw); SkDEBUGCODE(this->validate();) @@ -1749,13 +1776,23 @@ bool SkGpuDevice::onShouldDisableLCD(const SkPaint& paint) const { } void SkGpuDevice::flush() { + ASSERT_SINGLE_OWNER DO_DEFERRED_CLEAR(); + + // Clear batch debugging output + // TODO not exactly sure where this should live + if (GR_BATCH_DEBUGGING_OUTPUT) { + SkDebugf("%s\n", fContext->getAuditTrail()->toJson().c_str()); + // TODO This currently crashes because not all ops are accounted for + GR_AUDIT_TRAIL_RESET(fContext->getAuditTrail()); + } fRenderTarget->prepareForExternalIO(); } /////////////////////////////////////////////////////////////////////////////// SkBaseDevice* SkGpuDevice::onCreateDevice(const CreateInfo& cinfo, const SkPaint*) { + ASSERT_SINGLE_OWNER GrSurfaceDesc desc; desc.fConfig = fRenderTarget->config(); desc.fFlags = kRenderTarget_GrSurfaceFlag; @@ -1788,6 +1825,7 @@ SkBaseDevice* SkGpuDevice::onCreateDevice(const CreateInfo& cinfo, const SkPaint } SkSurface* SkGpuDevice::newSurface(const SkImageInfo& info, const SkSurfaceProps& props) { + ASSERT_SINGLE_OWNER // TODO: Change the signature of newSurface to take a budgeted parameter. static const SkSurface::Budgeted kBudgeted = SkSurface::kNo_Budgeted; return SkSurface::NewRenderTarget(fContext, kBudgeted, info, fRenderTarget->desc().fSampleCnt, @@ -1796,6 +1834,7 @@ SkSurface* SkGpuDevice::newSurface(const SkImageInfo& info, const SkSurfaceProps bool SkGpuDevice::EXPERIMENTAL_drawPicture(SkCanvas* mainCanvas, const SkPicture* mainPicture, const SkMatrix* matrix, const SkPaint* paint) { + ASSERT_SINGLE_OWNER #ifndef SK_IGNORE_GPU_LAYER_HOISTING // todo: should handle this natively if (paint) { @@ -1869,6 +1908,7 @@ SkImageFilter::Cache* SkGpuDevice::NewImageFilterCache() { } SkImageFilter::Cache* SkGpuDevice::getImageFilterCache() { + ASSERT_SINGLE_OWNER // We always return a transient cache, so it is freed after each // filter traversal. return SkGpuDevice::NewImageFilterCache(); diff --git a/gfx/skia/skia/src/gpu/SkGpuDevice.h b/gfx/skia/skia/src/gpu/SkGpuDevice.h index 5bf074d120..19bef21038 100644 --- a/gfx/skia/skia/src/gpu/SkGpuDevice.h +++ b/gfx/skia/skia/src/gpu/SkGpuDevice.h @@ -15,6 +15,7 @@ #include "SkPicture.h" #include "SkRegion.h" #include "SkSurface.h" +#include "GrDrawContext.h" #include "GrContext.h" #include "GrSurfacePriv.h" @@ -53,7 +54,7 @@ public: static SkGpuDevice* Create(GrContext*, SkSurface::Budgeted, const SkImageInfo&, int sampleCount, const SkSurfaceProps*, InitContents); - virtual ~SkGpuDevice(); + ~SkGpuDevice() override {} SkGpuDevice* cloneDevice(const SkSurfaceProps& props) { SkBaseDevice* dev = this->onCreateDevice(CreateInfo(this->imageInfo(), kPossible_TileUsage, @@ -140,6 +141,9 @@ public: static SkImageFilter::Cache* NewImageFilterCache(); + // for debugging purposes only + void drawTexture(GrTexture*, const SkRect& dst, const SkPaint&); + protected: bool onReadPixels(const SkImageInfo&, void*, size_t, int, int) override; bool onWritePixels(const SkImageInfo&, const void*, size_t, int, int) override; @@ -150,12 +154,14 @@ protected: const SkMatrix*, const SkPaint*) override; private: - GrContext* fContext; + // We want these unreffed in DrawContext, RenderTarget, GrContext order. + SkAutoTUnref fContext; + SkAutoTUnref fRenderTarget; + SkAutoTUnref fDrawContext; + SkAutoTUnref fClipStack; SkIPoint fClipOrigin; - GrClip fClip; - SkAutoTUnref fDrawContext; - GrRenderTarget* fRenderTarget; + GrClip fClip;; // remove when our clients don't rely on accessBitmap() SkBitmap fLegacyBitmap; bool fNeedClear; @@ -228,7 +234,6 @@ private: bool bicubic); void drawTextureProducer(GrTextureProducer*, - bool alphaOnly, const SkRect* srcRect, const SkRect* dstRect, SkCanvas::SrcRectConstraint, @@ -237,7 +242,6 @@ private: const SkPaint&); void drawTextureProducerImpl(GrTextureProducer*, - bool alphaOnly, const SkRect& clippedSrcRect, const SkRect& clippedDstRect, SkCanvas::SrcRectConstraint, @@ -246,6 +250,9 @@ private: const GrClip&, const SkPaint&); + void drawProducerNine(const SkDraw&, GrTextureProducer*, const SkIRect& center, + const SkRect& dst, const SkPaint&); + bool drawDashLine(const SkPoint pts[2], const SkPaint& paint); static GrRenderTarget* CreateRenderTarget(GrContext*, SkSurface::Budgeted, const SkImageInfo&, diff --git a/gfx/skia/skia/src/gpu/SkGpuDevice_drawTexture.cpp b/gfx/skia/skia/src/gpu/SkGpuDevice_drawTexture.cpp index 0b1d3f1f08..efbc6c98d5 100644 --- a/gfx/skia/skia/src/gpu/SkGpuDevice_drawTexture.cpp +++ b/gfx/skia/skia/src/gpu/SkGpuDevice_drawTexture.cpp @@ -88,7 +88,6 @@ static bool can_ignore_bilerp_constraint(const GrTextureProducer& producer, ////////////////////////////////////////////////////////////////////////////// void SkGpuDevice::drawTextureProducer(GrTextureProducer* producer, - bool alphaOnly, const SkRect* srcRect, const SkRect* dstRect, SkCanvas::SrcRectConstraint constraint, @@ -135,12 +134,11 @@ void SkGpuDevice::drawTextureProducer(GrTextureProducer* producer, } } - this->drawTextureProducerImpl(producer, alphaOnly, clippedSrcRect, clippedDstRect, constraint, - viewMatrix, srcToDstMatrix, clip, paint); + this->drawTextureProducerImpl(producer, clippedSrcRect, clippedDstRect, constraint, viewMatrix, + srcToDstMatrix, clip, paint); } void SkGpuDevice::drawTextureProducerImpl(GrTextureProducer* producer, - bool alphaTexture, const SkRect& clippedSrcRect, const SkRect& clippedDstRect, SkCanvas::SrcRectConstraint constraint, @@ -156,7 +154,7 @@ void SkGpuDevice::drawTextureProducerImpl(GrTextureProducer* producer, // The shader expects proper local coords, so we can't replace local coords with texture coords // if the shader will be used. If we have a mask filter we will change the underlying geometry // that is rendered. - bool canUseTextureCoordsAsLocalCoords = !use_shader(alphaTexture, paint) && !mf; + bool canUseTextureCoordsAsLocalCoords = !use_shader(producer->isAlphaOnly(), paint) && !mf; bool doBicubic; GrTextureParams::FilterMode fm = @@ -204,7 +202,8 @@ void SkGpuDevice::drawTextureProducerImpl(GrTextureProducer* producer, } GrPaint grPaint; - if (!SkPaintToGrPaintWithTexture(fContext, paint, viewMatrix, fp, alphaTexture, &grPaint)) { + if (!SkPaintToGrPaintWithTexture(fContext, paint, viewMatrix, fp, producer->isAlphaOnly(), + &grPaint)) { return; } diff --git a/gfx/skia/skia/src/gpu/SkGrPixelRef.cpp b/gfx/skia/skia/src/gpu/SkGrPixelRef.cpp index 6e014feded..de585b87e4 100644 --- a/gfx/skia/skia/src/gpu/SkGrPixelRef.cpp +++ b/gfx/skia/skia/src/gpu/SkGrPixelRef.cpp @@ -25,7 +25,7 @@ SkROLockPixelsPixelRef::~SkROLockPixelsPixelRef() {} bool SkROLockPixelsPixelRef::onNewLockPixels(LockRec* rec) { fBitmap.reset(); // SkDebugf("---------- calling readpixels in support of lockpixels\n"); - if (!this->onReadPixels(&fBitmap, nullptr)) { + if (!this->onReadPixels(&fBitmap, this->info().colorType(), nullptr)) { SkDebugf("SkROLockPixelsPixelRef::onLockPixels failed!\n"); return false; } @@ -51,7 +51,8 @@ bool SkROLockPixelsPixelRef::onLockPixelsAreWritable() const { /////////////////////////////////////////////////////////////////////////////// static SkGrPixelRef* copy_to_new_texture_pixelref(GrTexture* texture, SkColorType dstCT, - SkColorProfileType dstPT, const SkIRect* subset) { + SkAlphaType dstAT, SkColorProfileType dstPT, + const SkIRect* subset) { if (nullptr == texture || kUnknown_SkColorType == dstCT) { return nullptr; } @@ -75,7 +76,7 @@ static SkGrPixelRef* copy_to_new_texture_pixelref(GrTexture* texture, SkColorTyp srcRect = *subset; } desc.fFlags = kRenderTarget_GrSurfaceFlag; - desc.fConfig = SkImageInfo2GrPixelConfig(dstCT, kPremul_SkAlphaType, dstPT); + desc.fConfig = SkImageInfo2GrPixelConfig(dstCT, dstAT, dstPT); GrTexture* dst = context->textureProvider()->createTexture(desc, false, nullptr, 0); if (nullptr == dst) { @@ -88,8 +89,7 @@ static SkGrPixelRef* copy_to_new_texture_pixelref(GrTexture* texture, SkColorTyp context->copySurface(dst->asRenderTarget(), texture, srcRect, SkIPoint::Make(0,0), GrContext::kFlushWrites_PixelOp); - SkImageInfo info = SkImageInfo::Make(desc.fWidth, desc.fHeight, dstCT, kPremul_SkAlphaType, - dstPT); + SkImageInfo info = SkImageInfo::Make(desc.fWidth, desc.fHeight, dstCT, dstAT, dstPT); SkGrPixelRef* pixelRef = new SkGrPixelRef(info, dst); SkSafeUnref(dst); return pixelRef; @@ -142,7 +142,8 @@ SkPixelRef* SkGrPixelRef::deepCopy(SkColorType dstCT, SkColorProfileType dstPT, // a GrTexture owned elsewhere (e.g., SkGpuDevice), and cannot live // independently of that texture. Texture-backed pixel refs, on the other // hand, own their GrTextures, and are thus self-contained. - return copy_to_new_texture_pixelref(fSurface->asTexture(), dstCT, dstPT, subset); + return copy_to_new_texture_pixelref(fSurface->asTexture(), dstCT, this->info().alphaType(), + dstPT, subset); } static bool tryAllocBitmapPixels(SkBitmap* bitmap) { @@ -155,11 +156,20 @@ static bool tryAllocBitmapPixels(SkBitmap* bitmap) { } } -bool SkGrPixelRef::onReadPixels(SkBitmap* dst, const SkIRect* subset) { +bool SkGrPixelRef::onReadPixels(SkBitmap* dst, SkColorType colorType, const SkIRect* subset) { if (nullptr == fSurface || fSurface->wasDestroyed()) { return false; } + GrPixelConfig config; + if (kRGBA_8888_SkColorType == colorType) { + config = kRGBA_8888_GrPixelConfig; + } else if (kBGRA_8888_SkColorType == colorType) { + config = kBGRA_8888_GrPixelConfig; + } else { + return false; + } + SkIRect bounds; if (subset) { bounds = *subset; @@ -172,7 +182,9 @@ bool SkGrPixelRef::onReadPixels(SkBitmap* dst, const SkIRect* subset) { //Cache miss SkBitmap cachedBitmap; - cachedBitmap.setInfo(this->info().makeWH(bounds.width(), bounds.height())); + cachedBitmap.setInfo(SkImageInfo::Make(bounds.width(), bounds.height(), colorType, + this->info().alphaType(), + this->info().profileType())); // If we can't alloc the pixels, then fail if (!tryAllocBitmapPixels(&cachedBitmap)) { @@ -183,8 +195,7 @@ bool SkGrPixelRef::onReadPixels(SkBitmap* dst, const SkIRect* subset) { void* buffer = cachedBitmap.getPixels(); bool readPixelsOk = fSurface->readPixels(bounds.fLeft, bounds.fTop, bounds.width(), bounds.height(), - kSkia8888_GrPixelConfig, - buffer, cachedBitmap.rowBytes()); + config, buffer, cachedBitmap.rowBytes()); if (!readPixelsOk) { return false; diff --git a/gfx/skia/skia/src/gpu/batches/GrAAConvexPathRenderer.cpp b/gfx/skia/skia/src/gpu/batches/GrAAConvexPathRenderer.cpp index c5b7c579b7..9fde34c186 100644 --- a/gfx/skia/skia/src/gpu/batches/GrAAConvexPathRenderer.cpp +++ b/gfx/skia/skia/src/gpu/batches/GrAAConvexPathRenderer.cpp @@ -25,10 +25,12 @@ #include "SkString.h" #include "SkTraceEvent.h" #include "batches/GrVertexBatch.h" +#include "glsl/GrGLSLFragmentShaderBuilder.h" #include "glsl/GrGLSLGeometryProcessor.h" -#include "glsl/GrGLSLProgramBuilder.h" #include "glsl/GrGLSLProgramDataManager.h" +#include "glsl/GrGLSLUniformHandler.h" #include "glsl/GrGLSLVarying.h" +#include "glsl/GrGLSLVertexShaderBuilder.h" GrAAConvexPathRenderer::GrAAConvexPathRenderer() { } @@ -549,9 +551,9 @@ public: void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override { const QuadEdgeEffect& qe = args.fGP.cast(); - GrGLSLGPBuilder* pb = args.fPB; GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder; GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler; + GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; // emit attributes varyingHandler->emitAttributes(qe); @@ -563,16 +565,17 @@ public: GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder; // Setup pass through color if (!qe.colorIgnored()) { - this->setupUniformColor(pb, fragBuilder, args.fOutputColor, &fColorUniform); + this->setupUniformColor(fragBuilder, uniformHandler, args.fOutputColor, + &fColorUniform); } // Setup position - this->setupPosition(pb, vertBuilder, gpArgs, qe.inPosition()->fName); + this->setupPosition(vertBuilder, gpArgs, qe.inPosition()->fName); // emit transforms - this->emitTransforms(args.fPB, - vertBuilder, + this->emitTransforms(vertBuilder, varyingHandler, + uniformHandler, gpArgs->fPositionVar, qe.inPosition()->fName, qe.localMatrix(), @@ -999,6 +1002,7 @@ private: }; bool GrAAConvexPathRenderer::onDrawPath(const DrawPathArgs& args) { + GR_AUDIT_TRAIL_AUTO_FRAME(args.fTarget->getAuditTrail(), "GrAAConvexPathRenderer::onDrawPath"); if (args.fPath->isEmpty()) { return true; } diff --git a/gfx/skia/skia/src/gpu/batches/GrAAConvexTessellator.cpp b/gfx/skia/skia/src/gpu/batches/GrAAConvexTessellator.cpp index 5ef9aefddd..c3d25948de 100644 --- a/gfx/skia/skia/src/gpu/batches/GrAAConvexTessellator.cpp +++ b/gfx/skia/skia/src/gpu/batches/GrAAConvexTessellator.cpp @@ -835,6 +835,13 @@ void GrAAConvexTessellator::lineTo(SkPoint p, bool isCurve) { this->popLastPt(); fNorms.pop(); fIsCurve.pop(); + // double-check that the new last point is not a duplicate of the new point. In an ideal + // world this wouldn't be necessary (since it's only possible for non-convex paths), but + // floating point precision issues mean it can actually happen on paths that were determined + // to be convex. + if (duplicate_pt(p, this->lastPoint())) { + return; + } } SkScalar initialRingCoverage = fStrokeWidth < 0.0f ? 0.5f : 1.0f; this->addPt(p, 0.0f, initialRingCoverage, false, isCurve); diff --git a/gfx/skia/skia/src/gpu/batches/GrAADistanceFieldPathRenderer.cpp b/gfx/skia/skia/src/gpu/batches/GrAADistanceFieldPathRenderer.cpp index 0b14e4df4e..e94cd591f3 100644 --- a/gfx/skia/skia/src/gpu/batches/GrAADistanceFieldPathRenderer.cpp +++ b/gfx/skia/skia/src/gpu/batches/GrAADistanceFieldPathRenderer.cpp @@ -142,13 +142,13 @@ public: // in fPath since that path may have resulted from a SkStrokeRec::applyToPath call. uint32_t fGenID; SkStrokeRec fStroke; + GrColor fColor; bool fAntiAlias; }; - static GrDrawBatch* Create(const Geometry& geometry, GrColor color, const SkMatrix& viewMatrix, + static GrDrawBatch* Create(const Geometry& geometry, const SkMatrix& viewMatrix, GrBatchAtlas* atlas, PathCache* pathCache, PathDataList* pathList) { - return new AADistanceFieldPathBatch(geometry, color, viewMatrix, atlas, pathCache, - pathList); + return new AADistanceFieldPathBatch(geometry, viewMatrix, atlas, pathCache, pathList); } const char* name() const override { return "AADistanceFieldPathBatch"; } @@ -156,7 +156,7 @@ public: void computePipelineOptimizations(GrInitInvariantOutput* color, GrInitInvariantOutput* coverage, GrBatchToXPOverrides* overrides) const override { - color->setKnownFourComponents(fBatch.fColor); + color->setKnownFourComponents(fGeoData[0].fColor); coverage->setUnknownSingleComponent(); overrides->fUsePLSDstRead = false; } @@ -165,9 +165,9 @@ private: void initBatchTracker(const GrXPOverridesForBatch& overrides) override { // Handle any color overrides if (!overrides.readsColor()) { - fBatch.fColor = GrColor_ILLEGAL; + fGeoData[0].fColor = GrColor_ILLEGAL; } - overrides.getOverrideColorIfSet(&fBatch.fColor); + overrides.getOverrideColorIfSet(&fGeoData[0].fColor); // setup batch properties fBatch.fColorIgnored = !overrides.readsColor(); @@ -212,7 +212,7 @@ private: // allocate vertices size_t vertexStride = dfProcessor->getVertexStride(); - SkASSERT(vertexStride == 2 * sizeof(SkPoint)); + SkASSERT(vertexStride == 2 * sizeof(SkPoint) + sizeof(GrColor)); const GrVertexBuffer* vertexBuffer; void* vertices = target->makeVertexSpace(vertexStride, @@ -278,12 +278,12 @@ private: // Now set vertices intptr_t offset = reinterpret_cast(vertices); offset += i * kVerticesPerQuad * vertexStride; - SkPoint* positions = reinterpret_cast(offset); this->writePathVertices(target, atlas, this->pipeline(), dfProcessor, - positions, + offset, + args.fColor, vertexStride, this->viewMatrix(), args.fPath, @@ -296,11 +296,11 @@ private: SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; } - AADistanceFieldPathBatch(const Geometry& geometry, GrColor color, const SkMatrix& viewMatrix, + AADistanceFieldPathBatch(const Geometry& geometry, + const SkMatrix& viewMatrix, GrBatchAtlas* atlas, PathCache* pathCache, PathDataList* pathList) : INHERITED(ClassID()) { - fBatch.fColor = color; fBatch.fViewMatrix = viewMatrix; fGeoData.push_back(geometry); @@ -342,9 +342,15 @@ private: SkIRect devPathBounds; scaledBounds.roundOut(&devPathBounds); // pad to allow room for antialiasing - devPathBounds.outset(SkScalarCeilToInt(kAntiAliasPad), SkScalarCeilToInt(kAntiAliasPad)); - // move origin to upper left corner - devPathBounds.offsetTo(0,0); + const int intPad = SkScalarCeilToInt(kAntiAliasPad); + // pre-move origin (after outset, will be 0,0) + int width = devPathBounds.width(); + int height = devPathBounds.height(); + devPathBounds.fLeft = intPad; + devPathBounds.fTop = intPad; + devPathBounds.fRight = intPad + width; + devPathBounds.fBottom = intPad + height; + devPathBounds.outset(intPad, intPad); // draw path to bitmap SkMatrix drawMatrix; @@ -381,8 +387,8 @@ private: // generate signed distance field devPathBounds.outset(SK_DistanceFieldPad, SK_DistanceFieldPad); - int width = devPathBounds.width(); - int height = devPathBounds.height(); + width = devPathBounds.width(); + height = devPathBounds.height(); // TODO We should really generate this directly into the plot somehow SkAutoSMalloc<1024> dfStorage(width * height * sizeof(unsigned char)); @@ -437,7 +443,8 @@ private: GrBatchAtlas* atlas, const GrPipeline* pipeline, const GrGeometryProcessor* gp, - SkPoint* positions, + intptr_t offset, + GrColor color, size_t vertexStride, const SkMatrix& viewMatrix, const SkPath& path, @@ -460,13 +467,21 @@ private: SkFixed tw = SkScalarToFixed(pathData->fBounds.width()); SkFixed th = SkScalarToFixed(pathData->fBounds.height()); + SkPoint* positions = reinterpret_cast(offset); + // vertex positions // TODO make the vertex attributes a struct SkRect r = SkRect::MakeXYWH(dx, dy, width, height); positions->setRectFan(r.left(), r.top(), r.right(), r.bottom(), vertexStride); + // colors + for (int i = 0; i < kVerticesPerQuad; i++) { + GrColor* colorPtr = (GrColor*)(offset + sizeof(SkPoint) + i * vertexStride); + *colorPtr = color; + } + // vertex texture coords - SkPoint* textureCoords = positions + 1; + SkPoint* textureCoords = (SkPoint*)(offset + sizeof(SkPoint) + sizeof(GrColor)); textureCoords->setRectFan(SkFixedToFloat(texture->texturePriv().normalizeFixedX(tx)), SkFixedToFloat(texture->texturePriv().normalizeFixedY(ty)), SkFixedToFloat(texture->texturePriv().normalizeFixedX(tx + tw)), @@ -485,7 +500,7 @@ private: flushInfo->fInstancesToFlush = 0; } - GrColor color() const { return fBatch.fColor; } + GrColor color() const { return fGeoData[0].fColor; } const SkMatrix& viewMatrix() const { return fBatch.fViewMatrix; } bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; } @@ -496,12 +511,7 @@ private: return false; } - // TODO we could actually probably do a bunch of this work on the CPU, ie map viewMatrix, - // maybe upload color via attribute - if (this->color() != that->color()) { - return false; - } - + // TODO We can position on the cpu if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) { return false; } @@ -512,7 +522,6 @@ private: } struct BatchTracker { - GrColor fColor; SkMatrix fViewMatrix; bool fUsesLocalCoords; bool fColorIgnored; @@ -529,6 +538,8 @@ private: }; bool GrAADistanceFieldPathRenderer::onDrawPath(const DrawPathArgs& args) { + GR_AUDIT_TRAIL_AUTO_FRAME(args.fTarget->getAuditTrail(), + "GrAADistanceFieldPathRenderer::onDrawPath"); // we've already bailed on inverse filled paths, so this is safe if (args.fPath->isEmpty()) { return true; @@ -551,13 +562,14 @@ bool GrAADistanceFieldPathRenderer::onDrawPath(const DrawPathArgs& args) { } else { args.fStroke->applyToPath(&geometry.fPath, *args.fPath); } + geometry.fColor = args.fColor; geometry.fAntiAlias = args.fAntiAlias; // Note: this is the generation ID of the _original_ path. When a new path is // generated due to stroking it is important that the original path's id is used // for caching. geometry.fGenID = args.fPath->getGenerationID(); - SkAutoTUnref batch(AADistanceFieldPathBatch::Create(geometry, args.fColor, + SkAutoTUnref batch(AADistanceFieldPathBatch::Create(geometry, *args.fViewMatrix, fAtlas, &fPathCache, &fPathList)); args.fTarget->drawBatch(*args.fPipelineBuilder, batch); @@ -629,11 +641,12 @@ DRAW_BATCH_TEST_DEFINE(AADistanceFieldPathBatch) { GrColor color = GrRandomColor(random); AADistanceFieldPathBatch::Geometry geometry(GrTest::TestStrokeRec(random)); + geometry.fColor = color; geometry.fPath = GrTest::TestPath(random); geometry.fAntiAlias = random->nextBool(); geometry.fGenID = random->nextU(); - return AADistanceFieldPathBatch::Create(geometry, color, viewMatrix, + return AADistanceFieldPathBatch::Create(geometry, viewMatrix, gTestStruct.fAtlas, &gTestStruct.fPathCache, &gTestStruct.fPathList); diff --git a/gfx/skia/skia/src/gpu/batches/GrAAHairLinePathRenderer.cpp b/gfx/skia/skia/src/gpu/batches/GrAAHairLinePathRenderer.cpp index d6ad9d32c9..6b7b308900 100644 --- a/gfx/skia/skia/src/gpu/batches/GrAAHairLinePathRenderer.cpp +++ b/gfx/skia/skia/src/gpu/batches/GrAAHairLinePathRenderer.cpp @@ -974,6 +974,7 @@ static GrDrawBatch* create_hairline_batch(GrColor color, } bool GrAAHairLinePathRenderer::onDrawPath(const DrawPathArgs& args) { + GR_AUDIT_TRAIL_AUTO_FRAME(args.fTarget->getAuditTrail(),"GrAAHairlinePathRenderer::onDrawPath"); SkIRect devClipBounds; GrRenderTarget* rt = args.fPipelineBuilder->getRenderTarget(); args.fPipelineBuilder->clip().getConservativeBounds(rt->width(), rt->height(), &devClipBounds); diff --git a/gfx/skia/skia/src/gpu/batches/GrAALinearizingConvexPathRenderer.cpp b/gfx/skia/skia/src/gpu/batches/GrAALinearizingConvexPathRenderer.cpp index 4b03c5a3f6..c830509a2a 100644 --- a/gfx/skia/skia/src/gpu/batches/GrAALinearizingConvexPathRenderer.cpp +++ b/gfx/skia/skia/src/gpu/batches/GrAALinearizingConvexPathRenderer.cpp @@ -25,7 +25,6 @@ #include "SkPathPriv.h" #include "batches/GrVertexBatch.h" #include "glsl/GrGLSLGeometryProcessor.h" -#include "glsl/GrGLSLProgramBuilder.h" static const int DEFAULT_BUFFER_SIZE = 100; @@ -54,7 +53,7 @@ bool GrAALinearizingConvexPathRenderer::onCanDrawPath(const CanDrawPathArgs& arg } SkScalar strokeWidth = args.fViewMatrix->getMaxScale() * args.fStroke->getWidth(); return strokeWidth >= 1.0f && strokeWidth <= kMaxStrokeWidth && !args.fStroke->isDashed() && - SkPathPriv::LastVerbIsClose(*args.fPath) && + SkPathPriv::IsClosedSingleContour(*args.fPath) && args.fStroke->getJoin() != SkPaint::Join::kRound_Join; } return args.fStroke->getStyle() == SkStrokeRec::kFill_Style; @@ -323,6 +322,8 @@ private: }; bool GrAALinearizingConvexPathRenderer::onDrawPath(const DrawPathArgs& args) { + GR_AUDIT_TRAIL_AUTO_FRAME(args.fTarget->getAuditTrail(), + "GrAALinearizingConvexPathRenderer::onDrawPath"); if (args.fPath->isEmpty()) { return true; } diff --git a/gfx/skia/skia/src/gpu/batches/GrAtlasTextBatch.cpp b/gfx/skia/skia/src/gpu/batches/GrAtlasTextBatch.cpp index fa34036af6..11b35c196b 100644 --- a/gfx/skia/skia/src/gpu/batches/GrAtlasTextBatch.cpp +++ b/gfx/skia/skia/src/gpu/batches/GrAtlasTextBatch.cpp @@ -7,7 +7,6 @@ #include "GrAtlasTextBatch.h" -#include "GrBatchFontCache.h" #include "GrBatchFlushState.h" #include "GrBatchTest.h" #include "GrResourceProvider.h" @@ -17,6 +16,7 @@ #include "effects/GrBitmapTextGeoProc.h" #include "effects/GrDistanceFieldGeoProc.h" +#include "text/GrBatchFontCache.h" //////////////////////////////////////////////////////////////////////////////////////////////////// // A large template to handle regenerating the vertices of a textblob with as few branches as @@ -135,7 +135,7 @@ inline void GrAtlasTextBatch::regenBlob(Target* target, FlushInfo* flushInfo, Bl static_assert(!regenGlyphs || regenTexCoords, "must regenTexCoords along regenGlyphs"); GrBatchTextStrike* strike = nullptr; if (regenTexCoords) { - info->fBulkUseToken.reset(); + info->resetBulkUseToken(); // We can reuse if we have a valid strike and our descriptors / typeface are the // same. The override descriptor is only for the non distance field text within @@ -151,14 +151,14 @@ inline void GrAtlasTextBatch::regenBlob(Target* target, FlushInfo* flushInfo, Bl *desc = newDesc; *cache = SkGlyphCache::DetachCache(run->fTypeface, *desc); *scaler = GrTextContext::GetGrFontScaler(*cache); - strike = info->fStrike; + strike = info->strike(); *typeface = run->fTypeface; } if (regenGlyphs) { strike = fFontCache->getStrike(*scaler); } else { - strike = info->fStrike; + strike = info->strike(); } } @@ -166,7 +166,7 @@ inline void GrAtlasTextBatch::regenBlob(Target* target, FlushInfo* flushInfo, Bl for (int glyphIdx = 0; glyphIdx < glyphCount; glyphIdx++) { GrGlyph* glyph = nullptr; if (regenTexCoords) { - size_t glyphOffset = glyphIdx + info->fGlyphStartIndex; + size_t glyphOffset = glyphIdx + info->glyphStartIndex(); if (regenGlyphs) { // Get the id from the old glyph, and use the new strike to lookup @@ -190,12 +190,12 @@ inline void GrAtlasTextBatch::regenBlob(Target* target, FlushInfo* flushInfo, Bl this->maskFormat()); SkASSERT(success); } - fFontCache->addGlyphToBulkAndSetUseToken(&info->fBulkUseToken, glyph, + fFontCache->addGlyphToBulkAndSetUseToken(info->bulkUseToken(), glyph, target->currentToken()); } intptr_t vertex = reinterpret_cast(blob->fVertices); - vertex += info->fVertexStartIndex; + vertex += info->vertexStartIndex(); vertex += vertexStride * glyphIdx * GrAtlasTextBatch::kVerticesPerGlyph; regen_vertices(vertex, glyph, vertexStride, this->usesDistanceFields(), transX, @@ -203,14 +203,14 @@ inline void GrAtlasTextBatch::regenBlob(Target* target, FlushInfo* flushInfo, Bl flushInfo->fGlyphsToFlush++; } - // We my have changed the color so update it here - run->fColor = color; + // We may have changed the color so update it here + info->setColor(color); if (regenTexCoords) { if (regenGlyphs) { - info->fStrike.reset(SkRef(strike)); + info->setStrike(strike); } - info->fAtlasGeneration = brokenRun ? GrBatchAtlas::kInvalidAtlasGeneration : - fFontCache->atlasGeneration(this->maskFormat()); + info->setAtlasGeneration(brokenRun ? GrBatchAtlas::kInvalidAtlasGeneration : + fFontCache->atlasGeneration(this->maskFormat())); } } /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -312,12 +312,10 @@ void GrAtlasTextBatch::onPrepareDraws(Target* target) const { return; } - bool usesDistanceFields = this->usesDistanceFields(); GrMaskFormat maskFormat = this->maskFormat(); - bool isLCD = this->isLCD(); SkAutoTUnref gp; - if (usesDistanceFields) { + if (this->usesDistanceFields()) { gp.reset(this->setupDfProcessor(this->viewMatrix(), fFilteredColor, this->color(), texture)); } else { @@ -333,9 +331,7 @@ void GrAtlasTextBatch::onPrepareDraws(Target* target) const { FlushInfo flushInfo; flushInfo.fGlyphsToFlush = 0; size_t vertexStride = gp->getVertexStride(); - SkASSERT(vertexStride == (usesDistanceFields ? - GetVertexStrideDf(maskFormat, isLCD) : - GetVertexStride(maskFormat))); + SkASSERT(vertexStride == GrAtlasTextBlob::GetVertexStride(maskFormat)); target->initDraw(gp, this->pipeline()); @@ -378,17 +374,13 @@ void GrAtlasTextBatch::onPrepareDraws(Target* target) const { // new strike, we instead keep our ref to the old strike and use the packed ids from // it. These ids will still be valid as long as we hold the ref. When we are done // updating our cache of the GrGlyph*s, we drop our ref on the old strike - bool regenerateGlyphs = info.fStrike->isAbandoned(); - bool regenerateTextureCoords = info.fAtlasGeneration != currentAtlasGen || + bool regenerateGlyphs = info.strike()->isAbandoned(); + bool regenerateTextureCoords = info.atlasGeneration() != currentAtlasGen || regenerateGlyphs; - bool regenerateColors; - if (usesDistanceFields) { - regenerateColors = !isLCD && run.fColor != args.fColor; - } else { - regenerateColors = kA8_GrMaskFormat == maskFormat && run.fColor != args.fColor; - } + bool regenerateColors = kARGB_GrMaskFormat != maskFormat && + info.color() != args.fColor; bool regeneratePositions = args.fTransX != 0.f || args.fTransY != 0.f; - int glyphCount = info.fGlyphEndIndex - info.fGlyphStartIndex; + int glyphCount = info.glyphCount(); uint32_t regenMaskBits = kNoRegen; regenMaskBits |= regeneratePositions ? kRegenPos : 0; @@ -416,13 +408,14 @@ void GrAtlasTextBatch::onPrepareDraws(Target* target) const { // set use tokens for all of the glyphs in our subrun. This is only valid if we // have a valid atlas generation - fFontCache->setUseTokenBulk(info.fBulkUseToken, target->currentToken(), maskFormat); + fFontCache->setUseTokenBulk(*info.bulkUseToken(), target->currentToken(), + maskFormat); break; } // now copy all vertices - size_t byteCount = info.fVertexEndIndex - info.fVertexStartIndex; - memcpy(currVertex, blob->fVertices + info.fVertexStartIndex, byteCount); + size_t byteCount = info.byteCount(); + memcpy(currVertex, blob->fVertices + info.vertexStartIndex(), byteCount); currVertex += byteCount; } @@ -458,9 +451,7 @@ bool GrAtlasTextBatch::onCombineIfPossible(GrBatch* t, const GrCaps& caps) { } if (!this->usesDistanceFields()) { - // TODO we can often batch across LCD text if we have dual source blending and don't - // have to use the blend constant - if (kGrayscaleCoverageMask_MaskType != fMaskType && this->color() != that->color()) { + if (kColorBitmapMask_MaskType == fMaskType && this->color() != that->color()) { return false; } if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) { @@ -478,11 +469,6 @@ bool GrAtlasTextBatch::onCombineIfPossible(GrBatch* t, const GrCaps& caps) { if (fUseBGR != that->fUseBGR) { return false; } - - // TODO see note above - if (kLCDDistanceField_MaskType == fMaskType && this->color() != that->color()) { - return false; - } } fBatch.fNumGlyphs += that->numGlyphs(); @@ -551,7 +537,6 @@ GrGeometryProcessor* GrAtlasTextBatch::setupDfProcessor(const SkMatrix& viewMatr flags, this->usesLocalCoords()); } else { - flags |= kColorAttr_DistanceFieldEffectFlag; #ifdef SK_GAMMA_APPLY_TO_A8 U8CPU lum = SkColorSpaceLuminance::computeLuminance(SK_GAMMA_EXPONENT, filteredColor); float correction = (*fDistanceAdjustTable)[lum >> kDistanceAdjustLumShift]; diff --git a/gfx/skia/skia/src/gpu/batches/GrAtlasTextBatch.h b/gfx/skia/skia/src/gpu/batches/GrAtlasTextBatch.h index 65fd07d7bb..8f20313378 100644 --- a/gfx/skia/skia/src/gpu/batches/GrAtlasTextBatch.h +++ b/gfx/skia/skia/src/gpu/batches/GrAtlasTextBatch.h @@ -10,20 +10,16 @@ #include "batches/GrVertexBatch.h" -#include "GrAtlasTextContext.h" +#include "text/GrAtlasTextContext.h" +#include "text/GrDistanceFieldAdjustTable.h" class GrAtlasTextBatch : public GrVertexBatch { public: DEFINE_BATCH_CLASS_ID - static const size_t kLCDTextVASize = sizeof(SkPoint) + sizeof(SkIPoint16); - // position + local coord - static const size_t kColorTextVASize = sizeof(SkPoint) + sizeof(SkIPoint16); - static const size_t kGrayTextVASize = sizeof(SkPoint) + sizeof(GrColor) + sizeof(SkIPoint16); - static const int kVerticesPerGlyph = 4; + static const int kVerticesPerGlyph = GrAtlasTextBlob::kVerticesPerGlyph; static const int kIndicesPerGlyph = 6; - typedef GrAtlasTextContext::DistanceAdjustTable DistanceAdjustTable; typedef GrAtlasTextBlob Blob; typedef Blob::Run Run; typedef Run::SubRunInfo TextInfo; @@ -60,10 +56,11 @@ public: return batch; } - static GrAtlasTextBatch* CreateDistanceField(int glyphCount, GrBatchFontCache* fontCache, - const DistanceAdjustTable* distanceAdjustTable, - SkColor filteredColor, bool isLCD, - bool useBGR) { + static GrAtlasTextBatch* CreateDistanceField( + int glyphCount, GrBatchFontCache* fontCache, + const GrDistanceFieldAdjustTable* distanceAdjustTable, + SkColor filteredColor, bool isLCD, + bool useBGR) { GrAtlasTextBatch* batch = new GrAtlasTextBatch; batch->fFontCache = fontCache; @@ -89,7 +86,7 @@ public: // We don't yet position distance field text on the cpu, so we have to map the vertex bounds // into device space const Run& run = geo.fBlob->fRuns[geo.fRun]; - if (run.fSubRunInfo[geo.fSubRun].fDrawAsDistanceFields) { + if (run.fSubRunInfo[geo.fSubRun].drawAsDistanceFields()) { SkRect bounds = run.fVertexBounds; fBatch.fViewMatrix.mapRect(&bounds); this->setBounds(bounds); @@ -102,26 +99,6 @@ public: SkString dumpInfo() const override; - static size_t GetVertexStride(GrMaskFormat maskFormat) { - switch (maskFormat) { - case kA8_GrMaskFormat: - return kGrayTextVASize; - case kARGB_GrMaskFormat: - return kColorTextVASize; - default: - return kLCDTextVASize; - } - } - - static size_t GetVertexStrideDf(GrMaskFormat maskFormat, bool useLCDText) { - SkASSERT(maskFormat == kA8_GrMaskFormat); - if (useLCDText) { - return kLCDTextVASize; - } else { - return kGrayTextVASize; - } - } - protected: void computePipelineOptimizations(GrInitInvariantOutput* color, GrInitInvariantOutput* coverage, @@ -220,7 +197,7 @@ private: GrBatchFontCache* fFontCache; // Distance field properties - SkAutoTUnref fDistanceAdjustTable; + SkAutoTUnref fDistanceAdjustTable; SkColor fFilteredColor; typedef GrVertexBatch INHERITED; diff --git a/gfx/skia/skia/src/gpu/batches/GrBatch.h b/gfx/skia/skia/src/gpu/batches/GrBatch.h index 03e396a29c..a66d6ae308 100644 --- a/gfx/skia/skia/src/gpu/batches/GrBatch.h +++ b/gfx/skia/skia/src/gpu/batches/GrBatch.h @@ -16,6 +16,7 @@ class GrCaps; class GrBatchFlushState; +class GrRenderTarget; /** * GrBatch is the base class for all Ganesh deferred geometry generators. To facilitate @@ -113,6 +114,9 @@ public: /** Used for spewing information about batches when debugging. */ virtual SkString dumpInfo() const = 0; + /** Can remove this when multi-draw-buffer lands */ + virtual GrRenderTarget* renderTarget() const = 0; + protected: // NOTE, compute some bounds, even if extremely conservative. Do *NOT* setLargest on the bounds // rect because we outset it for dst copy textures diff --git a/gfx/skia/skia/src/gpu/batches/GrClearBatch.h b/gfx/skia/skia/src/gpu/batches/GrClearBatch.h index 944485ca7e..c38372c1e2 100644 --- a/gfx/skia/skia/src/gpu/batches/GrClearBatch.h +++ b/gfx/skia/skia/src/gpu/batches/GrClearBatch.h @@ -28,6 +28,7 @@ public: const char* name() const override { return "Clear"; } uint32_t renderTargetUniqueID() const override { return fRenderTarget.get()->getUniqueID(); } + GrRenderTarget* renderTarget() const override { return fRenderTarget.get(); } SkString dumpInfo() const override { SkString string; @@ -71,6 +72,7 @@ public: const char* name() const override { return "ClearStencilClip"; } uint32_t renderTargetUniqueID() const override { return fRenderTarget.get()->getUniqueID(); } + GrRenderTarget* renderTarget() const override { return fRenderTarget.get(); } SkString dumpInfo() const override { SkString string; diff --git a/gfx/skia/skia/src/gpu/batches/GrCopySurfaceBatch.h b/gfx/skia/skia/src/gpu/batches/GrCopySurfaceBatch.h index ed5e77f5b0..7bf8d8d8c2 100644 --- a/gfx/skia/skia/src/gpu/batches/GrCopySurfaceBatch.h +++ b/gfx/skia/skia/src/gpu/batches/GrCopySurfaceBatch.h @@ -26,6 +26,7 @@ public: GrRenderTarget* rt = fDst.get()->asRenderTarget(); return rt ? rt->getUniqueID() : 0; } + GrRenderTarget* renderTarget() const override { return fDst.get()->asRenderTarget(); } SkString dumpInfo() const override { SkString string; diff --git a/gfx/skia/skia/src/gpu/batches/GrDashLinePathRenderer.cpp b/gfx/skia/skia/src/gpu/batches/GrDashLinePathRenderer.cpp index e26f5d7627..902dca6d86 100644 --- a/gfx/skia/skia/src/gpu/batches/GrDashLinePathRenderer.cpp +++ b/gfx/skia/skia/src/gpu/batches/GrDashLinePathRenderer.cpp @@ -19,6 +19,7 @@ bool GrDashLinePathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const { } bool GrDashLinePathRenderer::onDrawPath(const DrawPathArgs& args) { + GR_AUDIT_TRAIL_AUTO_FRAME(args.fTarget->getAuditTrail(), "GrDashLinePathRenderer::onDrawPath"); SkPoint pts[2]; SkAssertResult(args.fPath->isLine(pts)); return GrDashingEffect::DrawDashLine(args.fTarget, *args.fPipelineBuilder, args.fColor, diff --git a/gfx/skia/skia/src/gpu/batches/GrDefaultPathRenderer.cpp b/gfx/skia/skia/src/gpu/batches/GrDefaultPathRenderer.cpp index c6ca1cafc7..c39a530645 100644 --- a/gfx/skia/skia/src/gpu/batches/GrDefaultPathRenderer.cpp +++ b/gfx/skia/skia/src/gpu/batches/GrDefaultPathRenderer.cpp @@ -20,6 +20,7 @@ #include "SkTLazy.h" #include "SkTraceEvent.h" +#include "batches/GrRectBatchFactory.h" #include "batches/GrVertexBatch.h" GrDefaultPathRenderer::GrDefaultPathRenderer(bool separateStencilSupport, @@ -289,7 +290,7 @@ private: } if (maxVertices == 0 || maxVertices > ((int)SK_MaxU16 + 1)) { - SkDebugf("Cannot render path (%d)\n", maxVertices); + //SkDebugf("Cannot render path (%d)\n", maxVertices); return; } @@ -697,7 +698,10 @@ bool GrDefaultPathRenderer::internalDrawPath(GrDrawTarget* target, } const SkMatrix& viewM = (reverse && viewMatrix.hasPerspective()) ? SkMatrix::I() : viewMatrix; - target->drawNonAARect(*pipelineBuilder, color, viewM, bounds, localMatrix); + SkAutoTUnref batch( + GrRectBatchFactory::CreateNonAAFill(color, viewM, bounds, nullptr, + &localMatrix)); + target->drawBatch(*pipelineBuilder, batch); } else { if (passCount > 1) { pipelineBuilder->setDisableColorXPFactory(); @@ -726,6 +730,7 @@ bool GrDefaultPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const { } bool GrDefaultPathRenderer::onDrawPath(const DrawPathArgs& args) { + GR_AUDIT_TRAIL_AUTO_FRAME(args.fTarget->getAuditTrail(), "GrDefaultPathRenderer::onDrawPath"); return this->internalDrawPath(args.fTarget, args.fPipelineBuilder, args.fColor, @@ -736,6 +741,7 @@ bool GrDefaultPathRenderer::onDrawPath(const DrawPathArgs& args) { } void GrDefaultPathRenderer::onStencilPath(const StencilPathArgs& args) { + GR_AUDIT_TRAIL_AUTO_FRAME(args.fTarget->getAuditTrail(),"GrDefaultPathRenderer::onStencilPath"); SkASSERT(SkPath::kInverseEvenOdd_FillType != args.fPath->getFillType()); SkASSERT(SkPath::kInverseWinding_FillType != args.fPath->getFillType()); this->internalDrawPath(args.fTarget, args.fPipelineBuilder, GrColor_WHITE, *args.fViewMatrix, diff --git a/gfx/skia/skia/src/gpu/batches/GrDiscardBatch.h b/gfx/skia/skia/src/gpu/batches/GrDiscardBatch.h index aa443cf603..65b6c4bf7d 100644 --- a/gfx/skia/skia/src/gpu/batches/GrDiscardBatch.h +++ b/gfx/skia/skia/src/gpu/batches/GrDiscardBatch.h @@ -26,6 +26,7 @@ public: const char* name() const override { return "Discard"; } uint32_t renderTargetUniqueID() const override { return fRenderTarget.get()->getUniqueID(); } + GrRenderTarget* renderTarget() const override { return fRenderTarget.get(); } SkString dumpInfo() const override { SkString string; diff --git a/gfx/skia/skia/src/gpu/batches/GrDrawBatch.h b/gfx/skia/skia/src/gpu/batches/GrDrawBatch.h index 8e73878981..4d4209ab40 100644 --- a/gfx/skia/skia/src/gpu/batches/GrDrawBatch.h +++ b/gfx/skia/skia/src/gpu/batches/GrDrawBatch.h @@ -63,6 +63,11 @@ public: return this->pipeline()->getRenderTarget()->getUniqueID(); } + GrRenderTarget* renderTarget() const final { + SkASSERT(fPipelineInstalled); + return this->pipeline()->getRenderTarget(); + } + SkString dumpInfo() const override { SkString string; string.appendf("RT: %d\n", this->renderTargetUniqueID()); @@ -78,7 +83,7 @@ public: this->pipeline()->getCoverageFragmentProcessor(i).name(), this->pipeline()->getCoverageFragmentProcessor(i).dumpInfo().c_str()); } - string.appendf("XP: %s\n", this->pipeline()->getXferProcessor()->name()); + string.appendf("XP: %s\n", this->pipeline()->getXferProcessor().name()); return string; } diff --git a/gfx/skia/skia/src/gpu/batches/GrDrawPathBatch.cpp b/gfx/skia/skia/src/gpu/batches/GrDrawPathBatch.cpp index 5209f907de..ff62539932 100644 --- a/gfx/skia/skia/src/gpu/batches/GrDrawPathBatch.cpp +++ b/gfx/skia/skia/src/gpu/batches/GrDrawPathBatch.cpp @@ -7,6 +7,10 @@ #include "GrDrawPathBatch.h" +static void pre_translate_transform_values(const float* xforms, + GrPathRendering::PathTransformType type, int count, + SkScalar x, SkScalar y, float* dst); + SkString GrDrawPathBatch::dumpInfo() const { SkString string; string.printf("PATH: 0x%p", fPath.get()); @@ -25,63 +29,59 @@ void GrDrawPathBatch::onDraw(GrBatchFlushState* state) { state->gpu()->pathRendering()->drawPath(args, fPath.get()); } -GrDrawPathRangeBatch::~GrDrawPathRangeBatch() { - for (DrawList::Iter iter(fDraws); iter.get(); iter.next()) { - (*iter.get())->unref(); - } -} - SkString GrDrawPathRangeBatch::dumpInfo() const { SkString string; - string.printf("RANGE: 0x%p COUNTS: [", *fDraws.head()); + string.printf("RANGE: 0x%p COUNTS: [", fPathRange.get()); for (DrawList::Iter iter(fDraws); iter.get(); iter.next()) { - string.appendf("%d ,", (*iter.get())->count()); + string.appendf("%d, ", iter.get()->fInstanceData->count()); } string.remove(string.size() - 2, 2); string.append("]"); return string; } -bool GrDrawPathRangeBatch::isWinding() const { - static const GrStencilSettings::Face pathFace = GrStencilSettings::kFront_Face; - bool isWinding = kInvert_StencilOp != this->stencilSettings().passOp(pathFace); - if (isWinding) { - // Double check that it is in fact winding. - SkASSERT(kIncClamp_StencilOp == this->stencilSettings().passOp(pathFace)); - SkASSERT(kIncClamp_StencilOp == this->stencilSettings().failOp(pathFace)); - SkASSERT(0x1 != this->stencilSettings().writeMask(pathFace)); - SkASSERT(!this->stencilSettings().isTwoSided()); - } - return isWinding; -} - -GrDrawPathRangeBatch::GrDrawPathRangeBatch(const SkMatrix& viewMatrix, const SkMatrix& localMatrix, - GrColor color, GrPathRange* range, GrPathRangeDraw* draw, - const SkRect& bounds) - : INHERITED(ClassID(), viewMatrix, color) +GrDrawPathRangeBatch::GrDrawPathRangeBatch(const SkMatrix& viewMatrix, SkScalar scale, SkScalar x, + SkScalar y, GrColor color, + GrPathRendering::FillType fill, GrPathRange* range, + const InstanceData* instanceData, const SkRect& bounds) + : INHERITED(ClassID(), viewMatrix, color, fill) , fPathRange(range) - , fLocalMatrix(localMatrix) { - SkDEBUGCODE(draw->fUsedInBatch = true;) - fDraws.addToHead(SkRef(draw)); - fTotalPathCount = draw->count(); + , fTotalPathCount(instanceData->count()) + , fScale(scale) { + fDraws.addToHead()->set(instanceData, x, y); fBounds = bounds; } bool GrDrawPathRangeBatch::onCombineIfPossible(GrBatch* t, const GrCaps& caps) { GrDrawPathRangeBatch* that = t->cast(); - if (this->fPathRange.get() != that->fPathRange.get()) { - return false; - } - if (!GrPathRangeDraw::CanMerge(**this->fDraws.head(), **that->fDraws.head())) { + if (this->fPathRange.get() != that->fPathRange.get() || + this->transformType() != that->transformType() || + this->fScale != that->fScale || + this->color() != that->color() || + !this->viewMatrix().cheapEqualTo(that->viewMatrix())) { return false; } if (!GrPipeline::AreEqual(*this->pipeline(), *that->pipeline(), false)) { return false; } - if (this->color() != that->color() || - !this->viewMatrix().cheapEqualTo(that->viewMatrix()) || - !fLocalMatrix.cheapEqualTo(that->fLocalMatrix)) { - return false; + switch (fDraws.head()->fInstanceData->transformType()) { + case GrPathRendering::kNone_PathTransformType: + if (this->fDraws.head()->fX != that->fDraws.head()->fX || + this->fDraws.head()->fY != that->fDraws.head()->fY) { + return false; + } + break; + case GrPathRendering::kTranslateX_PathTransformType: + if (this->fDraws.head()->fY != that->fDraws.head()->fY) { + return false; + } + break; + case GrPathRendering::kTranslateY_PathTransformType: + if (this->fDraws.head()->fX != that->fDraws.head()->fX) { + return false; + } + break; + default: break; } // TODO: Check some other things here. (winding, opaque, pathProc color, vm, ...) // Try to combine this call with the previous DrawPaths. We do this by stenciling all the @@ -90,54 +90,124 @@ bool GrDrawPathRangeBatch::onCombineIfPossible(GrBatch* t, const GrCaps& caps) { // work). Note that it's also possible for overlapping paths to cancel each other's winding // numbers, and we only partially account for this by not allowing even/odd paths to be // combined. (Glyphs in the same font tend to wind the same direction so it works out OK.) - if (!this->isWinding() || + if (GrPathRendering::kWinding_FillType != this->fillType() || this->stencilSettings() != that->stencilSettings() || this->overrides().willColorBlendWithDst()) { return false; } SkASSERT(!that->overrides().willColorBlendWithDst()); fTotalPathCount += that->fTotalPathCount; - while (GrPathRangeDraw** head = that->fDraws.head()) { - fDraws.addToTail(*head); - // We're stealing that's refs, so pop without unreffing. + while (Draw* head = that->fDraws.head()) { + Draw* draw = fDraws.addToTail(); + draw->fInstanceData.reset(head->fInstanceData.detach()); + draw->fX = head->fX; + draw->fY = head->fY; that->fDraws.popHead(); } return true; } void GrDrawPathRangeBatch::onDraw(GrBatchFlushState* state) { - GrProgramDesc desc; + const Draw& head = *fDraws.head(); + + SkMatrix drawMatrix(this->viewMatrix()); + drawMatrix.preScale(fScale, fScale); + drawMatrix.preTranslate(head.fX, head.fY); + + SkMatrix localMatrix; + localMatrix.setScale(fScale, fScale); + localMatrix.preTranslate(head.fX, head.fY); + SkAutoTUnref pathProc(GrPathProcessor::Create(this->color(), this->overrides(), - this->viewMatrix(), - fLocalMatrix)); + drawMatrix, + localMatrix)); + + GrProgramDesc desc; state->gpu()->buildProgramDesc(&desc, *pathProc, *this->pipeline()); GrPathRendering::DrawPathArgs args(pathProc, this->pipeline(), - &desc, &this->stencilSettings()); + &desc, &this->stencilSettings()); + if (fDraws.count() == 1) { - const GrPathRangeDraw& draw = **fDraws.head(); - state->gpu()->pathRendering()->drawPaths(args, fPathRange.get(), draw.indices(), - GrPathRange::kU16_PathIndexType, draw.transforms(), draw.transformType(), - draw.count()); + const InstanceData& instances = *head.fInstanceData; + state->gpu()->pathRendering()->drawPaths(args, fPathRange.get(), instances.indices(), + GrPathRange::kU16_PathIndexType, + instances.transformValues(), + instances.transformType(), + instances.count()); + } else { + int floatsPerTransform = GrPathRendering::PathTransformSize(this->transformType()); +#if defined(GOOGLE3) + //Stack frame size is limited in GOOGLE3. + SkAutoSTMalloc<512, float> transformStorage(floatsPerTransform * fTotalPathCount); + SkAutoSTMalloc<256, uint16_t> indexStorage(fTotalPathCount); +#else + SkAutoSTMalloc<4096, float> transformStorage(floatsPerTransform * fTotalPathCount); + SkAutoSTMalloc<2048, uint16_t> indexStorage(fTotalPathCount); +#endif + int idx = 0; + for (DrawList::Iter iter(fDraws); iter.get(); iter.next()) { + const Draw& draw = *iter.get(); + const InstanceData& instances = *draw.fInstanceData; + memcpy(&indexStorage[idx], instances.indices(), instances.count() * sizeof(uint16_t)); + pre_translate_transform_values(instances.transformValues(), this->transformType(), + instances.count(), + draw.fX - head.fX, draw.fY - head.fY, + &transformStorage[floatsPerTransform * idx]); + idx += instances.count(); + + // TODO: Support mismatched transform types if we start using more types other than 2D. + SkASSERT(instances.transformType() == this->transformType()); + } + SkASSERT(idx == fTotalPathCount); + + state->gpu()->pathRendering()->drawPaths(args, fPathRange.get(), indexStorage, + GrPathRange::kU16_PathIndexType, transformStorage, + this->transformType(), fTotalPathCount); + } +} + +inline void pre_translate_transform_values(const float* xforms, + GrPathRendering::PathTransformType type, int count, + SkScalar x, SkScalar y, float* dst) { + if (0 == x && 0 == y) { + memcpy(dst, xforms, count * GrPathRendering::PathTransformSize(type) * sizeof(float)); return; } - - GrPathRendering::PathTransformType transformType = (*fDraws.head())->transformType(); - int floatsPerTransform = GrPathRendering::PathTransformSize(transformType); - SkAutoSTMalloc<512, float> transformStorage(floatsPerTransform * fTotalPathCount); - SkAutoSTMalloc<256, uint16_t> indexStorage(fTotalPathCount); - uint16_t* indices = indexStorage.get(); - float* transforms = transformStorage.get(); - for (DrawList::Iter iter(fDraws); iter.get(); iter.next()) { - SkASSERT((*iter.get())->transformType() == transformType); - int cnt = (*iter.get())->count(); - memcpy(indices, (*iter.get())->indices(), cnt * sizeof(uint16_t)); - indices += cnt; - memcpy(transforms, (*iter.get())->transforms(), cnt * floatsPerTransform * sizeof(float)); - transforms += cnt * floatsPerTransform; + switch (type) { + case GrPathRendering::kNone_PathTransformType: + SkFAIL("Cannot pre-translate kNone_PathTransformType."); + break; + case GrPathRendering::kTranslateX_PathTransformType: + SkASSERT(0 == y); + for (int i = 0; i < count; i++) { + dst[i] = xforms[i] + x; + } + break; + case GrPathRendering::kTranslateY_PathTransformType: + SkASSERT(0 == x); + for (int i = 0; i < count; i++) { + dst[i] = xforms[i] + y; + } + break; + case GrPathRendering::kTranslate_PathTransformType: + for (int i = 0; i < 2 * count; i += 2) { + dst[i] = xforms[i] + x; + dst[i + 1] = xforms[i + 1] + y; + } + break; + case GrPathRendering::kAffine_PathTransformType: + for (int i = 0; i < 6 * count; i += 6) { + dst[i] = xforms[i]; + dst[i + 1] = xforms[i + 1]; + dst[i + 2] = xforms[i] * x + xforms[i + 1] * y + xforms[i + 2]; + dst[i + 3] = xforms[i + 3]; + dst[i + 4] = xforms[i + 4]; + dst[i + 5] = xforms[i + 3] * x + xforms[i + 4] * y + xforms[i + 5]; + } + break; + default: + SkFAIL("Unknown transform type."); + break; } - SkASSERT(indices - indexStorage.get() == fTotalPathCount); - state->gpu()->pathRendering()->drawPaths(args, fPathRange.get(), indexStorage.get(), - GrPathRange::kU16_PathIndexType, transformStorage.get(), transformType, - fTotalPathCount); } diff --git a/gfx/skia/skia/src/gpu/batches/GrDrawPathBatch.h b/gfx/skia/skia/src/gpu/batches/GrDrawPathBatch.h index 4067c808dc..0b24fe0546 100644 --- a/gfx/skia/skia/src/gpu/batches/GrDrawPathBatch.h +++ b/gfx/skia/skia/src/gpu/batches/GrDrawPathBatch.h @@ -27,13 +27,17 @@ public: overrides->fUsePLSDstRead = false; } + GrPathRendering::FillType fillType() const { return fFillType; } + void setStencilSettings(const GrStencilSettings& stencil) { fStencilSettings = stencil; } protected: - GrDrawPathBatchBase(uint32_t classID, const SkMatrix& viewMatrix, GrColor initialColor) + GrDrawPathBatchBase(uint32_t classID, const SkMatrix& viewMatrix, GrColor initialColor, + GrPathRendering::FillType fill) : INHERITED(classID) , fViewMatrix(viewMatrix) - , fColor(initialColor) {} + , fColor(initialColor) + , fFillType(fill) {} const GrStencilSettings& stencilSettings() const { return fStencilSettings; } const GrXPOverridesForBatch& overrides() const { return fOverrides; } @@ -48,6 +52,7 @@ private: SkMatrix fViewMatrix; GrColor fColor; + GrPathRendering::FillType fFillType; GrStencilSettings fStencilSettings; GrXPOverridesForBatch fOverrides; @@ -60,8 +65,8 @@ public: // This can't return a more abstract type because we install the stencil settings late :( static GrDrawPathBatchBase* Create(const SkMatrix& viewMatrix, GrColor color, - const GrPath* path) { - return new GrDrawPathBatch(viewMatrix, color, path); + GrPathRendering::FillType fill, const GrPath* path) { + return new GrDrawPathBatch(viewMatrix, color, fill, path); } const char* name() const override { return "DrawPath"; } @@ -69,8 +74,9 @@ public: SkString dumpInfo() const override; private: - GrDrawPathBatch(const SkMatrix& viewMatrix, GrColor color, const GrPath* path) - : INHERITED(ClassID(), viewMatrix, color) + GrDrawPathBatch(const SkMatrix& viewMatrix, GrColor color, GrPathRendering::FillType fill, + const GrPath* path) + : INHERITED(ClassID(), viewMatrix, color, fill) , fPath(path) { fBounds = path->getBounds(); viewMatrix.mapRect(&fBounds); @@ -87,83 +93,91 @@ private: typedef GrDrawPathBatchBase INHERITED; }; -/** - * This could be nested inside the batch class, but for now it must be declarable in a public - * header (GrDrawContext) - */ -class GrPathRangeDraw : public GrNonAtomicRef { -public: - typedef GrPathRendering::PathTransformType TransformType; - - static GrPathRangeDraw* Create(TransformType transformType, int reserveCnt) { - return new GrPathRangeDraw(transformType, reserveCnt); - } - - void append(uint16_t index, float transform[]) { - fTransforms.push_back_n(GrPathRendering::PathTransformSize(fTransformType), transform); - fIndices.push_back(index); - } - - int count() const { return fIndices.count(); } - - TransformType transformType() const { return fTransformType; } - - const float* transforms() const { return fTransforms.begin(); } - - const uint16_t* indices() const { return fIndices.begin(); } - - static bool CanMerge(const GrPathRangeDraw& a, const GrPathRangeDraw& b) { - return a.transformType() == b.transformType(); - } - -private: - GrPathRangeDraw(TransformType transformType, int reserveCnt) - : fTransformType(transformType) - , fIndices(reserveCnt) - , fTransforms(reserveCnt * GrPathRendering::PathTransformSize(transformType)) { - SkDEBUGCODE(fUsedInBatch = false;) - } - - // Reserve space for 64 paths where indices are 16 bit and transforms are translations. - static const int kIndexReserveCnt = 64; - static const int kTransformBufferReserveCnt = 2 * 64; - - GrPathRendering::PathTransformType fTransformType; - SkSTArray fIndices; - SkSTArray fTransforms; - - // To ensure we don't reuse these across batches. -#ifdef SK_DEBUG - bool fUsedInBatch; - friend class GrDrawPathRangeBatch; -#endif - - typedef GrNonAtomicRef INHERITED; -}; - // Template this if we decide to support index types other than 16bit class GrDrawPathRangeBatch final : public GrDrawPathBatchBase { public: + typedef GrPathRendering::PathTransformType TransformType; + DEFINE_BATCH_CLASS_ID - // This can't return a more abstract type because we install the stencil settings late :( - static GrDrawPathBatchBase* Create(const SkMatrix& viewMatrix, const SkMatrix& localMatrix, - GrColor color, GrPathRange* range, GrPathRangeDraw* draw, - const SkRect& bounds) { - return new GrDrawPathRangeBatch(viewMatrix, localMatrix, color, range, draw, bounds); - } + struct InstanceData : public SkNoncopyable { + public: + static InstanceData* Alloc(TransformType transformType, int reserveCnt) { + int transformSize = GrPathRendering::PathTransformSize(transformType); + uint8_t* ptr = (uint8_t*)sk_malloc_throw(Align32(sizeof(InstanceData)) + + Align32(reserveCnt * sizeof(uint16_t)) + + reserveCnt * transformSize * sizeof(float)); + InstanceData* instanceData = (InstanceData*)ptr; + instanceData->fIndices = (uint16_t*)&ptr[Align32(sizeof(InstanceData))]; + instanceData->fTransformValues = (float*)&ptr[Align32(sizeof(InstanceData)) + + Align32(reserveCnt * sizeof(uint16_t))]; + instanceData->fTransformType = transformType; + instanceData->fInstanceCount = 0; + instanceData->fRefCnt = 1; + SkDEBUGCODE(instanceData->fReserveCnt = reserveCnt;) + return instanceData; + } - ~GrDrawPathRangeBatch() override; + // Overload this method if we start using other transform types. + void append(uint16_t index, float x, float y) { + SkASSERT(GrPathRendering::kTranslate_PathTransformType == fTransformType); + SkASSERT(fInstanceCount < fReserveCnt); + fIndices[fInstanceCount] = index; + fTransformValues[2 * fInstanceCount] = x; + fTransformValues[2 * fInstanceCount + 1] = y; + ++fInstanceCount; + } + + TransformType transformType() const { return fTransformType; } + int count() const { return fInstanceCount; } + + const uint16_t* indices() const { return fIndices; } + uint16_t* indices() { return fIndices; } + + const float* transformValues() const { return fTransformValues; } + float* transformValues() { return fTransformValues; } + + void ref() const { ++fRefCnt; } + + void unref() const { + if (0 == --fRefCnt) { + sk_free(const_cast(this)); + } + } + + private: + static int Align32(int sizeInBytes) { return (sizeInBytes + 3) & ~3; } + + InstanceData() {} + ~InstanceData() {} + + uint16_t* fIndices; + float* fTransformValues; + TransformType fTransformType; + int fInstanceCount; + mutable int fRefCnt; + SkDEBUGCODE(int fReserveCnt;) + }; + + // This can't return a more abstract type because we install the stencil settings late :( + static GrDrawPathBatchBase* Create(const SkMatrix& viewMatrix, SkScalar scale, SkScalar x, + SkScalar y, GrColor color, GrPathRendering::FillType fill, + GrPathRange* range, const InstanceData* instanceData, + const SkRect& bounds) { + return new GrDrawPathRangeBatch(viewMatrix, scale, x, y, color, fill, range, instanceData, + bounds); + } const char* name() const override { return "DrawPathRange"; } SkString dumpInfo() const override; private: - inline bool isWinding() const; + GrDrawPathRangeBatch(const SkMatrix& viewMatrix, SkScalar scale, SkScalar x, SkScalar y, + GrColor color, GrPathRendering::FillType fill, GrPathRange* range, + const InstanceData* instanceData, const SkRect& bounds); - GrDrawPathRangeBatch(const SkMatrix& viewMatrix, const SkMatrix& localMatrix, GrColor color, - GrPathRange* range, GrPathRangeDraw* draw, const SkRect& bounds); + TransformType transformType() const { return fDraws.head()->fInstanceData->transformType(); } bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override; @@ -171,12 +185,24 @@ private: void onDraw(GrBatchFlushState* state) override; + struct Draw { + void set(const InstanceData* instanceData, SkScalar x, SkScalar y) { + fInstanceData.reset(SkRef(instanceData)); + fX = x; + fY = y; + } + + SkAutoTUnref fInstanceData; + SkScalar fX, fY; + }; + typedef GrPendingIOResource PendingPathRange; - typedef SkTLList DrawList; + typedef SkTLList DrawList; + PendingPathRange fPathRange; DrawList fDraws; int fTotalPathCount; - SkMatrix fLocalMatrix; + SkScalar fScale; typedef GrDrawPathBatchBase INHERITED; }; diff --git a/gfx/skia/skia/src/gpu/batches/GrStencilAndCoverPathRenderer.cpp b/gfx/skia/skia/src/gpu/batches/GrStencilAndCoverPathRenderer.cpp index a11d2b4683..ada6dbf963 100644 --- a/gfx/skia/skia/src/gpu/batches/GrStencilAndCoverPathRenderer.cpp +++ b/gfx/skia/skia/src/gpu/batches/GrStencilAndCoverPathRenderer.cpp @@ -10,27 +10,13 @@ #include "GrStencilAndCoverPathRenderer.h" #include "GrCaps.h" #include "GrContext.h" +#include "GrDrawPathBatch.h" #include "GrGpu.h" #include "GrPath.h" #include "GrRenderTarget.h" #include "GrResourceProvider.h" #include "GrStrokeInfo.h" - -/* - * For now paths only natively support winding and even odd fill types - */ -static GrPathRendering::FillType convert_skpath_filltype(SkPath::FillType fill) { - switch (fill) { - default: - SkFAIL("Incomplete Switch\n"); - case SkPath::kWinding_FillType: - case SkPath::kInverseWinding_FillType: - return GrPathRendering::kWinding_FillType; - case SkPath::kEvenOdd_FillType: - case SkPath::kInverseEvenOdd_FillType: - return GrPathRendering::kEvenOdd_FillType; - } -} +#include "batches/GrRectBatchFactory.h" GrPathRenderer* GrStencilAndCoverPathRenderer::Create(GrResourceProvider* resourceProvider, const GrCaps& caps) { @@ -78,13 +64,16 @@ static GrPath* get_gr_path(GrResourceProvider* resourceProvider, const SkPath& s } void GrStencilAndCoverPathRenderer::onStencilPath(const StencilPathArgs& args) { + GR_AUDIT_TRAIL_AUTO_FRAME(args.fTarget->getAuditTrail(), + "GrStencilAndCoverPathRenderer::onStencilPath"); SkASSERT(!args.fPath->isInverseFillType()); SkAutoTUnref p(get_gr_path(fResourceProvider, *args.fPath, *args.fStroke)); - args.fTarget->stencilPath(*args.fPipelineBuilder, *args.fViewMatrix, p, - convert_skpath_filltype(args.fPath->getFillType())); + args.fTarget->stencilPath(*args.fPipelineBuilder, *args.fViewMatrix, p, p->getFillType()); } bool GrStencilAndCoverPathRenderer::onDrawPath(const DrawPathArgs& args) { + GR_AUDIT_TRAIL_AUTO_FRAME(args.fTarget->getAuditTrail(), + "GrStencilAndCoverPathRenderer::onDrawPath"); SkASSERT(!args.fStroke->isHairlineStyle()); const SkPath& path = *args.fPath; GrPipelineBuilder* pipelineBuilder = args.fPipelineBuilder; @@ -114,8 +103,7 @@ bool GrStencilAndCoverPathRenderer::onDrawPath(const DrawPathArgs& args) { pipelineBuilder->setStencil(kInvertedStencilPass); // fake inverse with a stencil and cover - args.fTarget->stencilPath(*pipelineBuilder, viewMatrix, p, - convert_skpath_filltype(path.getFillType())); + args.fTarget->stencilPath(*pipelineBuilder, viewMatrix, p, p->getFillType()); SkMatrix invert = SkMatrix::I(); SkRect bounds = @@ -138,7 +126,11 @@ bool GrStencilAndCoverPathRenderer::onDrawPath(const DrawPathArgs& args) { if (pipelineBuilder->getRenderTarget()->hasMixedSamples()) { pipelineBuilder->disableState(GrPipelineBuilder::kHWAntialias_Flag); } - args.fTarget->drawNonAARect(*pipelineBuilder, args.fColor, viewM, bounds, invert); + + SkAutoTUnref batch( + GrRectBatchFactory::CreateNonAAFill(args.fColor, viewM, bounds, nullptr, + &invert)); + args.fTarget->drawBatch(*pipelineBuilder, batch); } else { GR_STATIC_CONST_SAME_STENCIL(kStencilPass, kZero_StencilOp, @@ -149,8 +141,9 @@ bool GrStencilAndCoverPathRenderer::onDrawPath(const DrawPathArgs& args) { 0xffff); pipelineBuilder->setStencil(kStencilPass); - args.fTarget->drawPath(*pipelineBuilder, viewMatrix, args.fColor, p, - convert_skpath_filltype(path.getFillType())); + SkAutoTUnref batch( + GrDrawPathBatch::Create(viewMatrix, args.fColor, p->getFillType(), p)); + args.fTarget->drawPathBatch(*pipelineBuilder, batch); } pipelineBuilder->stencil()->setDisabled(); diff --git a/gfx/skia/skia/src/gpu/batches/GrStencilPathBatch.h b/gfx/skia/skia/src/gpu/batches/GrStencilPathBatch.h index a0dcadb620..33189c2885 100644 --- a/gfx/skia/skia/src/gpu/batches/GrStencilPathBatch.h +++ b/gfx/skia/skia/src/gpu/batches/GrStencilPathBatch.h @@ -31,6 +31,7 @@ public: const char* name() const override { return "StencilPath"; } uint32_t renderTargetUniqueID() const override { return fRenderTarget.get()->getUniqueID(); } + GrRenderTarget* renderTarget() const override { return fRenderTarget.get(); } SkString dumpInfo() const override { SkString string; diff --git a/gfx/skia/skia/src/gpu/batches/GrTessellatingPathRenderer.cpp b/gfx/skia/skia/src/gpu/batches/GrTessellatingPathRenderer.cpp index 27e287e9c6..81436baec0 100644 --- a/gfx/skia/skia/src/gpu/batches/GrTessellatingPathRenderer.cpp +++ b/gfx/skia/skia/src/gpu/batches/GrTessellatingPathRenderer.cpp @@ -14,7 +14,7 @@ #include "GrVertices.h" #include "GrResourceCache.h" #include "GrResourceProvider.h" -#include "SkChunkAlloc.h" +#include "GrTessellator.h" #include "SkGeometry.h" #include "batches/GrVertexBatch.h" @@ -22,1324 +22,29 @@ #include /* - * This path renderer tessellates the path into triangles, uploads the triangles to a - * vertex buffer, and renders them with a single draw call. It does not currently do + * This path renderer tessellates the path into triangles using GrTessellator, uploads the triangles + * to a vertex buffer, and renders them with a single draw call. It does not currently do * antialiasing, so it must be used in conjunction with multisampling. - * - * There are six stages to the algorithm: - * - * 1) Linearize the path contours into piecewise linear segments (path_to_contours()). - * 2) Build a mesh of edges connecting the vertices (build_edges()). - * 3) Sort the vertices in Y (and secondarily in X) (merge_sort()). - * 4) Simplify the mesh by inserting new vertices at intersecting edges (simplify()). - * 5) Tessellate the simplified mesh into monotone polygons (tessellate()). - * 6) Triangulate the monotone polygons directly into a vertex buffer (polys_to_triangles()). - * - * The vertex sorting in step (3) is a merge sort, since it plays well with the linked list - * of vertices (and the necessity of inserting new vertices on intersection). - * - * Stages (4) and (5) use an active edge list, which a list of all edges for which the - * sweep line has crossed the top vertex, but not the bottom vertex. It's sorted - * left-to-right based on the point where both edges are active (when both top vertices - * have been seen, so the "lower" top vertex of the two). If the top vertices are equal - * (shared), it's sorted based on the last point where both edges are active, so the - * "upper" bottom vertex. - * - * The most complex step is the simplification (4). It's based on the Bentley-Ottman - * line-sweep algorithm, but due to floating point inaccuracy, the intersection points are - * not exact and may violate the mesh topology or active edge list ordering. We - * accommodate this by adjusting the topology of the mesh and AEL to match the intersection - * points. This occurs in three ways: - * - * A) Intersections may cause a shortened edge to no longer be ordered with respect to its - * neighbouring edges at the top or bottom vertex. This is handled by merging the - * edges (merge_collinear_edges()). - * B) Intersections may cause an edge to violate the left-to-right ordering of the - * active edge list. This is handled by splitting the neighbour edge on the - * intersected vertex (cleanup_active_edges()). - * C) Shortening an edge may cause an active edge to become inactive or an inactive edge - * to become active. This is handled by removing or inserting the edge in the active - * edge list (fix_active_state()). - * - * The tessellation steps (5) and (6) are based on "Triangulating Simple Polygons and - * Equivalent Problems" (Fournier and Montuno); also a line-sweep algorithm. Note that it - * currently uses a linked list for the active edge list, rather than a 2-3 tree as the - * paper describes. The 2-3 tree gives O(lg N) lookups, but insertion and removal also - * become O(lg N). In all the test cases, it was found that the cost of frequent O(lg N) - * insertions and removals was greater than the cost of infrequent O(N) lookups with the - * linked list implementation. With the latter, all removals are O(1), and most insertions - * are O(1), since we know the adjacent edge in the active edge list based on the topology. - * Only type 2 vertices (see paper) require the O(N) lookups, and these are much less - * frequent. There may be other data structures worth investigating, however. - * - * Note that the orientation of the line sweep algorithms is determined by the aspect ratio of the - * path bounds. When the path is taller than it is wide, we sort vertices based on increasing Y - * coordinate, and secondarily by increasing X coordinate. When the path is wider than it is tall, - * we sort by increasing X coordinate, but secondarily by *decreasing* Y coordinate. This is so - * that the "left" and "right" orientation in the code remains correct (edges to the left are - * increasing in Y; edges to the right are decreasing in Y). That is, the setting rotates 90 - * degrees counterclockwise, rather that transposing. */ -#define LOGGING_ENABLED 0 -#define WIREFRAME 0 - -#if LOGGING_ENABLED -#define LOG printf -#else -#define LOG(...) -#endif - -#define ALLOC_NEW(Type, args, alloc) new (alloc.allocThrow(sizeof(Type))) Type args - namespace { -struct Vertex; -struct Edge; -struct Poly; - -template -void insert(T* t, T* prev, T* next, T** head, T** tail) { - t->*Prev = prev; - t->*Next = next; - if (prev) { - prev->*Next = t; - } else if (head) { - *head = t; - } - if (next) { - next->*Prev = t; - } else if (tail) { - *tail = t; - } -} - -template -void remove(T* t, T** head, T** tail) { - if (t->*Prev) { - t->*Prev->*Next = t->*Next; - } else if (head) { - *head = t->*Next; - } - if (t->*Next) { - t->*Next->*Prev = t->*Prev; - } else if (tail) { - *tail = t->*Prev; - } - t->*Prev = t->*Next = nullptr; -} - -/** - * Vertices are used in three ways: first, the path contours are converted into a - * circularly-linked list of Vertices for each contour. After edge construction, the same Vertices - * are re-ordered by the merge sort according to the sweep_lt comparator (usually, increasing - * in Y) using the same fPrev/fNext pointers that were used for the contours, to avoid - * reallocation. Finally, MonotonePolys are built containing a circularly-linked list of - * Vertices. (Currently, those Vertices are newly-allocated for the MonotonePolys, since - * an individual Vertex from the path mesh may belong to multiple - * MonotonePolys, so the original Vertices cannot be re-used. - */ - -struct Vertex { - Vertex(const SkPoint& point) - : fPoint(point), fPrev(nullptr), fNext(nullptr) - , fFirstEdgeAbove(nullptr), fLastEdgeAbove(nullptr) - , fFirstEdgeBelow(nullptr), fLastEdgeBelow(nullptr) - , fProcessed(false) -#if LOGGING_ENABLED - , fID (-1.0f) -#endif - {} - SkPoint fPoint; // Vertex position - Vertex* fPrev; // Linked list of contours, then Y-sorted vertices. - Vertex* fNext; // " - Edge* fFirstEdgeAbove; // Linked list of edges above this vertex. - Edge* fLastEdgeAbove; // " - Edge* fFirstEdgeBelow; // Linked list of edges below this vertex. - Edge* fLastEdgeBelow; // " - bool fProcessed; // Has this vertex been seen in simplify()? -#if LOGGING_ENABLED - float fID; // Identifier used for logging. -#endif -}; - -/***************************************************************************************/ - -typedef bool (*CompareFunc)(const SkPoint& a, const SkPoint& b); - -struct Comparator { - CompareFunc sweep_lt; - CompareFunc sweep_gt; -}; - -bool sweep_lt_horiz(const SkPoint& a, const SkPoint& b) { - return a.fX == b.fX ? a.fY > b.fY : a.fX < b.fX; -} - -bool sweep_lt_vert(const SkPoint& a, const SkPoint& b) { - return a.fY == b.fY ? a.fX < b.fX : a.fY < b.fY; -} - -bool sweep_gt_horiz(const SkPoint& a, const SkPoint& b) { - return a.fX == b.fX ? a.fY < b.fY : a.fX > b.fX; -} - -bool sweep_gt_vert(const SkPoint& a, const SkPoint& b) { - return a.fY == b.fY ? a.fX > b.fX : a.fY > b.fY; -} - -inline SkPoint* emit_vertex(Vertex* v, SkPoint* data) { - *data++ = v->fPoint; - return data; -} - -SkPoint* emit_triangle(Vertex* v0, Vertex* v1, Vertex* v2, SkPoint* data) { -#if WIREFRAME - data = emit_vertex(v0, data); - data = emit_vertex(v1, data); - data = emit_vertex(v1, data); - data = emit_vertex(v2, data); - data = emit_vertex(v2, data); - data = emit_vertex(v0, data); -#else - data = emit_vertex(v0, data); - data = emit_vertex(v1, data); - data = emit_vertex(v2, data); -#endif - return data; -} - -struct EdgeList { - EdgeList() : fHead(nullptr), fTail(nullptr) {} - Edge* fHead; - Edge* fTail; -}; - -/** - * An Edge joins a top Vertex to a bottom Vertex. Edge ordering for the list of "edges above" and - * "edge below" a vertex as well as for the active edge list is handled by isLeftOf()/isRightOf(). - * Note that an Edge will give occasionally dist() != 0 for its own endpoints (because floating - * point). For speed, that case is only tested by the callers which require it (e.g., - * cleanup_active_edges()). Edges also handle checking for intersection with other edges. - * Currently, this converts the edges to the parametric form, in order to avoid doing a division - * until an intersection has been confirmed. This is slightly slower in the "found" case, but - * a lot faster in the "not found" case. - * - * The coefficients of the line equation stored in double precision to avoid catastrphic - * cancellation in the isLeftOf() and isRightOf() checks. Using doubles ensures that the result is - * correct in float, since it's a polynomial of degree 2. The intersect() function, being - * degree 5, is still subject to catastrophic cancellation. We deal with that by assuming its - * output may be incorrect, and adjusting the mesh topology to match (see comment at the top of - * this file). - */ - -struct Edge { - Edge(Vertex* top, Vertex* bottom, int winding) - : fWinding(winding) - , fTop(top) - , fBottom(bottom) - , fLeft(nullptr) - , fRight(nullptr) - , fPrevEdgeAbove(nullptr) - , fNextEdgeAbove(nullptr) - , fPrevEdgeBelow(nullptr) - , fNextEdgeBelow(nullptr) - , fLeftPoly(nullptr) - , fRightPoly(nullptr) { - recompute(); - } - int fWinding; // 1 == edge goes downward; -1 = edge goes upward. - Vertex* fTop; // The top vertex in vertex-sort-order (sweep_lt). - Vertex* fBottom; // The bottom vertex in vertex-sort-order. - Edge* fLeft; // The linked list of edges in the active edge list. - Edge* fRight; // " - Edge* fPrevEdgeAbove; // The linked list of edges in the bottom Vertex's "edges above". - Edge* fNextEdgeAbove; // " - Edge* fPrevEdgeBelow; // The linked list of edges in the top Vertex's "edges below". - Edge* fNextEdgeBelow; // " - Poly* fLeftPoly; // The Poly to the left of this edge, if any. - Poly* fRightPoly; // The Poly to the right of this edge, if any. - double fDX; // The line equation for this edge, in implicit form. - double fDY; // fDY * x + fDX * y + fC = 0, for point (x, y) on the line. - double fC; - double dist(const SkPoint& p) const { - return fDY * p.fX - fDX * p.fY + fC; - } - bool isRightOf(Vertex* v) const { - return dist(v->fPoint) < 0.0; - } - bool isLeftOf(Vertex* v) const { - return dist(v->fPoint) > 0.0; - } - void recompute() { - fDX = static_cast(fBottom->fPoint.fX) - fTop->fPoint.fX; - fDY = static_cast(fBottom->fPoint.fY) - fTop->fPoint.fY; - fC = static_cast(fTop->fPoint.fY) * fBottom->fPoint.fX - - static_cast(fTop->fPoint.fX) * fBottom->fPoint.fY; - } - bool intersect(const Edge& other, SkPoint* p) { - LOG("intersecting %g -> %g with %g -> %g\n", - fTop->fID, fBottom->fID, - other.fTop->fID, other.fBottom->fID); - if (fTop == other.fTop || fBottom == other.fBottom) { - return false; - } - double denom = fDX * other.fDY - fDY * other.fDX; - if (denom == 0.0) { - return false; - } - double dx = static_cast(fTop->fPoint.fX) - other.fTop->fPoint.fX; - double dy = static_cast(fTop->fPoint.fY) - other.fTop->fPoint.fY; - double sNumer = dy * other.fDX - dx * other.fDY; - double tNumer = dy * fDX - dx * fDY; - // If (sNumer / denom) or (tNumer / denom) is not in [0..1], exit early. - // This saves us doing the divide below unless absolutely necessary. - if (denom > 0.0 ? (sNumer < 0.0 || sNumer > denom || tNumer < 0.0 || tNumer > denom) - : (sNumer > 0.0 || sNumer < denom || tNumer > 0.0 || tNumer < denom)) { - return false; - } - double s = sNumer / denom; - SkASSERT(s >= 0.0 && s <= 1.0); - p->fX = SkDoubleToScalar(fTop->fPoint.fX + s * fDX); - p->fY = SkDoubleToScalar(fTop->fPoint.fY + s * fDY); - return true; - } - bool isActive(EdgeList* activeEdges) const { - return activeEdges && (fLeft || fRight || activeEdges->fHead == this); - } -}; - -/***************************************************************************************/ - -struct Poly { - Poly(int winding) - : fWinding(winding) - , fHead(nullptr) - , fTail(nullptr) - , fActive(nullptr) - , fNext(nullptr) - , fPartner(nullptr) - , fCount(0) - { -#if LOGGING_ENABLED - static int gID = 0; - fID = gID++; - LOG("*** created Poly %d\n", fID); -#endif - } - typedef enum { kNeither_Side, kLeft_Side, kRight_Side } Side; - struct MonotonePoly { - MonotonePoly() - : fSide(kNeither_Side) - , fHead(nullptr) - , fTail(nullptr) - , fPrev(nullptr) - , fNext(nullptr) {} - Side fSide; - Vertex* fHead; - Vertex* fTail; - MonotonePoly* fPrev; - MonotonePoly* fNext; - bool addVertex(Vertex* v, Side side, SkChunkAlloc& alloc) { - Vertex* newV = ALLOC_NEW(Vertex, (v->fPoint), alloc); - bool done = false; - if (fSide == kNeither_Side) { - fSide = side; - } else { - done = side != fSide; - } - if (fHead == nullptr) { - fHead = fTail = newV; - } else if (fSide == kRight_Side) { - newV->fPrev = fTail; - fTail->fNext = newV; - fTail = newV; - } else { - newV->fNext = fHead; - fHead->fPrev = newV; - fHead = newV; - } - return done; - } - - SkPoint* emit(SkPoint* data) { - Vertex* first = fHead; - Vertex* v = first->fNext; - while (v != fTail) { - SkASSERT(v && v->fPrev && v->fNext); - Vertex* prev = v->fPrev; - Vertex* curr = v; - Vertex* next = v->fNext; - double ax = static_cast(curr->fPoint.fX) - prev->fPoint.fX; - double ay = static_cast(curr->fPoint.fY) - prev->fPoint.fY; - double bx = static_cast(next->fPoint.fX) - curr->fPoint.fX; - double by = static_cast(next->fPoint.fY) - curr->fPoint.fY; - if (ax * by - ay * bx >= 0.0) { - data = emit_triangle(prev, curr, next, data); - v->fPrev->fNext = v->fNext; - v->fNext->fPrev = v->fPrev; - if (v->fPrev == first) { - v = v->fNext; - } else { - v = v->fPrev; - } - } else { - v = v->fNext; - } - } - return data; - } - }; - Poly* addVertex(Vertex* v, Side side, SkChunkAlloc& alloc) { - LOG("addVertex() to %d at %g (%g, %g), %s side\n", fID, v->fID, v->fPoint.fX, v->fPoint.fY, - side == kLeft_Side ? "left" : side == kRight_Side ? "right" : "neither"); - Poly* partner = fPartner; - Poly* poly = this; - if (partner) { - fPartner = partner->fPartner = nullptr; - } - if (!fActive) { - fActive = ALLOC_NEW(MonotonePoly, (), alloc); - } - if (fActive->addVertex(v, side, alloc)) { - if (fTail) { - fActive->fPrev = fTail; - fTail->fNext = fActive; - fTail = fActive; - } else { - fHead = fTail = fActive; - } - if (partner) { - partner->addVertex(v, side, alloc); - poly = partner; - } else { - Vertex* prev = fActive->fSide == Poly::kLeft_Side ? - fActive->fHead->fNext : fActive->fTail->fPrev; - fActive = ALLOC_NEW(MonotonePoly, , alloc); - fActive->addVertex(prev, Poly::kNeither_Side, alloc); - fActive->addVertex(v, side, alloc); - } - } - fCount++; - return poly; - } - void end(Vertex* v, SkChunkAlloc& alloc) { - LOG("end() %d at %g, %g\n", fID, v->fPoint.fX, v->fPoint.fY); - if (fPartner) { - fPartner = fPartner->fPartner = nullptr; - } - addVertex(v, fActive->fSide == kLeft_Side ? kRight_Side : kLeft_Side, alloc); - } - SkPoint* emit(SkPoint *data) { - if (fCount < 3) { - return data; - } - LOG("emit() %d, size %d\n", fID, fCount); - for (MonotonePoly* m = fHead; m != nullptr; m = m->fNext) { - data = m->emit(data); - } - return data; - } - int fWinding; - MonotonePoly* fHead; - MonotonePoly* fTail; - MonotonePoly* fActive; - Poly* fNext; - Poly* fPartner; - int fCount; -#if LOGGING_ENABLED - int fID; -#endif -}; - -/***************************************************************************************/ - -bool coincident(const SkPoint& a, const SkPoint& b) { - return a == b; -} - -Poly* new_poly(Poly** head, Vertex* v, int winding, SkChunkAlloc& alloc) { - Poly* poly = ALLOC_NEW(Poly, (winding), alloc); - poly->addVertex(v, Poly::kNeither_Side, alloc); - poly->fNext = *head; - *head = poly; - return poly; -} - -Vertex* append_point_to_contour(const SkPoint& p, Vertex* prev, Vertex** head, - SkChunkAlloc& alloc) { - Vertex* v = ALLOC_NEW(Vertex, (p), alloc); -#if LOGGING_ENABLED - static float gID = 0.0f; - v->fID = gID++; -#endif - if (prev) { - prev->fNext = v; - v->fPrev = prev; - } else { - *head = v; - } - return v; -} - -Vertex* generate_quadratic_points(const SkPoint& p0, - const SkPoint& p1, - const SkPoint& p2, - SkScalar tolSqd, - Vertex* prev, - Vertex** head, - int pointsLeft, - SkChunkAlloc& alloc) { - SkScalar d = p1.distanceToLineSegmentBetweenSqd(p0, p2); - if (pointsLeft < 2 || d < tolSqd || !SkScalarIsFinite(d)) { - return append_point_to_contour(p2, prev, head, alloc); - } - - const SkPoint q[] = { - { SkScalarAve(p0.fX, p1.fX), SkScalarAve(p0.fY, p1.fY) }, - { SkScalarAve(p1.fX, p2.fX), SkScalarAve(p1.fY, p2.fY) }, - }; - const SkPoint r = { SkScalarAve(q[0].fX, q[1].fX), SkScalarAve(q[0].fY, q[1].fY) }; - - pointsLeft >>= 1; - prev = generate_quadratic_points(p0, q[0], r, tolSqd, prev, head, pointsLeft, alloc); - prev = generate_quadratic_points(r, q[1], p2, tolSqd, prev, head, pointsLeft, alloc); - return prev; -} - -Vertex* generate_cubic_points(const SkPoint& p0, - const SkPoint& p1, - const SkPoint& p2, - const SkPoint& p3, - SkScalar tolSqd, - Vertex* prev, - Vertex** head, - int pointsLeft, - SkChunkAlloc& alloc) { - SkScalar d1 = p1.distanceToLineSegmentBetweenSqd(p0, p3); - SkScalar d2 = p2.distanceToLineSegmentBetweenSqd(p0, p3); - if (pointsLeft < 2 || (d1 < tolSqd && d2 < tolSqd) || - !SkScalarIsFinite(d1) || !SkScalarIsFinite(d2)) { - return append_point_to_contour(p3, prev, head, alloc); - } - const SkPoint q[] = { - { SkScalarAve(p0.fX, p1.fX), SkScalarAve(p0.fY, p1.fY) }, - { SkScalarAve(p1.fX, p2.fX), SkScalarAve(p1.fY, p2.fY) }, - { SkScalarAve(p2.fX, p3.fX), SkScalarAve(p2.fY, p3.fY) } - }; - const SkPoint r[] = { - { SkScalarAve(q[0].fX, q[1].fX), SkScalarAve(q[0].fY, q[1].fY) }, - { SkScalarAve(q[1].fX, q[2].fX), SkScalarAve(q[1].fY, q[2].fY) } - }; - const SkPoint s = { SkScalarAve(r[0].fX, r[1].fX), SkScalarAve(r[0].fY, r[1].fY) }; - pointsLeft >>= 1; - prev = generate_cubic_points(p0, q[0], r[0], s, tolSqd, prev, head, pointsLeft, alloc); - prev = generate_cubic_points(s, r[1], q[2], p3, tolSqd, prev, head, pointsLeft, alloc); - return prev; -} - -// Stage 1: convert the input path to a set of linear contours (linked list of Vertices). - -void path_to_contours(const SkPath& path, SkScalar tolerance, const SkRect& clipBounds, - Vertex** contours, SkChunkAlloc& alloc, bool *isLinear) { - - SkScalar toleranceSqd = tolerance * tolerance; - - SkPoint pts[4]; - bool done = false; - *isLinear = true; - SkPath::Iter iter(path, false); - Vertex* prev = nullptr; - Vertex* head = nullptr; - if (path.isInverseFillType()) { - SkPoint quad[4]; - clipBounds.toQuad(quad); - for (int i = 3; i >= 0; i--) { - prev = append_point_to_contour(quad[i], prev, &head, alloc); - } - head->fPrev = prev; - prev->fNext = head; - *contours++ = head; - head = prev = nullptr; - } - SkAutoConicToQuads converter; - while (!done) { - SkPath::Verb verb = iter.next(pts); - switch (verb) { - case SkPath::kConic_Verb: { - SkScalar weight = iter.conicWeight(); - const SkPoint* quadPts = converter.computeQuads(pts, weight, toleranceSqd); - for (int i = 0; i < converter.countQuads(); ++i) { - int pointsLeft = GrPathUtils::quadraticPointCount(quadPts, tolerance); - prev = generate_quadratic_points(quadPts[0], quadPts[1], quadPts[2], - toleranceSqd, prev, &head, pointsLeft, alloc); - quadPts += 2; - } - *isLinear = false; - break; - } - case SkPath::kMove_Verb: - if (head) { - head->fPrev = prev; - prev->fNext = head; - *contours++ = head; - } - head = prev = nullptr; - prev = append_point_to_contour(pts[0], prev, &head, alloc); - break; - case SkPath::kLine_Verb: { - prev = append_point_to_contour(pts[1], prev, &head, alloc); - break; - } - case SkPath::kQuad_Verb: { - int pointsLeft = GrPathUtils::quadraticPointCount(pts, tolerance); - prev = generate_quadratic_points(pts[0], pts[1], pts[2], toleranceSqd, prev, - &head, pointsLeft, alloc); - *isLinear = false; - break; - } - case SkPath::kCubic_Verb: { - int pointsLeft = GrPathUtils::cubicPointCount(pts, tolerance); - prev = generate_cubic_points(pts[0], pts[1], pts[2], pts[3], - toleranceSqd, prev, &head, pointsLeft, alloc); - *isLinear = false; - break; - } - case SkPath::kClose_Verb: - if (head) { - head->fPrev = prev; - prev->fNext = head; - *contours++ = head; - } - head = prev = nullptr; - break; - case SkPath::kDone_Verb: - if (head) { - head->fPrev = prev; - prev->fNext = head; - *contours++ = head; - } - done = true; - break; - } - } -} - -inline bool apply_fill_type(SkPath::FillType fillType, int winding) { - switch (fillType) { - case SkPath::kWinding_FillType: - return winding != 0; - case SkPath::kEvenOdd_FillType: - return (winding & 1) != 0; - case SkPath::kInverseWinding_FillType: - return winding == 1; - case SkPath::kInverseEvenOdd_FillType: - return (winding & 1) == 1; - default: - SkASSERT(false); - return false; - } -} - -Edge* new_edge(Vertex* prev, Vertex* next, SkChunkAlloc& alloc, Comparator& c) { - int winding = c.sweep_lt(prev->fPoint, next->fPoint) ? 1 : -1; - Vertex* top = winding < 0 ? next : prev; - Vertex* bottom = winding < 0 ? prev : next; - return ALLOC_NEW(Edge, (top, bottom, winding), alloc); -} - -void remove_edge(Edge* edge, EdgeList* edges) { - LOG("removing edge %g -> %g\n", edge->fTop->fID, edge->fBottom->fID); - SkASSERT(edge->isActive(edges)); - remove(edge, &edges->fHead, &edges->fTail); -} - -void insert_edge(Edge* edge, Edge* prev, EdgeList* edges) { - LOG("inserting edge %g -> %g\n", edge->fTop->fID, edge->fBottom->fID); - SkASSERT(!edge->isActive(edges)); - Edge* next = prev ? prev->fRight : edges->fHead; - insert(edge, prev, next, &edges->fHead, &edges->fTail); -} - -void find_enclosing_edges(Vertex* v, EdgeList* edges, Edge** left, Edge** right) { - if (v->fFirstEdgeAbove) { - *left = v->fFirstEdgeAbove->fLeft; - *right = v->fLastEdgeAbove->fRight; - return; - } - Edge* next = nullptr; - Edge* prev; - for (prev = edges->fTail; prev != nullptr; prev = prev->fLeft) { - if (prev->isLeftOf(v)) { - break; - } - next = prev; - } - *left = prev; - *right = next; - return; -} - -void find_enclosing_edges(Edge* edge, EdgeList* edges, Comparator& c, Edge** left, Edge** right) { - Edge* prev = nullptr; - Edge* next; - for (next = edges->fHead; next != nullptr; next = next->fRight) { - if ((c.sweep_gt(edge->fTop->fPoint, next->fTop->fPoint) && next->isRightOf(edge->fTop)) || - (c.sweep_gt(next->fTop->fPoint, edge->fTop->fPoint) && edge->isLeftOf(next->fTop)) || - (c.sweep_lt(edge->fBottom->fPoint, next->fBottom->fPoint) && - next->isRightOf(edge->fBottom)) || - (c.sweep_lt(next->fBottom->fPoint, edge->fBottom->fPoint) && - edge->isLeftOf(next->fBottom))) { - break; - } - prev = next; - } - *left = prev; - *right = next; - return; -} - -void fix_active_state(Edge* edge, EdgeList* activeEdges, Comparator& c) { - if (edge->isActive(activeEdges)) { - if (edge->fBottom->fProcessed || !edge->fTop->fProcessed) { - remove_edge(edge, activeEdges); - } - } else if (edge->fTop->fProcessed && !edge->fBottom->fProcessed) { - Edge* left; - Edge* right; - find_enclosing_edges(edge, activeEdges, c, &left, &right); - insert_edge(edge, left, activeEdges); - } -} - -void insert_edge_above(Edge* edge, Vertex* v, Comparator& c) { - if (edge->fTop->fPoint == edge->fBottom->fPoint || - c.sweep_gt(edge->fTop->fPoint, edge->fBottom->fPoint)) { - return; - } - LOG("insert edge (%g -> %g) above vertex %g\n", edge->fTop->fID, edge->fBottom->fID, v->fID); - Edge* prev = nullptr; - Edge* next; - for (next = v->fFirstEdgeAbove; next; next = next->fNextEdgeAbove) { - if (next->isRightOf(edge->fTop)) { - break; - } - prev = next; - } - insert( - edge, prev, next, &v->fFirstEdgeAbove, &v->fLastEdgeAbove); -} - -void insert_edge_below(Edge* edge, Vertex* v, Comparator& c) { - if (edge->fTop->fPoint == edge->fBottom->fPoint || - c.sweep_gt(edge->fTop->fPoint, edge->fBottom->fPoint)) { - return; - } - LOG("insert edge (%g -> %g) below vertex %g\n", edge->fTop->fID, edge->fBottom->fID, v->fID); - Edge* prev = nullptr; - Edge* next; - for (next = v->fFirstEdgeBelow; next; next = next->fNextEdgeBelow) { - if (next->isRightOf(edge->fBottom)) { - break; - } - prev = next; - } - insert( - edge, prev, next, &v->fFirstEdgeBelow, &v->fLastEdgeBelow); -} - -void remove_edge_above(Edge* edge) { - LOG("removing edge (%g -> %g) above vertex %g\n", edge->fTop->fID, edge->fBottom->fID, - edge->fBottom->fID); - remove( - edge, &edge->fBottom->fFirstEdgeAbove, &edge->fBottom->fLastEdgeAbove); -} - -void remove_edge_below(Edge* edge) { - LOG("removing edge (%g -> %g) below vertex %g\n", edge->fTop->fID, edge->fBottom->fID, - edge->fTop->fID); - remove( - edge, &edge->fTop->fFirstEdgeBelow, &edge->fTop->fLastEdgeBelow); -} - -void erase_edge_if_zero_winding(Edge* edge, EdgeList* edges) { - if (edge->fWinding != 0) { - return; - } - LOG("erasing edge (%g -> %g)\n", edge->fTop->fID, edge->fBottom->fID); - remove_edge_above(edge); - remove_edge_below(edge); - if (edge->isActive(edges)) { - remove_edge(edge, edges); - } -} - -void merge_collinear_edges(Edge* edge, EdgeList* activeEdges, Comparator& c); - -void set_top(Edge* edge, Vertex* v, EdgeList* activeEdges, Comparator& c) { - remove_edge_below(edge); - edge->fTop = v; - edge->recompute(); - insert_edge_below(edge, v, c); - fix_active_state(edge, activeEdges, c); - merge_collinear_edges(edge, activeEdges, c); -} - -void set_bottom(Edge* edge, Vertex* v, EdgeList* activeEdges, Comparator& c) { - remove_edge_above(edge); - edge->fBottom = v; - edge->recompute(); - insert_edge_above(edge, v, c); - fix_active_state(edge, activeEdges, c); - merge_collinear_edges(edge, activeEdges, c); -} - -void merge_edges_above(Edge* edge, Edge* other, EdgeList* activeEdges, Comparator& c) { - if (coincident(edge->fTop->fPoint, other->fTop->fPoint)) { - LOG("merging coincident above edges (%g, %g) -> (%g, %g)\n", - edge->fTop->fPoint.fX, edge->fTop->fPoint.fY, - edge->fBottom->fPoint.fX, edge->fBottom->fPoint.fY); - other->fWinding += edge->fWinding; - erase_edge_if_zero_winding(other, activeEdges); - edge->fWinding = 0; - erase_edge_if_zero_winding(edge, activeEdges); - } else if (c.sweep_lt(edge->fTop->fPoint, other->fTop->fPoint)) { - other->fWinding += edge->fWinding; - erase_edge_if_zero_winding(other, activeEdges); - set_bottom(edge, other->fTop, activeEdges, c); - } else { - edge->fWinding += other->fWinding; - erase_edge_if_zero_winding(edge, activeEdges); - set_bottom(other, edge->fTop, activeEdges, c); - } -} - -void merge_edges_below(Edge* edge, Edge* other, EdgeList* activeEdges, Comparator& c) { - if (coincident(edge->fBottom->fPoint, other->fBottom->fPoint)) { - LOG("merging coincident below edges (%g, %g) -> (%g, %g)\n", - edge->fTop->fPoint.fX, edge->fTop->fPoint.fY, - edge->fBottom->fPoint.fX, edge->fBottom->fPoint.fY); - other->fWinding += edge->fWinding; - erase_edge_if_zero_winding(other, activeEdges); - edge->fWinding = 0; - erase_edge_if_zero_winding(edge, activeEdges); - } else if (c.sweep_lt(edge->fBottom->fPoint, other->fBottom->fPoint)) { - edge->fWinding += other->fWinding; - erase_edge_if_zero_winding(edge, activeEdges); - set_top(other, edge->fBottom, activeEdges, c); - } else { - other->fWinding += edge->fWinding; - erase_edge_if_zero_winding(other, activeEdges); - set_top(edge, other->fBottom, activeEdges, c); - } -} - -void merge_collinear_edges(Edge* edge, EdgeList* activeEdges, Comparator& c) { - if (edge->fPrevEdgeAbove && (edge->fTop == edge->fPrevEdgeAbove->fTop || - !edge->fPrevEdgeAbove->isLeftOf(edge->fTop))) { - merge_edges_above(edge, edge->fPrevEdgeAbove, activeEdges, c); - } else if (edge->fNextEdgeAbove && (edge->fTop == edge->fNextEdgeAbove->fTop || - !edge->isLeftOf(edge->fNextEdgeAbove->fTop))) { - merge_edges_above(edge, edge->fNextEdgeAbove, activeEdges, c); - } - if (edge->fPrevEdgeBelow && (edge->fBottom == edge->fPrevEdgeBelow->fBottom || - !edge->fPrevEdgeBelow->isLeftOf(edge->fBottom))) { - merge_edges_below(edge, edge->fPrevEdgeBelow, activeEdges, c); - } else if (edge->fNextEdgeBelow && (edge->fBottom == edge->fNextEdgeBelow->fBottom || - !edge->isLeftOf(edge->fNextEdgeBelow->fBottom))) { - merge_edges_below(edge, edge->fNextEdgeBelow, activeEdges, c); - } -} - -void split_edge(Edge* edge, Vertex* v, EdgeList* activeEdges, Comparator& c, SkChunkAlloc& alloc); - -void cleanup_active_edges(Edge* edge, EdgeList* activeEdges, Comparator& c, SkChunkAlloc& alloc) { - Vertex* top = edge->fTop; - Vertex* bottom = edge->fBottom; - if (edge->fLeft) { - Vertex* leftTop = edge->fLeft->fTop; - Vertex* leftBottom = edge->fLeft->fBottom; - if (c.sweep_gt(top->fPoint, leftTop->fPoint) && !edge->fLeft->isLeftOf(top)) { - split_edge(edge->fLeft, edge->fTop, activeEdges, c, alloc); - } else if (c.sweep_gt(leftTop->fPoint, top->fPoint) && !edge->isRightOf(leftTop)) { - split_edge(edge, leftTop, activeEdges, c, alloc); - } else if (c.sweep_lt(bottom->fPoint, leftBottom->fPoint) && - !edge->fLeft->isLeftOf(bottom)) { - split_edge(edge->fLeft, bottom, activeEdges, c, alloc); - } else if (c.sweep_lt(leftBottom->fPoint, bottom->fPoint) && !edge->isRightOf(leftBottom)) { - split_edge(edge, leftBottom, activeEdges, c, alloc); - } - } - if (edge->fRight) { - Vertex* rightTop = edge->fRight->fTop; - Vertex* rightBottom = edge->fRight->fBottom; - if (c.sweep_gt(top->fPoint, rightTop->fPoint) && !edge->fRight->isRightOf(top)) { - split_edge(edge->fRight, top, activeEdges, c, alloc); - } else if (c.sweep_gt(rightTop->fPoint, top->fPoint) && !edge->isLeftOf(rightTop)) { - split_edge(edge, rightTop, activeEdges, c, alloc); - } else if (c.sweep_lt(bottom->fPoint, rightBottom->fPoint) && - !edge->fRight->isRightOf(bottom)) { - split_edge(edge->fRight, bottom, activeEdges, c, alloc); - } else if (c.sweep_lt(rightBottom->fPoint, bottom->fPoint) && - !edge->isLeftOf(rightBottom)) { - split_edge(edge, rightBottom, activeEdges, c, alloc); - } - } -} - -void split_edge(Edge* edge, Vertex* v, EdgeList* activeEdges, Comparator& c, SkChunkAlloc& alloc) { - LOG("splitting edge (%g -> %g) at vertex %g (%g, %g)\n", - edge->fTop->fID, edge->fBottom->fID, - v->fID, v->fPoint.fX, v->fPoint.fY); - if (c.sweep_lt(v->fPoint, edge->fTop->fPoint)) { - set_top(edge, v, activeEdges, c); - } else if (c.sweep_gt(v->fPoint, edge->fBottom->fPoint)) { - set_bottom(edge, v, activeEdges, c); - } else { - Edge* newEdge = ALLOC_NEW(Edge, (v, edge->fBottom, edge->fWinding), alloc); - insert_edge_below(newEdge, v, c); - insert_edge_above(newEdge, edge->fBottom, c); - set_bottom(edge, v, activeEdges, c); - cleanup_active_edges(edge, activeEdges, c, alloc); - fix_active_state(newEdge, activeEdges, c); - merge_collinear_edges(newEdge, activeEdges, c); - } -} - -void merge_vertices(Vertex* src, Vertex* dst, Vertex** head, Comparator& c, SkChunkAlloc& alloc) { - LOG("found coincident verts at %g, %g; merging %g into %g\n", src->fPoint.fX, src->fPoint.fY, - src->fID, dst->fID); - for (Edge* edge = src->fFirstEdgeAbove; edge;) { - Edge* next = edge->fNextEdgeAbove; - set_bottom(edge, dst, nullptr, c); - edge = next; - } - for (Edge* edge = src->fFirstEdgeBelow; edge;) { - Edge* next = edge->fNextEdgeBelow; - set_top(edge, dst, nullptr, c); - edge = next; - } - remove(src, head, nullptr); -} - -Vertex* check_for_intersection(Edge* edge, Edge* other, EdgeList* activeEdges, Comparator& c, - SkChunkAlloc& alloc) { - SkPoint p; - if (!edge || !other) { - return nullptr; - } - if (edge->intersect(*other, &p)) { - Vertex* v; - LOG("found intersection, pt is %g, %g\n", p.fX, p.fY); - if (p == edge->fTop->fPoint || c.sweep_lt(p, edge->fTop->fPoint)) { - split_edge(other, edge->fTop, activeEdges, c, alloc); - v = edge->fTop; - } else if (p == edge->fBottom->fPoint || c.sweep_gt(p, edge->fBottom->fPoint)) { - split_edge(other, edge->fBottom, activeEdges, c, alloc); - v = edge->fBottom; - } else if (p == other->fTop->fPoint || c.sweep_lt(p, other->fTop->fPoint)) { - split_edge(edge, other->fTop, activeEdges, c, alloc); - v = other->fTop; - } else if (p == other->fBottom->fPoint || c.sweep_gt(p, other->fBottom->fPoint)) { - split_edge(edge, other->fBottom, activeEdges, c, alloc); - v = other->fBottom; - } else { - Vertex* nextV = edge->fTop; - while (c.sweep_lt(p, nextV->fPoint)) { - nextV = nextV->fPrev; - } - while (c.sweep_lt(nextV->fPoint, p)) { - nextV = nextV->fNext; - } - Vertex* prevV = nextV->fPrev; - if (coincident(prevV->fPoint, p)) { - v = prevV; - } else if (coincident(nextV->fPoint, p)) { - v = nextV; - } else { - v = ALLOC_NEW(Vertex, (p), alloc); - LOG("inserting between %g (%g, %g) and %g (%g, %g)\n", - prevV->fID, prevV->fPoint.fX, prevV->fPoint.fY, - nextV->fID, nextV->fPoint.fX, nextV->fPoint.fY); -#if LOGGING_ENABLED - v->fID = (nextV->fID + prevV->fID) * 0.5f; -#endif - v->fPrev = prevV; - v->fNext = nextV; - prevV->fNext = v; - nextV->fPrev = v; - } - split_edge(edge, v, activeEdges, c, alloc); - split_edge(other, v, activeEdges, c, alloc); - } - return v; - } - return nullptr; -} - -void sanitize_contours(Vertex** contours, int contourCnt) { - for (int i = 0; i < contourCnt; ++i) { - SkASSERT(contours[i]); - for (Vertex* v = contours[i];;) { - if (coincident(v->fPrev->fPoint, v->fPoint)) { - LOG("vertex %g,%g coincident; removing\n", v->fPoint.fX, v->fPoint.fY); - if (v->fPrev == v) { - contours[i] = nullptr; - break; - } - v->fPrev->fNext = v->fNext; - v->fNext->fPrev = v->fPrev; - if (contours[i] == v) { - contours[i] = v->fNext; - } - v = v->fPrev; - } else { - v = v->fNext; - if (v == contours[i]) break; - } - } - } -} - -void merge_coincident_vertices(Vertex** vertices, Comparator& c, SkChunkAlloc& alloc) { - for (Vertex* v = (*vertices)->fNext; v != nullptr; v = v->fNext) { - if (c.sweep_lt(v->fPoint, v->fPrev->fPoint)) { - v->fPoint = v->fPrev->fPoint; - } - if (coincident(v->fPrev->fPoint, v->fPoint)) { - merge_vertices(v->fPrev, v, vertices, c, alloc); - } - } -} - -// Stage 2: convert the contours to a mesh of edges connecting the vertices. - -Vertex* build_edges(Vertex** contours, int contourCnt, Comparator& c, SkChunkAlloc& alloc) { - Vertex* vertices = nullptr; - Vertex* prev = nullptr; - for (int i = 0; i < contourCnt; ++i) { - for (Vertex* v = contours[i]; v != nullptr;) { - Vertex* vNext = v->fNext; - Edge* edge = new_edge(v->fPrev, v, alloc, c); - if (edge->fWinding > 0) { - insert_edge_below(edge, v->fPrev, c); - insert_edge_above(edge, v, c); - } else { - insert_edge_below(edge, v, c); - insert_edge_above(edge, v->fPrev, c); - } - merge_collinear_edges(edge, nullptr, c); - if (prev) { - prev->fNext = v; - v->fPrev = prev; - } else { - vertices = v; - } - prev = v; - v = vNext; - if (v == contours[i]) break; - } - } - if (prev) { - prev->fNext = vertices->fPrev = nullptr; - } - return vertices; -} - -// Stage 3: sort the vertices by increasing sweep direction. - -Vertex* sorted_merge(Vertex* a, Vertex* b, Comparator& c); - -void front_back_split(Vertex* v, Vertex** pFront, Vertex** pBack) { - Vertex* fast; - Vertex* slow; - if (!v || !v->fNext) { - *pFront = v; - *pBack = nullptr; - } else { - slow = v; - fast = v->fNext; - - while (fast != nullptr) { - fast = fast->fNext; - if (fast != nullptr) { - slow = slow->fNext; - fast = fast->fNext; - } - } - - *pFront = v; - *pBack = slow->fNext; - slow->fNext->fPrev = nullptr; - slow->fNext = nullptr; - } -} - -void merge_sort(Vertex** head, Comparator& c) { - if (!*head || !(*head)->fNext) { - return; - } - - Vertex* a; - Vertex* b; - front_back_split(*head, &a, &b); - - merge_sort(&a, c); - merge_sort(&b, c); - - *head = sorted_merge(a, b, c); -} - -inline void append_vertex(Vertex* v, Vertex** head, Vertex** tail) { - insert(v, *tail, nullptr, head, tail); -} - -inline void append_vertex_list(Vertex* v, Vertex** head, Vertex** tail) { - insert(v, *tail, v->fNext, head, tail); -} - -Vertex* sorted_merge(Vertex* a, Vertex* b, Comparator& c) { - Vertex* head = nullptr; - Vertex* tail = nullptr; - - while (a && b) { - if (c.sweep_lt(a->fPoint, b->fPoint)) { - Vertex* next = a->fNext; - append_vertex(a, &head, &tail); - a = next; - } else { - Vertex* next = b->fNext; - append_vertex(b, &head, &tail); - b = next; - } - } - if (a) { - append_vertex_list(a, &head, &tail); - } - if (b) { - append_vertex_list(b, &head, &tail); - } - return head; -} - -// Stage 4: Simplify the mesh by inserting new vertices at intersecting edges. - -void simplify(Vertex* vertices, Comparator& c, SkChunkAlloc& alloc) { - LOG("simplifying complex polygons\n"); - EdgeList activeEdges; - for (Vertex* v = vertices; v != nullptr; v = v->fNext) { - if (!v->fFirstEdgeAbove && !v->fFirstEdgeBelow) { - continue; - } -#if LOGGING_ENABLED - LOG("\nvertex %g: (%g,%g)\n", v->fID, v->fPoint.fX, v->fPoint.fY); -#endif - Edge* leftEnclosingEdge = nullptr; - Edge* rightEnclosingEdge = nullptr; - bool restartChecks; - do { - restartChecks = false; - find_enclosing_edges(v, &activeEdges, &leftEnclosingEdge, &rightEnclosingEdge); - if (v->fFirstEdgeBelow) { - for (Edge* edge = v->fFirstEdgeBelow; edge != nullptr; edge = edge->fNextEdgeBelow) { - if (check_for_intersection(edge, leftEnclosingEdge, &activeEdges, c, alloc)) { - restartChecks = true; - break; - } - if (check_for_intersection(edge, rightEnclosingEdge, &activeEdges, c, alloc)) { - restartChecks = true; - break; - } - } - } else { - if (Vertex* pv = check_for_intersection(leftEnclosingEdge, rightEnclosingEdge, - &activeEdges, c, alloc)) { - if (c.sweep_lt(pv->fPoint, v->fPoint)) { - v = pv; - } - restartChecks = true; - } - - } - } while (restartChecks); - for (Edge* e = v->fFirstEdgeAbove; e; e = e->fNextEdgeAbove) { - remove_edge(e, &activeEdges); - } - Edge* leftEdge = leftEnclosingEdge; - for (Edge* e = v->fFirstEdgeBelow; e; e = e->fNextEdgeBelow) { - insert_edge(e, leftEdge, &activeEdges); - leftEdge = e; - } - v->fProcessed = true; - } -} - -// Stage 5: Tessellate the simplified mesh into monotone polygons. - -Poly* tessellate(Vertex* vertices, SkChunkAlloc& alloc) { - LOG("tessellating simple polygons\n"); - EdgeList activeEdges; - Poly* polys = nullptr; - for (Vertex* v = vertices; v != nullptr; v = v->fNext) { - if (!v->fFirstEdgeAbove && !v->fFirstEdgeBelow) { - continue; - } -#if LOGGING_ENABLED - LOG("\nvertex %g: (%g,%g)\n", v->fID, v->fPoint.fX, v->fPoint.fY); -#endif - Edge* leftEnclosingEdge = nullptr; - Edge* rightEnclosingEdge = nullptr; - find_enclosing_edges(v, &activeEdges, &leftEnclosingEdge, &rightEnclosingEdge); - Poly* leftPoly = nullptr; - Poly* rightPoly = nullptr; - if (v->fFirstEdgeAbove) { - leftPoly = v->fFirstEdgeAbove->fLeftPoly; - rightPoly = v->fLastEdgeAbove->fRightPoly; - } else { - leftPoly = leftEnclosingEdge ? leftEnclosingEdge->fRightPoly : nullptr; - rightPoly = rightEnclosingEdge ? rightEnclosingEdge->fLeftPoly : nullptr; - } -#if LOGGING_ENABLED - LOG("edges above:\n"); - for (Edge* e = v->fFirstEdgeAbove; e; e = e->fNextEdgeAbove) { - LOG("%g -> %g, lpoly %d, rpoly %d\n", e->fTop->fID, e->fBottom->fID, - e->fLeftPoly ? e->fLeftPoly->fID : -1, e->fRightPoly ? e->fRightPoly->fID : -1); - } - LOG("edges below:\n"); - for (Edge* e = v->fFirstEdgeBelow; e; e = e->fNextEdgeBelow) { - LOG("%g -> %g, lpoly %d, rpoly %d\n", e->fTop->fID, e->fBottom->fID, - e->fLeftPoly ? e->fLeftPoly->fID : -1, e->fRightPoly ? e->fRightPoly->fID : -1); - } -#endif - if (v->fFirstEdgeAbove) { - if (leftPoly) { - leftPoly = leftPoly->addVertex(v, Poly::kRight_Side, alloc); - } - if (rightPoly) { - rightPoly = rightPoly->addVertex(v, Poly::kLeft_Side, alloc); - } - for (Edge* e = v->fFirstEdgeAbove; e != v->fLastEdgeAbove; e = e->fNextEdgeAbove) { - Edge* leftEdge = e; - Edge* rightEdge = e->fNextEdgeAbove; - SkASSERT(rightEdge->isRightOf(leftEdge->fTop)); - remove_edge(leftEdge, &activeEdges); - if (leftEdge->fRightPoly) { - leftEdge->fRightPoly->end(v, alloc); - } - if (rightEdge->fLeftPoly && rightEdge->fLeftPoly != leftEdge->fRightPoly) { - rightEdge->fLeftPoly->end(v, alloc); - } - } - remove_edge(v->fLastEdgeAbove, &activeEdges); - if (!v->fFirstEdgeBelow) { - if (leftPoly && rightPoly && leftPoly != rightPoly) { - SkASSERT(leftPoly->fPartner == nullptr && rightPoly->fPartner == nullptr); - rightPoly->fPartner = leftPoly; - leftPoly->fPartner = rightPoly; - } - } - } - if (v->fFirstEdgeBelow) { - if (!v->fFirstEdgeAbove) { - if (leftPoly && leftPoly == rightPoly) { - // Split the poly. - if (leftPoly->fActive->fSide == Poly::kLeft_Side) { - leftPoly = new_poly(&polys, leftEnclosingEdge->fTop, leftPoly->fWinding, - alloc); - leftPoly->addVertex(v, Poly::kRight_Side, alloc); - rightPoly->addVertex(v, Poly::kLeft_Side, alloc); - leftEnclosingEdge->fRightPoly = leftPoly; - } else { - rightPoly = new_poly(&polys, rightEnclosingEdge->fTop, rightPoly->fWinding, - alloc); - rightPoly->addVertex(v, Poly::kLeft_Side, alloc); - leftPoly->addVertex(v, Poly::kRight_Side, alloc); - rightEnclosingEdge->fLeftPoly = rightPoly; - } - } else { - if (leftPoly) { - leftPoly = leftPoly->addVertex(v, Poly::kRight_Side, alloc); - } - if (rightPoly) { - rightPoly = rightPoly->addVertex(v, Poly::kLeft_Side, alloc); - } - } - } - Edge* leftEdge = v->fFirstEdgeBelow; - leftEdge->fLeftPoly = leftPoly; - insert_edge(leftEdge, leftEnclosingEdge, &activeEdges); - for (Edge* rightEdge = leftEdge->fNextEdgeBelow; rightEdge; - rightEdge = rightEdge->fNextEdgeBelow) { - insert_edge(rightEdge, leftEdge, &activeEdges); - int winding = leftEdge->fLeftPoly ? leftEdge->fLeftPoly->fWinding : 0; - winding += leftEdge->fWinding; - if (winding != 0) { - Poly* poly = new_poly(&polys, v, winding, alloc); - leftEdge->fRightPoly = rightEdge->fLeftPoly = poly; - } - leftEdge = rightEdge; - } - v->fLastEdgeBelow->fRightPoly = rightPoly; - } -#if LOGGING_ENABLED - LOG("\nactive edges:\n"); - for (Edge* e = activeEdges.fHead; e != nullptr; e = e->fRight) { - LOG("%g -> %g, lpoly %d, rpoly %d\n", e->fTop->fID, e->fBottom->fID, - e->fLeftPoly ? e->fLeftPoly->fID : -1, e->fRightPoly ? e->fRightPoly->fID : -1); - } -#endif - } - return polys; -} - -// This is a driver function which calls stages 2-5 in turn. - -Poly* contours_to_polys(Vertex** contours, int contourCnt, Comparator& c, SkChunkAlloc& alloc) { -#if LOGGING_ENABLED - for (int i = 0; i < contourCnt; ++i) { - Vertex* v = contours[i]; - SkASSERT(v); - LOG("path.moveTo(%20.20g, %20.20g);\n", v->fPoint.fX, v->fPoint.fY); - for (v = v->fNext; v != contours[i]; v = v->fNext) { - LOG("path.lineTo(%20.20g, %20.20g);\n", v->fPoint.fX, v->fPoint.fY); - } - } -#endif - sanitize_contours(contours, contourCnt); - Vertex* vertices = build_edges(contours, contourCnt, c, alloc); - if (!vertices) { - return nullptr; - } - - // Sort vertices in Y (secondarily in X). - merge_sort(&vertices, c); - merge_coincident_vertices(&vertices, c, alloc); -#if LOGGING_ENABLED - for (Vertex* v = vertices; v != nullptr; v = v->fNext) { - static float gID = 0.0f; - v->fID = gID++; - } -#endif - simplify(vertices, c, alloc); - return tessellate(vertices, alloc); -} - -// Stage 6: Triangulate the monotone polygons into a vertex buffer. - -SkPoint* polys_to_triangles(Poly* polys, SkPath::FillType fillType, SkPoint* data) { - SkPoint* d = data; - for (Poly* poly = polys; poly; poly = poly->fNext) { - if (apply_fill_type(fillType, poly->fWinding)) { - d = poly->emit(d); - } - } - return d; -} - struct TessInfo { SkScalar fTolerance; int fCount; }; +// When the SkPathRef genID changes, invalidate a corresponding GrResource described by key. +class PathInvalidator : public SkPathRef::GenIDChangeListener { +public: + explicit PathInvalidator(const GrUniqueKey& key) : fMsg(key) {} +private: + GrUniqueKeyInvalidatedMessage fMsg; + + void onChange() override { + SkMessageBus::Post(fMsg); + } +}; + bool cache_match(GrVertexBuffer* vertexBuffer, SkScalar tol, int* actualCount) { if (!vertexBuffer) { return false; @@ -1354,27 +59,11 @@ bool cache_match(GrVertexBuffer* vertexBuffer, SkScalar tol, int* actualCount) { return false; } -}; +} // namespace GrTessellatingPathRenderer::GrTessellatingPathRenderer() { } -namespace { - -// When the SkPathRef genID changes, invalidate a corresponding GrResource described by key. -class PathInvalidator : public SkPathRef::GenIDChangeListener { -public: - explicit PathInvalidator(const GrUniqueKey& key) : fMsg(key) {} -private: - GrUniqueKeyInvalidatedMessage fMsg; - - void onChange() override { - SkMessageBus::Post(fMsg); - } -}; - -} // namespace - bool GrTessellatingPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const { // This path renderer can draw all fill styles, all stroke styles except hairlines, but does // not do antialiasing. It can do convex and concave paths, but we'll leave the convex ones to @@ -1435,90 +124,23 @@ private: } stroke.setFillStyle(); } - SkRect pathBounds = path.getBounds(); - Comparator c; - if (pathBounds.width() > pathBounds.height()) { - c.sweep_lt = sweep_lt_horiz; - c.sweep_gt = sweep_gt_horiz; - } else { - c.sweep_lt = sweep_lt_vert; - c.sweep_gt = sweep_gt_vert; - } SkScalar screenSpaceTol = GrPathUtils::kDefaultTolerance; + SkRect pathBounds = path.getBounds(); SkScalar tol = GrPathUtils::scaleToleranceToSrc(screenSpaceTol, fViewMatrix, pathBounds); - int contourCnt; - int maxPts = GrPathUtils::worstCasePointCount(path, &contourCnt, tol); - if (maxPts <= 0) { - return 0; - } - if (maxPts > ((int)SK_MaxU16 + 1)) { - SkDebugf("Path not rendered, too many verts (%d)\n", maxPts); - return 0; - } - SkPath::FillType fillType = path.getFillType(); - if (SkPath::IsInverseFillType(fillType)) { - contourCnt++; - } - LOG("got %d pts, %d contours\n", maxPts, contourCnt); - SkAutoTDeleteArray contours(new Vertex* [contourCnt]); - - // For the initial size of the chunk allocator, estimate based on the point count: - // one vertex per point for the initial passes, plus two for the vertices in the - // resulting Polys, since the same point may end up in two Polys. Assume minimal - // connectivity of one Edge per Vertex (will grow for intersections). - SkChunkAlloc alloc(maxPts * (3 * sizeof(Vertex) + sizeof(Edge))); bool isLinear; - path_to_contours(path, tol, fClipBounds, contours.get(), alloc, &isLinear); - Poly* polys; - polys = contours_to_polys(contours.get(), contourCnt, c, alloc); - int count = 0; - for (Poly* poly = polys; poly; poly = poly->fNext) { - if (apply_fill_type(fillType, poly->fWinding) && poly->fCount >= 3) { - count += (poly->fCount - 2) * (WIREFRAME ? 6 : 3); - } - } - if (0 == count) { - return 0; - } - - size_t size = count * sizeof(SkPoint); - if (!vertexBuffer.get() || vertexBuffer->gpuMemorySize() < size) { - vertexBuffer.reset(resourceProvider->createVertexBuffer( - size, GrResourceProvider::kStatic_BufferUsage, 0)); - } - if (!vertexBuffer.get()) { - SkDebugf("Could not allocate vertices\n"); - return 0; - } - SkPoint* verts; - if (canMapVB) { - verts = static_cast(vertexBuffer->map()); - } else { - verts = new SkPoint[count]; - } - SkPoint* end = polys_to_triangles(polys, fillType, verts); - int actualCount = static_cast(end - verts); - LOG("actual count: %d\n", actualCount); - SkASSERT(actualCount <= count); - if (canMapVB) { - vertexBuffer->unmap(); - } else { - vertexBuffer->updateData(verts, actualCount * sizeof(SkPoint)); - delete[] verts; - } - - + int count = GrTessellator::PathToTriangles(path, tol, fClipBounds, resourceProvider, + vertexBuffer, canMapVB, &isLinear); if (!fPath.isVolatile()) { TessInfo info; info.fTolerance = isLinear ? 0 : tol; - info.fCount = actualCount; + info.fCount = count; SkAutoTUnref data(SkData::NewWithCopy(&info, sizeof(info))); key->setCustomData(data.get()); resourceProvider->assignUniqueKeyToResource(*key, vertexBuffer.get()); SkPathPriv::AddGenIDChangeListener(fPath, new PathInvalidator(*key)); } - return actualCount; + return count; } void onPrepareDraws(Target* target) const override { @@ -1574,8 +196,8 @@ private: target->initDraw(gp, this->pipeline()); SkASSERT(gp->getVertexStride() == sizeof(SkPoint)); - GrPrimitiveType primitiveType = WIREFRAME ? kLines_GrPrimitiveType - : kTriangles_GrPrimitiveType; + GrPrimitiveType primitiveType = TESSELLATOR_WIREFRAME ? kLines_GrPrimitiveType + : kTriangles_GrPrimitiveType; GrVertices vertices; vertices.init(primitiveType, vertexBuffer.get(), 0, actualCount); target->draw(vertices); @@ -1627,6 +249,8 @@ private: }; bool GrTessellatingPathRenderer::onDrawPath(const DrawPathArgs& args) { + GR_AUDIT_TRAIL_AUTO_FRAME(args.fTarget->getAuditTrail(), + "GrTessellatingPathRenderer::onDrawPath"); SkASSERT(!args.fAntiAlias); const GrRenderTarget* rt = args.fPipelineBuilder->getRenderTarget(); if (nullptr == rt) { diff --git a/gfx/skia/skia/src/gpu/effects/GrBezierEffect.cpp b/gfx/skia/skia/src/gpu/effects/GrBezierEffect.cpp index 13672da6ea..fdb737dab4 100644 --- a/gfx/skia/skia/src/gpu/effects/GrBezierEffect.cpp +++ b/gfx/skia/skia/src/gpu/effects/GrBezierEffect.cpp @@ -7,11 +7,13 @@ #include "GrBezierEffect.h" +#include "glsl/GrGLSLFragmentShaderBuilder.h" #include "glsl/GrGLSLGeometryProcessor.h" -#include "glsl/GrGLSLProgramBuilder.h" #include "glsl/GrGLSLProgramDataManager.h" +#include "glsl/GrGLSLUniformHandler.h" #include "glsl/GrGLSLUtil.h" #include "glsl/GrGLSLVarying.h" +#include "glsl/GrGLSLVertexShaderBuilder.h" class GrGLConicEffect : public GrGLSLGeometryProcessor { public: @@ -73,63 +75,98 @@ GrGLConicEffect::GrGLConicEffect(const GrGeometryProcessor& processor) } void GrGLConicEffect::onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) { - GrGLSLGPBuilder* pb = args.fPB; GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder; const GrConicEffect& gp = args.fGP.cast(); GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler; + GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; // emit attributes varyingHandler->emitAttributes(gp); GrGLSLVertToFrag v(kVec4f_GrSLType); - varyingHandler->addVarying("ConicCoeffs", &v); + varyingHandler->addVarying("ConicCoeffs", &v, kHigh_GrSLPrecision); vertBuilder->codeAppendf("%s = %s;", v.vsOut(), gp.inConicCoeffs()->fName); GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder; // Setup pass through color if (!gp.colorIgnored()) { - this->setupUniformColor(args.fPB, fragBuilder, args.fOutputColor, &fColorUniform); + this->setupUniformColor(fragBuilder, uniformHandler, args.fOutputColor, &fColorUniform); } // Setup position - this->setupPosition(pb, - vertBuilder, + this->setupPosition(vertBuilder, + uniformHandler, gpArgs, gp.inPosition()->fName, gp.viewMatrix(), &fViewMatrixUniform); // emit transforms with position - this->emitTransforms(pb, - vertBuilder, + this->emitTransforms(vertBuilder, varyingHandler, + uniformHandler, gpArgs->fPositionVar, gp.inPosition()->fName, gp.localMatrix(), args.fTransformsIn, args.fTransformsOut); - fragBuilder->codeAppend("float edgeAlpha;"); + // TODO: this precision check should actually be a check on the number of bits + // high and medium provide and the selection of the lowest level that suffices. + // Additionally we should assert that the upstream code only lets us get here if + // either high or medium provides the required number of bits. + GrSLPrecision precision = kHigh_GrSLPrecision; + const GrShaderCaps::PrecisionInfo& highP = args.fGLSLCaps->getFloatShaderPrecisionInfo( + kFragment_GrShaderType, + kHigh_GrSLPrecision); + if (!highP.supported()) { + precision = kMedium_GrSLPrecision; + } + + GrGLSLShaderVar edgeAlpha("edgeAlpha", kFloat_GrSLType, 0, precision); + GrGLSLShaderVar dklmdx("dklmdx", kVec3f_GrSLType, 0, precision); + GrGLSLShaderVar dklmdy("dklmdy", kVec3f_GrSLType, 0, precision); + GrGLSLShaderVar dfdx("dfdx", kFloat_GrSLType, 0, precision); + GrGLSLShaderVar dfdy("dfdy", kFloat_GrSLType, 0, precision); + GrGLSLShaderVar gF("gF", kVec2f_GrSLType, 0, precision); + GrGLSLShaderVar gFM("gFM", kFloat_GrSLType, 0, precision); + GrGLSLShaderVar func("func", kFloat_GrSLType, 0, precision); + + fragBuilder->declAppend(edgeAlpha); + fragBuilder->declAppend(dklmdx); + fragBuilder->declAppend(dklmdy); + fragBuilder->declAppend(dfdx); + fragBuilder->declAppend(dfdy); + fragBuilder->declAppend(gF); + fragBuilder->declAppend(gFM); + fragBuilder->declAppend(func); switch (fEdgeType) { case kHairlineAA_GrProcessorEdgeType: { SkAssertResult(fragBuilder->enableFeature( GrGLSLFragmentShaderBuilder::kStandardDerivatives_GLSLFeature)); - fragBuilder->codeAppendf("vec3 dklmdx = dFdx(%s.xyz);", v.fsIn()); - fragBuilder->codeAppendf("vec3 dklmdy = dFdy(%s.xyz);", v.fsIn()); - fragBuilder->codeAppendf("float dfdx =" - "2.0 * %s.x * dklmdx.x - %s.y * dklmdx.z - %s.z * dklmdx.y;", - v.fsIn(), v.fsIn(), v.fsIn()); - fragBuilder->codeAppendf("float dfdy =" - "2.0 * %s.x * dklmdy.x - %s.y * dklmdy.z - %s.z * dklmdy.y;", - v.fsIn(), v.fsIn(), v.fsIn()); - fragBuilder->codeAppend("vec2 gF = vec2(dfdx, dfdy);"); - fragBuilder->codeAppend("float gFM = sqrt(dot(gF, gF));"); - fragBuilder->codeAppendf("float func = %s.x*%s.x - %s.y*%s.z;", v.fsIn(), v.fsIn(), - v.fsIn(), v.fsIn()); - fragBuilder->codeAppend("func = abs(func);"); - fragBuilder->codeAppend("edgeAlpha = func / gFM;"); - fragBuilder->codeAppend("edgeAlpha = max(1.0 - edgeAlpha, 0.0);"); + fragBuilder->codeAppendf("%s = dFdx(%s.xyz);", dklmdx.c_str(), v.fsIn()); + fragBuilder->codeAppendf("%s = dFdy(%s.xyz);", dklmdy.c_str(), v.fsIn()); + fragBuilder->codeAppendf("%s = 2.0 * %s.x * %s.x - %s.y * %s.z - %s.z * %s.y;", + dfdx.c_str(), + v.fsIn(), dklmdx.c_str(), + v.fsIn(), dklmdx.c_str(), + v.fsIn(), dklmdx.c_str()); + fragBuilder->codeAppendf("%s = 2.0 * %s.x * %s.x - %s.y * %s.z - %s.z * %s.y;", + dfdy.c_str(), + v.fsIn(), dklmdy.c_str(), + v.fsIn(), dklmdy.c_str(), + v.fsIn(), dklmdy.c_str()); + fragBuilder->codeAppendf("%s = vec2(%s, %s);", gF.c_str(), dfdx.c_str(), dfdy.c_str()); + fragBuilder->codeAppendf("%s = sqrt(dot(%s, %s));", + gFM.c_str(), gF.c_str(), gF.c_str()); + fragBuilder->codeAppendf("%s = %s.x*%s.x - %s.y*%s.z;", + func.c_str(), v.fsIn(), v.fsIn(), v.fsIn(), v.fsIn()); + fragBuilder->codeAppendf("%s = abs(%s);", func.c_str(), func.c_str()); + fragBuilder->codeAppendf("%s = %s / %s;", + edgeAlpha.c_str(), func.c_str(), gFM.c_str()); + fragBuilder->codeAppendf("%s = max(1.0 - %s, 0.0);", + edgeAlpha.c_str(), edgeAlpha.c_str()); // Add line below for smooth cubic ramp // fragBuilder->codeAppend("edgeAlpha = edgeAlpha*edgeAlpha*(3.0-2.0*edgeAlpha);"); break; @@ -137,28 +174,38 @@ void GrGLConicEffect::onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) { case kFillAA_GrProcessorEdgeType: { SkAssertResult(fragBuilder->enableFeature( GrGLSLFragmentShaderBuilder::kStandardDerivatives_GLSLFeature)); - fragBuilder->codeAppendf("vec3 dklmdx = dFdx(%s.xyz);", v.fsIn()); - fragBuilder->codeAppendf("vec3 dklmdy = dFdy(%s.xyz);", v.fsIn()); - fragBuilder->codeAppendf("float dfdx =" - "2.0 * %s.x * dklmdx.x - %s.y * dklmdx.z - %s.z * dklmdx.y;", - v.fsIn(), v.fsIn(), v.fsIn()); - fragBuilder->codeAppendf("float dfdy =" - "2.0 * %s.x * dklmdy.x - %s.y * dklmdy.z - %s.z * dklmdy.y;", - v.fsIn(), v.fsIn(), v.fsIn()); - fragBuilder->codeAppend("vec2 gF = vec2(dfdx, dfdy);"); - fragBuilder->codeAppend("float gFM = sqrt(dot(gF, gF));"); - fragBuilder->codeAppendf("float func = %s.x * %s.x - %s.y * %s.z;", v.fsIn(), v.fsIn(), - v.fsIn(), v.fsIn()); - fragBuilder->codeAppend("edgeAlpha = func / gFM;"); - fragBuilder->codeAppend("edgeAlpha = clamp(1.0 - edgeAlpha, 0.0, 1.0);"); + fragBuilder->codeAppendf("%s = dFdx(%s.xyz);", dklmdx.c_str(), v.fsIn()); + fragBuilder->codeAppendf("%s = dFdy(%s.xyz);", dklmdy.c_str(), v.fsIn()); + fragBuilder->codeAppendf("%s =" + "2.0 * %s.x * %s.x - %s.y * %s.z - %s.z * %s.y;", + dfdx.c_str(), + v.fsIn(), dklmdx.c_str(), + v.fsIn(), dklmdx.c_str(), + v.fsIn(), dklmdx.c_str()); + fragBuilder->codeAppendf("%s =" + "2.0 * %s.x * %s.x - %s.y * %s.z - %s.z * %s.y;", + dfdy.c_str(), + v.fsIn(), dklmdy.c_str(), + v.fsIn(), dklmdy.c_str(), + v.fsIn(), dklmdy.c_str()); + fragBuilder->codeAppendf("%s = vec2(%s, %s);", gF.c_str(), dfdx.c_str(), dfdy.c_str()); + fragBuilder->codeAppendf("%s = sqrt(dot(%s, %s));", + gFM.c_str(), gF.c_str(), gF.c_str()); + fragBuilder->codeAppendf("%s = %s.x * %s.x - %s.y * %s.z;", + func.c_str(), v.fsIn(), v.fsIn(), v.fsIn(), v.fsIn()); + fragBuilder->codeAppendf("%s = %s / %s;", + edgeAlpha.c_str(), func.c_str(), gFM.c_str()); + fragBuilder->codeAppendf("%s = clamp(1.0 - %s, 0.0, 1.0);", + edgeAlpha.c_str(), edgeAlpha.c_str()); // Add line below for smooth cubic ramp // fragBuilder->codeAppend("edgeAlpha = edgeAlpha*edgeAlpha*(3.0-2.0*edgeAlpha);"); break; } case kFillBW_GrProcessorEdgeType: { - fragBuilder->codeAppendf("edgeAlpha = %s.x * %s.x - %s.y * %s.z;", v.fsIn(), v.fsIn(), - v.fsIn(), v.fsIn()); - fragBuilder->codeAppend("edgeAlpha = float(edgeAlpha < 0.0);"); + fragBuilder->codeAppendf("%s = %s.x * %s.x - %s.y * %s.z;", + edgeAlpha.c_str(), v.fsIn(), v.fsIn(), v.fsIn(), v.fsIn()); + fragBuilder->codeAppendf("%s = float(%s < 0.0);", + edgeAlpha.c_str(), edgeAlpha.c_str()); break; } default: @@ -168,14 +215,16 @@ void GrGLConicEffect::onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) { // TODO should we really be doing this? if (gp.coverageScale() != 0xff) { const char* coverageScale; - fCoverageScaleUniform = pb->addUniform(GrGLSLProgramBuilder::kFragment_Visibility, - kFloat_GrSLType, - kDefault_GrSLPrecision, - "Coverage", - &coverageScale); - fragBuilder->codeAppendf("%s = vec4(%s * edgeAlpha);", args.fOutputCoverage, coverageScale); + fCoverageScaleUniform = uniformHandler->addUniform( + GrGLSLUniformHandler::kFragment_Visibility, + kFloat_GrSLType, + kHigh_GrSLPrecision, + "Coverage", + &coverageScale); + fragBuilder->codeAppendf("%s = vec4(%s * %s);", + args.fOutputCoverage, coverageScale, edgeAlpha.c_str()); } else { - fragBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage); + fragBuilder->codeAppendf("%s = vec4(%s);", args.fOutputCoverage, edgeAlpha.c_str()); } } @@ -301,10 +350,10 @@ GrGLQuadEffect::GrGLQuadEffect(const GrGeometryProcessor& processor) } void GrGLQuadEffect::onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) { - GrGLSLGPBuilder* pb = args.fPB; GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder; const GrQuadEffect& gp = args.fGP.cast(); GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler; + GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; // emit attributes varyingHandler->emitAttributes(gp); @@ -316,21 +365,21 @@ void GrGLQuadEffect::onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) { GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder; // Setup pass through color if (!gp.colorIgnored()) { - this->setupUniformColor(args.fPB, fragBuilder, args.fOutputColor, &fColorUniform); + this->setupUniformColor(fragBuilder, uniformHandler, args.fOutputColor, &fColorUniform); } // Setup position - this->setupPosition(pb, - vertBuilder, + this->setupPosition(vertBuilder, + uniformHandler, gpArgs, gp.inPosition()->fName, gp.viewMatrix(), &fViewMatrixUniform); // emit transforms with position - this->emitTransforms(pb, - vertBuilder, + this->emitTransforms(vertBuilder, varyingHandler, + uniformHandler, gpArgs->fPositionVar, gp.inPosition()->fName, gp.localMatrix(), @@ -384,11 +433,12 @@ void GrGLQuadEffect::onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) { if (0xff != gp.coverageScale()) { const char* coverageScale; - fCoverageScaleUniform = pb->addUniform(GrGLSLProgramBuilder::kFragment_Visibility, - kFloat_GrSLType, - kDefault_GrSLPrecision, - "Coverage", - &coverageScale); + fCoverageScaleUniform = uniformHandler->addUniform( + GrGLSLUniformHandler::kFragment_Visibility, + kFloat_GrSLType, + kDefault_GrSLPrecision, + "Coverage", + &coverageScale); fragBuilder->codeAppendf("%s = vec4(%s * edgeAlpha);", args.fOutputCoverage, coverageScale); } else { fragBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage); @@ -507,6 +557,7 @@ void GrGLCubicEffect::onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) { GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder; const GrCubicEffect& gp = args.fGP.cast(); GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler; + GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; // emit attributes varyingHandler->emitAttributes(gp); @@ -518,21 +569,21 @@ void GrGLCubicEffect::onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) { GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder; // Setup pass through color if (!gp.colorIgnored()) { - this->setupUniformColor(args.fPB, fragBuilder, args.fOutputColor, &fColorUniform); + this->setupUniformColor(fragBuilder, uniformHandler, args.fOutputColor, &fColorUniform); } // Setup position - this->setupPosition(args.fPB, - vertBuilder, + this->setupPosition(vertBuilder, + uniformHandler, gpArgs, gp.inPosition()->fName, gp.viewMatrix(), &fViewMatrixUniform); // emit transforms with position - this->emitTransforms(args.fPB, - vertBuilder, + this->emitTransforms(vertBuilder, varyingHandler, + uniformHandler, gpArgs->fPositionVar, gp.inPosition()->fName, args.fTransformsIn, @@ -602,8 +653,8 @@ void GrGLCubicEffect::onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) { fragBuilder->codeAppendf("%s = sqrt(dot(%s, %s));", gFM.c_str(), gF.c_str(), gF.c_str()); fragBuilder->codeAppendf("%s = %s.x * %s.x * %s.x - %s.y * %s.z;", - func.c_str(), v.fsIn(), v.fsIn(), - v.fsIn(), v.fsIn(), v.fsIn()); + func.c_str(), + v.fsIn(), v.fsIn(), v.fsIn(), v.fsIn(), v.fsIn()); fragBuilder->codeAppendf("%s = %s / %s;", edgeAlpha.c_str(), func.c_str(), gFM.c_str()); fragBuilder->codeAppendf("%s = clamp(1.0 - %s, 0.0, 1.0);", diff --git a/gfx/skia/skia/src/gpu/effects/GrBicubicEffect.cpp b/gfx/skia/skia/src/gpu/effects/GrBicubicEffect.cpp index 8efcffdf03..37ac4ab766 100644 --- a/gfx/skia/skia/src/gpu/effects/GrBicubicEffect.cpp +++ b/gfx/skia/skia/src/gpu/effects/GrBicubicEffect.cpp @@ -8,8 +8,8 @@ #include "GrBicubicEffect.h" #include "GrInvariantOutput.h" #include "glsl/GrGLSLFragmentShaderBuilder.h" -#include "glsl/GrGLSLProgramBuilder.h" #include "glsl/GrGLSLProgramDataManager.h" +#include "glsl/GrGLSLUniformHandler.h" #define DS(x) SkDoubleToScalar(x) @@ -52,15 +52,16 @@ GrGLBicubicEffect::GrGLBicubicEffect(const GrProcessor&) { void GrGLBicubicEffect::emitCode(EmitArgs& args) { const GrTextureDomain& domain = args.fFp.cast().domain(); - fCoefficientsUni = args.fBuilder->addUniform(GrGLSLProgramBuilder::kFragment_Visibility, - kMat44f_GrSLType, kDefault_GrSLPrecision, - "Coefficients"); - fImageIncrementUni = args.fBuilder->addUniform(GrGLSLProgramBuilder::kFragment_Visibility, - kVec2f_GrSLType, kDefault_GrSLPrecision, - "ImageIncrement"); + GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; + fCoefficientsUni = uniformHandler->addUniform(GrGLSLUniformHandler::kFragment_Visibility, + kMat44f_GrSLType, kDefault_GrSLPrecision, + "Coefficients"); + fImageIncrementUni = uniformHandler->addUniform(GrGLSLUniformHandler::kFragment_Visibility, + kVec2f_GrSLType, kDefault_GrSLPrecision, + "ImageIncrement"); - const char* imgInc = args.fBuilder->getUniformCStr(fImageIncrementUni); - const char* coeff = args.fBuilder->getUniformCStr(fCoefficientsUni); + const char* imgInc = uniformHandler->getUniformCStr(fImageIncrementUni); + const char* coeff = uniformHandler->getUniformCStr(fCoefficientsUni); SkString cubicBlendName; @@ -98,6 +99,7 @@ void GrGLBicubicEffect::emitCode(EmitArgs& args) { SkString sampleVar; sampleVar.printf("rowColors[%d]", x); fDomain.sampleTexture(fragBuilder, + args.fUniformHandler, args.fGLSLCaps, domain, sampleVar.c_str(), diff --git a/gfx/skia/skia/src/gpu/effects/GrBitmapTextGeoProc.cpp b/gfx/skia/skia/src/gpu/effects/GrBitmapTextGeoProc.cpp index 594646256e..6fd353849e 100644 --- a/gfx/skia/skia/src/gpu/effects/GrBitmapTextGeoProc.cpp +++ b/gfx/skia/skia/src/gpu/effects/GrBitmapTextGeoProc.cpp @@ -10,8 +10,8 @@ #include "GrTexture.h" #include "glsl/GrGLSLFragmentShaderBuilder.h" #include "glsl/GrGLSLGeometryProcessor.h" -#include "glsl/GrGLSLProgramBuilder.h" #include "glsl/GrGLSLProgramDataManager.h" +#include "glsl/GrGLSLUniformHandler.h" #include "glsl/GrGLSLVarying.h" #include "glsl/GrGLSLVertexShaderBuilder.h" @@ -22,9 +22,9 @@ public: void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override { const GrBitmapTextGeoProc& cte = args.fGP.cast(); - GrGLSLGPBuilder* pb = args.fPB; GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder; GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler; + GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; // emit attributes varyingHandler->emitAttributes(cte); @@ -49,17 +49,18 @@ public: if (cte.hasVertexColor()) { varyingHandler->addPassThroughAttribute(cte.inColor(), args.fOutputColor); } else { - this->setupUniformColor(pb, fragBuilder, args.fOutputColor, &fColorUniform); + this->setupUniformColor(fragBuilder, uniformHandler, args.fOutputColor, + &fColorUniform); } } // Setup position - this->setupPosition(pb, vertBuilder, gpArgs, cte.inPosition()->fName); + this->setupPosition(vertBuilder, gpArgs, cte.inPosition()->fName); // emit transforms - this->emitTransforms(args.fPB, - vertBuilder, + this->emitTransforms(vertBuilder, varyingHandler, + uniformHandler, gpArgs->fPositionVar, cte.inPosition()->fName, cte.localMatrix(), @@ -143,9 +144,8 @@ GrBitmapTextGeoProc::GrBitmapTextGeoProc(GrColor color, GrTexture* texture, this->initClassID(); fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType)); - // TODO we could think about removing this attribute if color is ignored, but unfortunately - // we don't do text positioning in batch, so we can't quite do that yet. - bool hasVertexColor = kA8_GrMaskFormat == fMaskFormat; + bool hasVertexColor = kA8_GrMaskFormat == fMaskFormat || + kA565_GrMaskFormat == fMaskFormat; if (hasVertexColor) { fInColor = &this->addVertexAttrib(Attribute("inColor", kVec4ub_GrVertexAttribType)); } diff --git a/gfx/skia/skia/src/gpu/effects/GrConfigConversionEffect.cpp b/gfx/skia/skia/src/gpu/effects/GrConfigConversionEffect.cpp index 4df894b40a..129d057b7f 100644 --- a/gfx/skia/skia/src/gpu/effects/GrConfigConversionEffect.cpp +++ b/gfx/skia/skia/src/gpu/effects/GrConfigConversionEffect.cpp @@ -13,7 +13,6 @@ #include "SkMatrix.h" #include "glsl/GrGLSLFragmentProcessor.h" #include "glsl/GrGLSLFragmentShaderBuilder.h" -#include "glsl/GrGLSLProgramBuilder.h" class GrGLConfigConversionEffect : public GrGLSLFragmentProcessor { public: diff --git a/gfx/skia/skia/src/gpu/effects/GrConstColorProcessor.cpp b/gfx/skia/skia/src/gpu/effects/GrConstColorProcessor.cpp index cf567aac5c..03b0fa4054 100644 --- a/gfx/skia/skia/src/gpu/effects/GrConstColorProcessor.cpp +++ b/gfx/skia/skia/src/gpu/effects/GrConstColorProcessor.cpp @@ -6,10 +6,11 @@ */ #include "effects/GrConstColorProcessor.h" +#include "GrInvariantOutput.h" #include "glsl/GrGLSLFragmentProcessor.h" #include "glsl/GrGLSLFragmentShaderBuilder.h" -#include "glsl/GrGLSLProgramBuilder.h" #include "glsl/GrGLSLProgramDataManager.h" +#include "glsl/GrGLSLUniformHandler.h" class GLConstColorProcessor : public GrGLSLFragmentProcessor { public: @@ -18,9 +19,10 @@ public: void emitCode(EmitArgs& args) override { GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder; const char* colorUni; - fColorUniform = args.fBuilder->addUniform(GrGLSLProgramBuilder::kFragment_Visibility, - kVec4f_GrSLType, kMedium_GrSLPrecision, "constantColor", - &colorUni); + fColorUniform = args.fUniformHandler->addUniform(GrGLSLUniformHandler::kFragment_Visibility, + kVec4f_GrSLType, kMedium_GrSLPrecision, + "constantColor", + &colorUni); GrConstColorProcessor::InputMode mode = args.fFp.cast().inputMode(); if (!args.fInputColor) { mode = GrConstColorProcessor::kIgnore_InputMode; diff --git a/gfx/skia/skia/src/gpu/effects/GrConvexPolyEffect.cpp b/gfx/skia/skia/src/gpu/effects/GrConvexPolyEffect.cpp index 13df7bc4bd..dc7a528501 100644 --- a/gfx/skia/skia/src/gpu/effects/GrConvexPolyEffect.cpp +++ b/gfx/skia/skia/src/gpu/effects/GrConvexPolyEffect.cpp @@ -10,8 +10,8 @@ #include "SkPathPriv.h" #include "glsl/GrGLSLFragmentProcessor.h" #include "glsl/GrGLSLFragmentShaderBuilder.h" -#include "glsl/GrGLSLProgramBuilder.h" #include "glsl/GrGLSLProgramDataManager.h" +#include "glsl/GrGLSLUniformHandler.h" ////////////////////////////////////////////////////////////////////////////// class AARectEffect : public GrFragmentProcessor { @@ -105,11 +105,11 @@ void GLAARectEffect::emitCode(EmitArgs& args) { const char *rectName; // The rect uniform's xyzw refer to (left + 0.5, top + 0.5, right - 0.5, bottom - 0.5), // respectively. - fRectUniform = args.fBuilder->addUniform(GrGLSLProgramBuilder::kFragment_Visibility, - kVec4f_GrSLType, - kDefault_GrSLPrecision, - "rect", - &rectName); + fRectUniform = args.fUniformHandler->addUniform(GrGLSLUniformHandler::kFragment_Visibility, + kVec4f_GrSLType, + kDefault_GrSLPrecision, + "rect", + &rectName); GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder; const char* fragmentPos = fragBuilder->fragmentPosition(); @@ -191,12 +191,12 @@ void GrGLConvexPolyEffect::emitCode(EmitArgs& args) { const GrConvexPolyEffect& cpe = args.fFp.cast(); const char *edgeArrayName; - fEdgeUniform = args.fBuilder->addUniformArray(GrGLSLProgramBuilder::kFragment_Visibility, - kVec3f_GrSLType, - kDefault_GrSLPrecision, - "edges", - cpe.getEdgeCount(), - &edgeArrayName); + fEdgeUniform = args.fUniformHandler->addUniformArray(GrGLSLUniformHandler::kFragment_Visibility, + kVec3f_GrSLType, + kDefault_GrSLPrecision, + "edges", + cpe.getEdgeCount(), + &edgeArrayName); GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder; fragBuilder->codeAppend("\t\tfloat alpha = 1.0;\n"); fragBuilder->codeAppend("\t\tfloat edge;\n"); diff --git a/gfx/skia/skia/src/gpu/effects/GrConvolutionEffect.cpp b/gfx/skia/skia/src/gpu/effects/GrConvolutionEffect.cpp index 8e05f4789b..fb68a29420 100644 --- a/gfx/skia/skia/src/gpu/effects/GrConvolutionEffect.cpp +++ b/gfx/skia/skia/src/gpu/effects/GrConvolutionEffect.cpp @@ -8,8 +8,8 @@ #include "GrConvolutionEffect.h" #include "glsl/GrGLSLFragmentProcessor.h" #include "glsl/GrGLSLFragmentShaderBuilder.h" -#include "glsl/GrGLSLProgramBuilder.h" #include "glsl/GrGLSLProgramDataManager.h" +#include "glsl/GrGLSLUniformHandler.h" // For brevity typedef GrGLSLProgramDataManager::UniformHandle UniformHandle; @@ -48,17 +48,18 @@ GrGLConvolutionEffect::GrGLConvolutionEffect(const GrProcessor& processor) { } void GrGLConvolutionEffect::emitCode(EmitArgs& args) { - fImageIncrementUni = args.fBuilder->addUniform(GrGLSLProgramBuilder::kFragment_Visibility, - kVec2f_GrSLType, kDefault_GrSLPrecision, - "ImageIncrement"); + GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; + fImageIncrementUni = uniformHandler->addUniform(GrGLSLUniformHandler::kFragment_Visibility, + kVec2f_GrSLType, kDefault_GrSLPrecision, + "ImageIncrement"); if (this->useBounds()) { - fBoundsUni = args.fBuilder->addUniform(GrGLSLProgramBuilder::kFragment_Visibility, - kVec2f_GrSLType, kDefault_GrSLPrecision, - "Bounds"); + fBoundsUni = uniformHandler->addUniform(GrGLSLUniformHandler::kFragment_Visibility, + kVec2f_GrSLType, kDefault_GrSLPrecision, + "Bounds"); } - fKernelUni = args.fBuilder->addUniformArray(GrGLSLProgramBuilder::kFragment_Visibility, - kFloat_GrSLType, kDefault_GrSLPrecision, - "Kernel", this->width()); + fKernelUni = uniformHandler->addUniformArray(GrGLSLUniformHandler::kFragment_Visibility, + kFloat_GrSLType, kDefault_GrSLPrecision, + "Kernel", this->width()); GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder; SkString coords2D = fragBuilder->ensureFSCoords2D(args.fCoords, 0); @@ -66,8 +67,8 @@ void GrGLConvolutionEffect::emitCode(EmitArgs& args) { fragBuilder->codeAppendf("\t\t%s = vec4(0, 0, 0, 0);\n", args.fOutputColor); int width = this->width(); - const GrGLSLShaderVar& kernel = args.fBuilder->getUniformVariable(fKernelUni); - const char* imgInc = args.fBuilder->getUniformCStr(fImageIncrementUni); + const GrGLSLShaderVar& kernel = uniformHandler->getUniformVariable(fKernelUni); + const char* imgInc = uniformHandler->getUniformCStr(fImageIncrementUni); fragBuilder->codeAppendf("\t\tvec2 coord = %s - %d.0 * %s;\n", coords2D.c_str(), fRadius, imgInc); @@ -82,7 +83,7 @@ void GrGLConvolutionEffect::emitCode(EmitArgs& args) { // We used to compute a bool indicating whether we're in bounds or not, cast it to a // float, and then mul weight*texture_sample by the float. However, the Adreno 430 seems // to have a bug that caused corruption. - const char* bounds = args.fBuilder->getUniformCStr(fBoundsUni); + const char* bounds = uniformHandler->getUniformCStr(fBoundsUni); const char* component = this->direction() == Gr1DKernelEffect::kY_Direction ? "y" : "x"; fragBuilder->codeAppendf("if (coord.%s >= %s.x && coord.%s <= %s.y) {", component, bounds, component, bounds); diff --git a/gfx/skia/skia/src/gpu/effects/GrCoverageSetOpXP.cpp b/gfx/skia/skia/src/gpu/effects/GrCoverageSetOpXP.cpp index 219ed0b505..7761a6445a 100644 --- a/gfx/skia/skia/src/gpu/effects/GrCoverageSetOpXP.cpp +++ b/gfx/skia/skia/src/gpu/effects/GrCoverageSetOpXP.cpp @@ -12,8 +12,8 @@ #include "GrProcessor.h" #include "GrProcOptInfo.h" #include "glsl/GrGLSLBlend.h" -#include "glsl/GrGLSLProgramBuilder.h" #include "glsl/GrGLSLFragmentShaderBuilder.h" +#include "glsl/GrGLSLUniformHandler.h" #include "glsl/GrGLSLXferProcessor.h" class CoverageSetOpXP : public GrXferProcessor { @@ -200,8 +200,8 @@ public: } private: - void emitBlendCodeForDstRead(GrGLSLXPBuilder* pb, - GrGLSLXPFragmentBuilder* fragBuilder, + void emitBlendCodeForDstRead(GrGLSLXPFragmentBuilder* fragBuilder, + GrGLSLUniformHandler* uniformHandler, const char* srcColor, const char* srcCoverage, const char* dstColor, diff --git a/gfx/skia/skia/src/gpu/effects/GrCustomXfermode.cpp b/gfx/skia/skia/src/gpu/effects/GrCustomXfermode.cpp index d2aff6042c..04ff4a746b 100644 --- a/gfx/skia/skia/src/gpu/effects/GrCustomXfermode.cpp +++ b/gfx/skia/skia/src/gpu/effects/GrCustomXfermode.cpp @@ -20,8 +20,8 @@ #include "glsl/GrGLSLCaps.h" #include "glsl/GrGLSLFragmentProcessor.h" #include "glsl/GrGLSLFragmentShaderBuilder.h" -#include "glsl/GrGLSLProgramBuilder.h" #include "glsl/GrGLSLProgramDataManager.h" +#include "glsl/GrGLSLUniformHandler.h" #include "glsl/GrGLSLXferProcessor.h" bool GrCustomXfermode::IsSupportedMode(SkXfermode::Mode mode) { @@ -158,8 +158,8 @@ private: } } - void emitBlendCodeForDstRead(GrGLSLXPBuilder* pb, - GrGLSLXPFragmentBuilder* fragBuilder, + void emitBlendCodeForDstRead(GrGLSLXPFragmentBuilder* fragBuilder, + GrGLSLUniformHandler* uniformHandler, const char* srcColor, const char* srcCoverage, const char* dstColor, diff --git a/gfx/skia/skia/src/gpu/effects/GrDashingEffect.cpp b/gfx/skia/skia/src/gpu/effects/GrDashingEffect.cpp index e62ede0a33..1516efd2b2 100644 --- a/gfx/skia/skia/src/gpu/effects/GrDashingEffect.cpp +++ b/gfx/skia/skia/src/gpu/effects/GrDashingEffect.cpp @@ -23,8 +23,8 @@ #include "batches/GrVertexBatch.h" #include "glsl/GrGLSLFragmentShaderBuilder.h" #include "glsl/GrGLSLGeometryProcessor.h" -#include "glsl/GrGLSLProgramBuilder.h" #include "glsl/GrGLSLProgramDataManager.h" +#include "glsl/GrGLSLUniformHandler.h" #include "glsl/GrGLSLVarying.h" #include "glsl/GrGLSLVertexShaderBuilder.h" @@ -863,9 +863,9 @@ GLDashingCircleEffect::GLDashingCircleEffect() { void GLDashingCircleEffect::onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) { const DashingCircleEffect& dce = args.fGP.cast(); - GrGLSLGPBuilder* pb = args.fPB; GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder; GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler; + GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; // emit attributes varyingHandler->emitAttributes(dce); @@ -883,16 +883,16 @@ void GLDashingCircleEffect::onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) { GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder; // Setup pass through color if (!dce.colorIgnored()) { - this->setupUniformColor(pb, fragBuilder, args.fOutputColor, &fColorUniform); + this->setupUniformColor(fragBuilder, uniformHandler, args.fOutputColor, &fColorUniform); } // Setup position - this->setupPosition(pb, vertBuilder, gpArgs, dce.inPosition()->fName); + this->setupPosition(vertBuilder, gpArgs, dce.inPosition()->fName); // emit transforms - this->emitTransforms(args.fPB, - vertBuilder, + this->emitTransforms(vertBuilder, varyingHandler, + uniformHandler, gpArgs->fPositionVar, dce.inPosition()->fName, dce.localMatrix(), @@ -1075,10 +1075,10 @@ GLDashingLineEffect::GLDashingLineEffect() { void GLDashingLineEffect::onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) { const DashingLineEffect& de = args.fGP.cast(); - GrGLSLGPBuilder* pb = args.fPB; GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder; GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler; + GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; // emit attributes varyingHandler->emitAttributes(de); @@ -1097,16 +1097,16 @@ void GLDashingLineEffect::onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) { GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder; // Setup pass through color if (!de.colorIgnored()) { - this->setupUniformColor(pb, fragBuilder, args.fOutputColor, &fColorUniform); + this->setupUniformColor(fragBuilder, uniformHandler, args.fOutputColor, &fColorUniform); } // Setup position - this->setupPosition(pb, vertBuilder, gpArgs, de.inPosition()->fName); + this->setupPosition(vertBuilder, gpArgs, de.inPosition()->fName); // emit transforms - this->emitTransforms(args.fPB, - vertBuilder, + this->emitTransforms(vertBuilder, varyingHandler, + uniformHandler, gpArgs->fPositionVar, de.inPosition()->fName, de.localMatrix(), diff --git a/gfx/skia/skia/src/gpu/effects/GrDisableColorXP.cpp b/gfx/skia/skia/src/gpu/effects/GrDisableColorXP.cpp index e669f8b024..70e40e4a14 100644 --- a/gfx/skia/skia/src/gpu/effects/GrDisableColorXP.cpp +++ b/gfx/skia/skia/src/gpu/effects/GrDisableColorXP.cpp @@ -8,7 +8,6 @@ #include "effects/GrDisableColorXP.h" #include "GrProcessor.h" #include "glsl/GrGLSLFragmentShaderBuilder.h" -#include "glsl/GrGLSLProgramBuilder.h" #include "glsl/GrGLSLProgramDataManager.h" #include "glsl/GrGLSLXferProcessor.h" diff --git a/gfx/skia/skia/src/gpu/effects/GrDistanceFieldGeoProc.cpp b/gfx/skia/skia/src/gpu/effects/GrDistanceFieldGeoProc.cpp index cf77157e0d..7a63a62dec 100644 --- a/gfx/skia/skia/src/gpu/effects/GrDistanceFieldGeoProc.cpp +++ b/gfx/skia/skia/src/gpu/effects/GrDistanceFieldGeoProc.cpp @@ -13,8 +13,8 @@ #include "glsl/GrGLSLFragmentShaderBuilder.h" #include "glsl/GrGLSLGeometryProcessor.h" -#include "glsl/GrGLSLProgramBuilder.h" #include "glsl/GrGLSLProgramDataManager.h" +#include "glsl/GrGLSLUniformHandler.h" #include "glsl/GrGLSLUtil.h" #include "glsl/GrGLSLVarying.h" #include "glsl/GrGLSLVertexShaderBuilder.h" @@ -26,7 +26,6 @@ class GrGLDistanceFieldA8TextGeoProc : public GrGLSLGeometryProcessor { public: GrGLDistanceFieldA8TextGeoProc() : fViewMatrix(SkMatrix::InvalidMatrix()) - , fColor(GrColor_ILLEGAL) #ifdef SK_GAMMA_APPLY_TO_A8 , fDistanceAdjust(-1.0f) #endif @@ -35,13 +34,13 @@ public: void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{ const GrDistanceFieldA8TextGeoProc& dfTexEffect = args.fGP.cast(); - GrGLSLGPBuilder* pb = args.fPB; GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder; SkAssertResult(fragBuilder->enableFeature( GrGLSLFragmentShaderBuilder::kStandardDerivatives_GLSLFeature)); GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder; GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler; + GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; // emit attributes varyingHandler->emitAttributes(dfTexEffect); @@ -50,32 +49,28 @@ public: // adjust based on gamma const char* distanceAdjustUniName = nullptr; // width, height, 1/(3*width) - fDistanceAdjustUni = pb->addUniform(GrGLSLProgramBuilder::kFragment_Visibility, - kFloat_GrSLType, kDefault_GrSLPrecision, - "DistanceAdjust", &distanceAdjustUniName); + fDistanceAdjustUni = uniformHandler->addUniform(GrGLSLUniformHandler::kFragment_Visibility, + kFloat_GrSLType, kDefault_GrSLPrecision, + "DistanceAdjust", &distanceAdjustUniName); #endif // Setup pass through color if (!dfTexEffect.colorIgnored()) { - if (dfTexEffect.hasVertexColor()) { - varyingHandler->addPassThroughAttribute(dfTexEffect.inColor(), args.fOutputColor); - } else { - this->setupUniformColor(pb, fragBuilder, args.fOutputColor, &fColorUniform); - } + varyingHandler->addPassThroughAttribute(dfTexEffect.inColor(), args.fOutputColor); } // Setup position - this->setupPosition(pb, - vertBuilder, + this->setupPosition(vertBuilder, + uniformHandler, gpArgs, dfTexEffect.inPosition()->fName, dfTexEffect.viewMatrix(), &fViewMatrixUniform); // emit transforms - this->emitTransforms(pb, - vertBuilder, + this->emitTransforms(vertBuilder, varyingHandler, + uniformHandler, gpArgs->fPositionVar, dfTexEffect.inPosition()->fName, args.fTransformsIn, @@ -173,13 +168,6 @@ public: GrGLSLGetMatrix<3>(viewMatrix, fViewMatrix); pdman.setMatrix3f(fViewMatrixUniform, viewMatrix); } - - if (dfa8gp.color() != fColor && !dfa8gp.hasVertexColor()) { - float c[4]; - GrColorToRGBAFloat(dfa8gp.color(), c); - pdman.set4fv(fColorUniform, 1, c); - fColor = dfa8gp.color(); - } } static inline void GenKey(const GrGeometryProcessor& gp, @@ -187,8 +175,7 @@ public: GrProcessorKeyBuilder* b) { const GrDistanceFieldA8TextGeoProc& dfTexEffect = gp.cast(); uint32_t key = dfTexEffect.getFlags(); - key |= dfTexEffect.hasVertexColor() << 16; - key |= dfTexEffect.colorIgnored() << 17; + key |= dfTexEffect.colorIgnored() << 16; key |= ComputePosKey(dfTexEffect.viewMatrix()) << 25; b->add32(key); @@ -202,8 +189,6 @@ public: private: SkMatrix fViewMatrix; - GrColor fColor; - UniformHandle fColorUniform; UniformHandle fViewMatrixUniform; #ifdef SK_GAMMA_APPLY_TO_A8 float fDistanceAdjust; @@ -237,9 +222,7 @@ GrDistanceFieldA8TextGeoProc::GrDistanceFieldA8TextGeoProc(GrColor color, this->initClassID(); fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType, kHigh_GrSLPrecision)); - if (flags & kColorAttr_DistanceFieldEffectFlag) { - fInColor = &this->addVertexAttrib(Attribute("inColor", kVec4ub_GrVertexAttribType)); - } + fInColor = &this->addVertexAttrib(Attribute("inColor", kVec4ub_GrVertexAttribType)); fInTextureCoords = &this->addVertexAttrib(Attribute("inTextureCoords", kVec2s_GrVertexAttribType)); this->addTextureAccess(&fTextureAccess); @@ -290,19 +273,18 @@ class GrGLDistanceFieldPathGeoProc : public GrGLSLGeometryProcessor { public: GrGLDistanceFieldPathGeoProc() : fViewMatrix(SkMatrix::InvalidMatrix()) - , fColor(GrColor_ILLEGAL) , fTextureSize(SkISize::Make(-1, -1)) {} void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{ const GrDistanceFieldPathGeoProc& dfTexEffect = args.fGP.cast(); - GrGLSLGPBuilder* pb = args.fPB; GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder; SkAssertResult(fragBuilder->enableFeature( GrGLSLFragmentShaderBuilder::kStandardDerivatives_GLSLFeature)); GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder; GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler; + GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; // emit attributes varyingHandler->emitAttributes(dfTexEffect); @@ -312,35 +294,31 @@ public: // setup pass through color if (!dfTexEffect.colorIgnored()) { - if (dfTexEffect.hasVertexColor()) { - varyingHandler->addPassThroughAttribute(dfTexEffect.inColor(), args.fOutputColor); - } else { - this->setupUniformColor(pb, fragBuilder, args.fOutputColor, &fColorUniform); - } + varyingHandler->addPassThroughAttribute(dfTexEffect.inColor(), args.fOutputColor); } vertBuilder->codeAppendf("%s = %s;", v.vsOut(), dfTexEffect.inTextureCoords()->fName); // Setup position - this->setupPosition(pb, - vertBuilder, + this->setupPosition(vertBuilder, + uniformHandler, gpArgs, dfTexEffect.inPosition()->fName, dfTexEffect.viewMatrix(), &fViewMatrixUniform); // emit transforms - this->emitTransforms(pb, - vertBuilder, + this->emitTransforms(vertBuilder, varyingHandler, + uniformHandler, gpArgs->fPositionVar, dfTexEffect.inPosition()->fName, args.fTransformsIn, args.fTransformsOut); const char* textureSizeUniName = nullptr; - fTextureSizeUni = pb->addUniform(GrGLSLProgramBuilder::kFragment_Visibility, - kVec2f_GrSLType, kDefault_GrSLPrecision, - "TextureSize", &textureSizeUniName); + fTextureSizeUni = uniformHandler->addUniform(GrGLSLUniformHandler::kFragment_Visibility, + kVec2f_GrSLType, kDefault_GrSLPrecision, + "TextureSize", &textureSizeUniName); // Use highp to work around aliasing issues fragBuilder->codeAppend(GrGLSLShaderVar::PrecisionString(args.fGLSLCaps, @@ -413,13 +391,6 @@ public: GrGLSLGetMatrix<3>(viewMatrix, fViewMatrix); pdman.setMatrix3f(fViewMatrixUniform, viewMatrix); } - - if (dfpgp.color() != fColor) { - float c[4]; - GrColorToRGBAFloat(dfpgp.color(), c); - pdman.set4fv(fColorUniform, 1, c); - fColor = dfpgp.color(); - } } static inline void GenKey(const GrGeometryProcessor& gp, @@ -429,17 +400,14 @@ public: uint32_t key = dfTexEffect.getFlags(); key |= dfTexEffect.colorIgnored() << 16; - key |= dfTexEffect.hasVertexColor() << 17; key |= ComputePosKey(dfTexEffect.viewMatrix()) << 25; b->add32(key); } private: - UniformHandle fColorUniform; UniformHandle fTextureSizeUni; UniformHandle fViewMatrixUniform; SkMatrix fViewMatrix; - GrColor fColor; SkISize fTextureSize; typedef GrGLSLGeometryProcessor INHERITED; @@ -464,9 +432,7 @@ GrDistanceFieldPathGeoProc::GrDistanceFieldPathGeoProc( this->initClassID(); fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType, kHigh_GrSLPrecision)); - if (flags & kColorAttr_DistanceFieldEffectFlag) { - fInColor = &this->addVertexAttrib(Attribute("inColor", kVec4ub_GrVertexAttribType)); - } + fInColor = &this->addVertexAttrib(Attribute("inColor", kVec4ub_GrVertexAttribType)); fInTextureCoords = &this->addVertexAttrib(Attribute("inTextureCoords", kVec2f_GrVertexAttribType)); this->addTextureAccess(&fTextureAccess); @@ -514,17 +480,17 @@ const GrGeometryProcessor* GrDistanceFieldPathGeoProc::TestCreate(GrProcessorTes class GrGLDistanceFieldLCDTextGeoProc : public GrGLSLGeometryProcessor { public: GrGLDistanceFieldLCDTextGeoProc() - : fViewMatrix(SkMatrix::InvalidMatrix()), fColor(GrColor_ILLEGAL) { + : fViewMatrix(SkMatrix::InvalidMatrix()) { fDistanceAdjust = GrDistanceFieldLCDTextGeoProc::DistanceAdjust::Make(1.0f, 1.0f, 1.0f); } void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{ const GrDistanceFieldLCDTextGeoProc& dfTexEffect = args.fGP.cast(); - GrGLSLGPBuilder* pb = args.fPB; GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder; GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler; + GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; // emit attributes varyingHandler->emitAttributes(dfTexEffect); @@ -533,21 +499,21 @@ public: // setup pass through color if (!dfTexEffect.colorIgnored()) { - this->setupUniformColor(pb, fragBuilder, args.fOutputColor, &fColorUniform); + varyingHandler->addPassThroughAttribute(dfTexEffect.inColor(), args.fOutputColor); } // Setup position - this->setupPosition(pb, - vertBuilder, + this->setupPosition(vertBuilder, + uniformHandler, gpArgs, dfTexEffect.inPosition()->fName, dfTexEffect.viewMatrix(), &fViewMatrixUniform); // emit transforms - this->emitTransforms(pb, - vertBuilder, + this->emitTransforms(vertBuilder, varyingHandler, + uniformHandler, gpArgs->fPositionVar, dfTexEffect.inPosition()->fName, args.fTransformsIn, @@ -628,9 +594,9 @@ public: // adjust width based on gamma const char* distanceAdjustUniName = nullptr; - fDistanceAdjustUni = pb->addUniform(GrGLSLProgramBuilder::kFragment_Visibility, - kVec3f_GrSLType, kDefault_GrSLPrecision, - "DistanceAdjust", &distanceAdjustUniName); + fDistanceAdjustUni = uniformHandler->addUniform(GrGLSLUniformHandler::kFragment_Visibility, + kVec3f_GrSLType, kDefault_GrSLPrecision, + "DistanceAdjust", &distanceAdjustUniName); fragBuilder->codeAppendf("distance -= %s;", distanceAdjustUniName); // To be strictly correct, we should compute the anti-aliasing factor separately @@ -693,13 +659,6 @@ public: GrGLSLGetMatrix<3>(viewMatrix, fViewMatrix); pdman.setMatrix3f(fViewMatrixUniform, viewMatrix); } - - if (dflcd.color() != fColor) { - float c[4]; - GrColorToRGBAFloat(dflcd.color(), c); - pdman.set4fv(fColorUniform, 1, c); - fColor = dflcd.color(); - } } static inline void GenKey(const GrGeometryProcessor& gp, @@ -722,7 +681,6 @@ public: private: SkMatrix fViewMatrix; - GrColor fColor; UniformHandle fViewMatrixUniform; UniformHandle fColorUniform; GrDistanceFieldLCDTextGeoProc::DistanceAdjust fDistanceAdjust; @@ -748,6 +706,7 @@ GrDistanceFieldLCDTextGeoProc::GrDistanceFieldLCDTextGeoProc( this->initClassID(); fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType, kHigh_GrSLPrecision)); + fInColor = &this->addVertexAttrib(Attribute("inColor", kVec4ub_GrVertexAttribType)); fInTextureCoords = &this->addVertexAttrib(Attribute("inTextureCoords", kVec2s_GrVertexAttribType)); this->addTextureAccess(&fTextureAccess); diff --git a/gfx/skia/skia/src/gpu/effects/GrDistanceFieldGeoProc.h b/gfx/skia/skia/src/gpu/effects/GrDistanceFieldGeoProc.h index e17a0ce781..46d39f0123 100644 --- a/gfx/skia/skia/src/gpu/effects/GrDistanceFieldGeoProc.h +++ b/gfx/skia/skia/src/gpu/effects/GrDistanceFieldGeoProc.h @@ -22,15 +22,13 @@ enum GrDistanceFieldEffectFlags { kUseLCD_DistanceFieldEffectFlag = 0x04, // use lcd text kBGR_DistanceFieldEffectFlag = 0x08, // lcd display has bgr order kPortrait_DistanceFieldEffectFlag = 0x10, // lcd display is in portrait mode (not used yet) - kColorAttr_DistanceFieldEffectFlag = 0x20, // color vertex attribute kInvalid_DistanceFieldEffectFlag = 0x80, // invalid state (for initialization) kUniformScale_DistanceFieldEffectMask = kSimilarity_DistanceFieldEffectFlag | kRectToRect_DistanceFieldEffectFlag, // The subset of the flags relevant to GrDistanceFieldA8TextGeoProc - kNonLCD_DistanceFieldEffectMask = kSimilarity_DistanceFieldEffectFlag | - kColorAttr_DistanceFieldEffectFlag, + kNonLCD_DistanceFieldEffectMask = kSimilarity_DistanceFieldEffectFlag, // The subset of the flags relevant to GrDistanceFieldLCDTextGeoProc kLCD_DistanceFieldEffectMask = kSimilarity_DistanceFieldEffectFlag | kRectToRect_DistanceFieldEffectFlag | @@ -71,7 +69,6 @@ public: const Attribute* inTextureCoords() const { return fInTextureCoords; } GrColor color() const { return fColor; } bool colorIgnored() const { return GrColor_ILLEGAL == fColor; } - bool hasVertexColor() const { return SkToBool(fInColor); } const SkMatrix& viewMatrix() const { return fViewMatrix; } bool usesLocalCoords() const { return fUsesLocalCoords; } #ifdef SK_GAMMA_APPLY_TO_A8 @@ -133,7 +130,6 @@ public: const Attribute* inTextureCoords() const { return fInTextureCoords; } GrColor color() const { return fColor; } bool colorIgnored() const { return GrColor_ILLEGAL == fColor; } - bool hasVertexColor() const { return SkToBool(fInColor); } const SkMatrix& viewMatrix() const { return fViewMatrix; } uint32_t getFlags() const { return fFlags; } bool usesLocalCoords() const { return fUsesLocalCoords; } @@ -197,6 +193,7 @@ public: const char* name() const override { return "DistanceFieldLCDTexture"; } const Attribute* inPosition() const { return fInPosition; } + const Attribute* inColor() const { return fInColor; } const Attribute* inTextureCoords() const { return fInTextureCoords; } DistanceAdjust getDistanceAdjust() const { return fDistanceAdjust; } GrColor color() const { return fColor; } @@ -221,6 +218,7 @@ private: DistanceAdjust fDistanceAdjust; uint32_t fFlags; const Attribute* fInPosition; + const Attribute* fInColor; const Attribute* fInTextureCoords; bool fUsesLocalCoords; diff --git a/gfx/skia/skia/src/gpu/effects/GrDitherEffect.cpp b/gfx/skia/skia/src/gpu/effects/GrDitherEffect.cpp index 37cc1ae570..44aacfccab 100644 --- a/gfx/skia/skia/src/gpu/effects/GrDitherEffect.cpp +++ b/gfx/skia/skia/src/gpu/effects/GrDitherEffect.cpp @@ -11,7 +11,6 @@ #include "SkRect.h" #include "glsl/GrGLSLFragmentProcessor.h" #include "glsl/GrGLSLFragmentShaderBuilder.h" -#include "glsl/GrGLSLProgramBuilder.h" ////////////////////////////////////////////////////////////////////////////// diff --git a/gfx/skia/skia/src/gpu/effects/GrMatrixConvolutionEffect.cpp b/gfx/skia/skia/src/gpu/effects/GrMatrixConvolutionEffect.cpp index 5fb694b0d7..8867ab9dba 100644 --- a/gfx/skia/skia/src/gpu/effects/GrMatrixConvolutionEffect.cpp +++ b/gfx/skia/skia/src/gpu/effects/GrMatrixConvolutionEffect.cpp @@ -6,8 +6,9 @@ */ #include "GrMatrixConvolutionEffect.h" #include "glsl/GrGLSLFragmentProcessor.h" -#include "glsl/GrGLSLProgramBuilder.h" +#include "glsl/GrGLSLFragmentShaderBuilder.h" #include "glsl/GrGLSLProgramDataManager.h" +#include "glsl/GrGLSLUniformHandler.h" class GrGLMatrixConvolutionEffect : public GrGLSLFragmentProcessor { public: @@ -42,25 +43,27 @@ GrGLMatrixConvolutionEffect::GrGLMatrixConvolutionEffect(const GrProcessor& proc void GrGLMatrixConvolutionEffect::emitCode(EmitArgs& args) { const GrTextureDomain& domain = args.fFp.cast().domain(); - fImageIncrementUni = args.fBuilder->addUniform(GrGLSLProgramBuilder::kFragment_Visibility, - kVec2f_GrSLType, kDefault_GrSLPrecision, - "ImageIncrement"); - fKernelUni = args.fBuilder->addUniformArray(GrGLSLProgramBuilder::kFragment_Visibility, - kFloat_GrSLType, kDefault_GrSLPrecision, - "Kernel", - fKernelSize.width() * fKernelSize.height()); - fKernelOffsetUni = args.fBuilder->addUniform(GrGLSLProgramBuilder::kFragment_Visibility, - kVec2f_GrSLType, kDefault_GrSLPrecision, "KernelOffset"); - fGainUni = args.fBuilder->addUniform(GrGLSLProgramBuilder::kFragment_Visibility, - kFloat_GrSLType, kDefault_GrSLPrecision, "Gain"); - fBiasUni = args.fBuilder->addUniform(GrGLSLProgramBuilder::kFragment_Visibility, - kFloat_GrSLType, kDefault_GrSLPrecision, "Bias"); + GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; + fImageIncrementUni = uniformHandler->addUniform(GrGLSLUniformHandler::kFragment_Visibility, + kVec2f_GrSLType, kDefault_GrSLPrecision, + "ImageIncrement"); + fKernelUni = uniformHandler->addUniformArray(GrGLSLUniformHandler::kFragment_Visibility, + kFloat_GrSLType, kDefault_GrSLPrecision, + "Kernel", + fKernelSize.width() * fKernelSize.height()); + fKernelOffsetUni = uniformHandler->addUniform(GrGLSLUniformHandler::kFragment_Visibility, + kVec2f_GrSLType, kDefault_GrSLPrecision, + "KernelOffset"); + fGainUni = uniformHandler->addUniform(GrGLSLUniformHandler::kFragment_Visibility, + kFloat_GrSLType, kDefault_GrSLPrecision, "Gain"); + fBiasUni = uniformHandler->addUniform(GrGLSLUniformHandler::kFragment_Visibility, + kFloat_GrSLType, kDefault_GrSLPrecision, "Bias"); - const char* kernelOffset = args.fBuilder->getUniformCStr(fKernelOffsetUni); - const char* imgInc = args.fBuilder->getUniformCStr(fImageIncrementUni); - const char* kernel = args.fBuilder->getUniformCStr(fKernelUni); - const char* gain = args.fBuilder->getUniformCStr(fGainUni); - const char* bias = args.fBuilder->getUniformCStr(fBiasUni); + const char* kernelOffset = uniformHandler->getUniformCStr(fKernelOffsetUni); + const char* imgInc = uniformHandler->getUniformCStr(fImageIncrementUni); + const char* kernel = uniformHandler->getUniformCStr(fKernelUni); + const char* gain = uniformHandler->getUniformCStr(fGainUni); + const char* bias = uniformHandler->getUniformCStr(fBiasUni); int kWidth = fKernelSize.width(); int kHeight = fKernelSize.height(); @@ -77,6 +80,7 @@ void GrGLMatrixConvolutionEffect::emitCode(EmitArgs& args) { SkString coord; coord.printf("coord + vec2(%d, %d) * %s", x, y, imgInc); fDomain.sampleTexture(fragBuilder, + uniformHandler, args.fGLSLCaps, domain, "c", @@ -95,6 +99,7 @@ void GrGLMatrixConvolutionEffect::emitCode(EmitArgs& args) { args.fOutputColor, args.fOutputColor, args.fOutputColor); } else { fDomain.sampleTexture(fragBuilder, + uniformHandler, args.fGLSLCaps, domain, "c", diff --git a/gfx/skia/skia/src/gpu/effects/GrOvalEffect.cpp b/gfx/skia/skia/src/gpu/effects/GrOvalEffect.cpp index 94c5acd949..c7e685f902 100644 --- a/gfx/skia/skia/src/gpu/effects/GrOvalEffect.cpp +++ b/gfx/skia/skia/src/gpu/effects/GrOvalEffect.cpp @@ -12,8 +12,8 @@ #include "SkRect.h" #include "glsl/GrGLSLFragmentProcessor.h" #include "glsl/GrGLSLFragmentShaderBuilder.h" -#include "glsl/GrGLSLProgramBuilder.h" #include "glsl/GrGLSLProgramDataManager.h" +#include "glsl/GrGLSLUniformHandler.h" ////////////////////////////////////////////////////////////////////////////// @@ -119,10 +119,10 @@ void GLCircleEffect::emitCode(EmitArgs& args) { const char *circleName; // The circle uniform is (center.x, center.y, radius + 0.5, 1 / (radius + 0.5)) for regular // fills and (..., radius - 0.5, 1 / (radius - 0.5)) for inverse fills. - fCircleUniform = args.fBuilder->addUniform(GrGLSLProgramBuilder::kFragment_Visibility, - kVec4f_GrSLType, kDefault_GrSLPrecision, - "circle", - &circleName); + fCircleUniform = args.fUniformHandler->addUniform(GrGLSLUniformHandler::kFragment_Visibility, + kVec4f_GrSLType, kDefault_GrSLPrecision, + "circle", + &circleName); GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder; const char* fragmentPos = fragBuilder->fragmentPosition(); @@ -133,19 +133,19 @@ void GLCircleEffect::emitCode(EmitArgs& args) { // mediump. It'd be nice to only to this on mediump devices but we currently don't have the // caps here. if (GrProcessorEdgeTypeIsInverseFill(ce.getEdgeType())) { - fragBuilder->codeAppendf("\t\tfloat d = (length((%s.xy - %s.xy) * %s.w) - 1.0) * %s.z;\n", + fragBuilder->codeAppendf("float d = (length((%s.xy - %s.xy) * %s.w) - 1.0) * %s.z;", circleName, fragmentPos, circleName, circleName); } else { - fragBuilder->codeAppendf("\t\tfloat d = (1.0 - length((%s.xy - %s.xy) * %s.w)) * %s.z;\n", + fragBuilder->codeAppendf("float d = (1.0 - length((%s.xy - %s.xy) * %s.w)) * %s.z;", circleName, fragmentPos, circleName, circleName); } if (GrProcessorEdgeTypeIsAA(ce.getEdgeType())) { - fragBuilder->codeAppend("\t\td = clamp(d, 0.0, 1.0);\n"); + fragBuilder->codeAppend("d = clamp(d, 0.0, 1.0);"); } else { - fragBuilder->codeAppend("\t\td = d > 0.5 ? 1.0 : 0.0;\n"); + fragBuilder->codeAppend("d = d > 0.5 ? 1.0 : 0.0;"); } - fragBuilder->codeAppendf("\t\t%s = %s;\n", args.fOutputColor, + fragBuilder->codeAppendf("%s = %s;", args.fOutputColor, (GrGLSLExpr4(args.fInputColor) * GrGLSLExpr1("d")).c_str()); } @@ -276,6 +276,7 @@ protected: private: GrGLSLProgramDataManager::UniformHandle fEllipseUniform; + GrGLSLProgramDataManager::UniformHandle fScaleUniform; SkPoint fPrevCenter; SkVector fPrevRadii; @@ -291,43 +292,59 @@ void GLEllipseEffect::emitCode(EmitArgs& args) { const char *ellipseName; // The ellipse uniform is (center.x, center.y, 1 / rx^2, 1 / ry^2) // The last two terms can underflow on mediump, so we use highp. - fEllipseUniform = args.fBuilder->addUniform(GrGLSLProgramBuilder::kFragment_Visibility, - kVec4f_GrSLType, kHigh_GrSLPrecision, - "ellipse", - &ellipseName); + fEllipseUniform = args.fUniformHandler->addUniform(GrGLSLUniformHandler::kFragment_Visibility, + kVec4f_GrSLType, kHigh_GrSLPrecision, + "ellipse", + &ellipseName); + // If we're on a device with a "real" mediump then we'll do the distance computation in a space + // that is normalized by the larger radius. The scale uniform will be scale, 1/scale. The + // inverse squared radii uniform values are already in this normalized space. The center is + // not. + const char* scaleName = nullptr; + if (args.fGLSLCaps->floatPrecisionVaries()) { + fScaleUniform = args.fUniformHandler->addUniform( + GrGLSLUniformHandler::kFragment_Visibility, kVec2f_GrSLType, kDefault_GrSLPrecision, + "scale", &scaleName); + } GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder; const char* fragmentPos = fragBuilder->fragmentPosition(); // d is the offset to the ellipse center - fragBuilder->codeAppendf("\t\tvec2 d = %s.xy - %s.xy;\n", fragmentPos, ellipseName); - fragBuilder->codeAppendf("\t\tvec2 Z = d * %s.zw;\n", ellipseName); + fragBuilder->codeAppendf("vec2 d = %s.xy - %s.xy;", fragmentPos, ellipseName); + if (scaleName) { + fragBuilder->codeAppendf("d *= %s.y;", scaleName); + } + fragBuilder->codeAppendf("vec2 Z = d * %s.zw;", ellipseName); // implicit is the evaluation of (x/rx)^2 + (y/ry)^2 - 1. - fragBuilder->codeAppend("\t\tfloat implicit = dot(Z, d) - 1.0;\n"); + fragBuilder->codeAppend("float implicit = dot(Z, d) - 1.0;"); // grad_dot is the squared length of the gradient of the implicit. - fragBuilder->codeAppendf("\t\tfloat grad_dot = 4.0 * dot(Z, Z);\n"); - // avoid calling inversesqrt on zero. - fragBuilder->codeAppend("\t\tgrad_dot = max(grad_dot, 1.0e-4);\n"); - fragBuilder->codeAppendf("\t\tfloat approx_dist = implicit * inversesqrt(grad_dot);\n"); + fragBuilder->codeAppendf("float grad_dot = 4.0 * dot(Z, Z);"); + // Avoid calling inversesqrt on zero. + fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);"); + fragBuilder->codeAppendf("float approx_dist = implicit * inversesqrt(grad_dot);"); + if (scaleName) { + fragBuilder->codeAppendf("approx_dist *= %s.x;", scaleName); + } switch (ee.getEdgeType()) { case kFillAA_GrProcessorEdgeType: - fragBuilder->codeAppend("\t\tfloat alpha = clamp(0.5 - approx_dist, 0.0, 1.0);\n"); + fragBuilder->codeAppend("float alpha = clamp(0.5 - approx_dist, 0.0, 1.0);"); break; case kInverseFillAA_GrProcessorEdgeType: - fragBuilder->codeAppend("\t\tfloat alpha = clamp(0.5 + approx_dist, 0.0, 1.0);\n"); + fragBuilder->codeAppend("float alpha = clamp(0.5 + approx_dist, 0.0, 1.0);"); break; case kFillBW_GrProcessorEdgeType: - fragBuilder->codeAppend("\t\tfloat alpha = approx_dist > 0.0 ? 0.0 : 1.0;\n"); + fragBuilder->codeAppend("float alpha = approx_dist > 0.0 ? 0.0 : 1.0;"); break; case kInverseFillBW_GrProcessorEdgeType: - fragBuilder->codeAppend("\t\tfloat alpha = approx_dist > 0.0 ? 1.0 : 0.0;\n"); + fragBuilder->codeAppend("float alpha = approx_dist > 0.0 ? 1.0 : 0.0;"); break; case kHairlineAA_GrProcessorEdgeType: SkFAIL("Hairline not expected here."); } - fragBuilder->codeAppendf("\t\t%s = %s;\n", args.fOutputColor, + fragBuilder->codeAppendf("%s = %s;", args.fOutputColor, (GrGLSLExpr4(args.fInputColor) * GrGLSLExpr1("alpha")).c_str()); } @@ -341,8 +358,26 @@ void GLEllipseEffect::onSetData(const GrGLSLProgramDataManager& pdman, const GrProcessor& effect) { const EllipseEffect& ee = effect.cast(); if (ee.getRadii() != fPrevRadii || ee.getCenter() != fPrevCenter) { - SkScalar invRXSqd = 1.f / (ee.getRadii().fX * ee.getRadii().fX); - SkScalar invRYSqd = 1.f / (ee.getRadii().fY * ee.getRadii().fY); + float invRXSqd; + float invRYSqd; + // If we're using a scale factor to work around precision issues, choose the larger radius + // as the scale factor. The inv radii need to be pre-adjusted by the scale factor. + if (fScaleUniform.isValid()) { + if (ee.getRadii().fX > ee.getRadii().fY) { + invRXSqd = 1.f; + invRYSqd = (ee.getRadii().fX * ee.getRadii().fX) / + (ee.getRadii().fY * ee.getRadii().fY); + pdman.set2f(fScaleUniform, ee.getRadii().fX, 1.f / ee.getRadii().fX); + } else { + invRXSqd = (ee.getRadii().fY * ee.getRadii().fY) / + (ee.getRadii().fX * ee.getRadii().fX); + invRYSqd = 1.f; + pdman.set2f(fScaleUniform, ee.getRadii().fY, 1.f / ee.getRadii().fY); + } + } else { + invRXSqd = 1.f / (ee.getRadii().fX * ee.getRadii().fX); + invRYSqd = 1.f / (ee.getRadii().fY * ee.getRadii().fY); + } pdman.set4f(fEllipseUniform, ee.getCenter().fX, ee.getCenter().fY, invRXSqd, invRYSqd); fPrevCenter = ee.getCenter(); fPrevRadii = ee.getRadii(); diff --git a/gfx/skia/skia/src/gpu/effects/GrPorterDuffXferProcessor.cpp b/gfx/skia/skia/src/gpu/effects/GrPorterDuffXferProcessor.cpp index 42a9631976..94d3dd67a9 100644 --- a/gfx/skia/skia/src/gpu/effects/GrPorterDuffXferProcessor.cpp +++ b/gfx/skia/skia/src/gpu/effects/GrPorterDuffXferProcessor.cpp @@ -16,9 +16,10 @@ #include "GrXferProcessor.h" #include "glsl/GrGLSLBlend.h" #include "glsl/GrGLSLFragmentShaderBuilder.h" -#include "glsl/GrGLSLProgramBuilder.h" #include "glsl/GrGLSLProgramDataManager.h" +#include "glsl/GrGLSLUniformHandler.h" #include "glsl/GrGLSLXferProcessor.h" +#include /** * Wraps the shader outputs and HW blend state that comprise a Porter Duff blend mode with coverage. @@ -71,7 +72,7 @@ public: */ template - struct get_properties : skstd::integral_constant( + struct get_properties : std::integral_constant( (GR_BLEND_MODIFIES_DST(BlendEquation, SrcCoeff, DstCoeff) ? kModifiesDst_Property : 0) | @@ -545,8 +546,8 @@ public: } private: - void emitBlendCodeForDstRead(GrGLSLXPBuilder* pb, - GrGLSLXPFragmentBuilder* fragBuilder, + void emitBlendCodeForDstRead(GrGLSLXPFragmentBuilder* fragBuilder, + GrGLSLUniformHandler* uniformHandler, const char* srcColor, const char* srcCoverage, const char* dstColor, @@ -850,6 +851,12 @@ void GrPorterDuffXPFactory::TestGetXPOutputTypes(const GrXferProcessor* xp, //////////////////////////////////////////////////////////////////////////////////////////////// // SrcOver Global functions //////////////////////////////////////////////////////////////////////////////////////////////// +const GrXferProcessor& GrPorterDuffXPFactory::SimpleSrcOverXP() { + static BlendFormula gSrcOverBlendFormula = COEFF_FORMULA(kOne_GrBlendCoeff, + kISA_GrBlendCoeff); + static PorterDuffXferProcessor gSrcOverXP(gSrcOverBlendFormula); + return gSrcOverXP; +} GrXferProcessor* GrPorterDuffXPFactory::CreateSrcOverXferProcessor( const GrCaps& caps, @@ -860,12 +867,11 @@ GrXferProcessor* GrPorterDuffXPFactory::CreateSrcOverXferProcessor( !(optimizations.fCoveragePOI.isSolidWhite() && !hasMixedSamples && optimizations.fColorPOI.isOpaque())) { - static BlendFormula gSrcOverBlendFormula = COEFF_FORMULA(kOne_GrBlendCoeff, - kISA_GrBlendCoeff); - static PorterDuffXferProcessor gSrcOverXP(gSrcOverBlendFormula); - SkASSERT(!dstTexture || !dstTexture->texture()); - gSrcOverXP.ref(); - return &gSrcOverXP; + // We return nullptr here, which our caller interprets as meaning "use SimpleSrcOverXP". + // We don't simply return the address of that XP here because our caller would have to unref + // it and since it is a global object and GrProgramElement's ref-cnting system is not thread + // safe. + return nullptr; } BlendFormula blendFormula; diff --git a/gfx/skia/skia/src/gpu/effects/GrRRectEffect.cpp b/gfx/skia/skia/src/gpu/effects/GrRRectEffect.cpp index c45f5f309c..0b492762c2 100644 --- a/gfx/skia/skia/src/gpu/effects/GrRRectEffect.cpp +++ b/gfx/skia/skia/src/gpu/effects/GrRRectEffect.cpp @@ -12,10 +12,11 @@ #include "GrInvariantOutput.h" #include "GrOvalEffect.h" #include "SkRRect.h" +#include "SkTLazy.h" #include "glsl/GrGLSLFragmentProcessor.h" #include "glsl/GrGLSLFragmentShaderBuilder.h" -#include "glsl/GrGLSLProgramBuilder.h" #include "glsl/GrGLSLProgramDataManager.h" +#include "glsl/GrGLSLUniformHandler.h" // The effects defined here only handle rrect radii >= kRadiusMin. static const SkScalar kRadiusMin = SK_ScalarHalf; @@ -150,20 +151,31 @@ GLCircularRRectEffect::GLCircularRRectEffect(const GrProcessor& ) { void GLCircularRRectEffect::emitCode(EmitArgs& args) { const CircularRRectEffect& crre = args.fFp.cast(); + GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; const char *rectName; const char *radiusPlusHalfName; // The inner rect is the rrect bounds inset by the radius. Its left, top, right, and bottom // edges correspond to components x, y, z, and w, respectively. When a side of the rrect has // only rectangular corners, that side's value corresponds to the rect edge's value outset by // half a pixel. - fInnerRectUniform = args.fBuilder->addUniform(GrGLSLProgramBuilder::kFragment_Visibility, - kVec4f_GrSLType, kDefault_GrSLPrecision, - "innerRect", - &rectName); - fRadiusPlusHalfUniform = args.fBuilder->addUniform(GrGLSLProgramBuilder::kFragment_Visibility, - kFloat_GrSLType, kDefault_GrSLPrecision, - "radiusPlusHalf", - &radiusPlusHalfName); + fInnerRectUniform = uniformHandler->addUniform(GrGLSLUniformHandler::kFragment_Visibility, + kVec4f_GrSLType, kDefault_GrSLPrecision, + "innerRect", + &rectName); + // x is (r + .5) and y is 1/(r + .5) + fRadiusPlusHalfUniform = uniformHandler->addUniform(GrGLSLUniformHandler::kFragment_Visibility, + kVec2f_GrSLType, kDefault_GrSLPrecision, + "radiusPlusHalf", + &radiusPlusHalfName); + + // If we're on a device with a "real" mediump then the length calculation could overflow. + SkString clampedCircleDistance; + if (args.fGLSLCaps->floatPrecisionVaries()) { + clampedCircleDistance.printf("clamp(%s.x * (1.0 - length(dxy * %s.y)), 0.0, 1.0);", + radiusPlusHalfName, radiusPlusHalfName); + } else { + clampedCircleDistance.printf("clamp(%s.x - length(dxy), 0.0, 1.0);", radiusPlusHalfName); + } GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder; const char* fragmentPos = fragBuilder->fragmentPosition(); @@ -184,103 +196,94 @@ void GLCircularRRectEffect::emitCode(EmitArgs& args) { // alphas together. switch (crre.getCircularCornerFlags()) { case CircularRRectEffect::kAll_CornerFlags: - fragBuilder->codeAppendf("\t\tvec2 dxy0 = %s.xy - %s.xy;\n", rectName, fragmentPos); - fragBuilder->codeAppendf("\t\tvec2 dxy1 = %s.xy - %s.zw;\n", fragmentPos, rectName); - fragBuilder->codeAppend("\t\tvec2 dxy = max(max(dxy0, dxy1), 0.0);\n"); - fragBuilder->codeAppendf("\t\tfloat alpha = clamp(%s - length(dxy), 0.0, 1.0);\n", - radiusPlusHalfName); + fragBuilder->codeAppendf("vec2 dxy0 = %s.xy - %s.xy;", rectName, fragmentPos); + fragBuilder->codeAppendf("vec2 dxy1 = %s.xy - %s.zw;", fragmentPos, rectName); + fragBuilder->codeAppend("vec2 dxy = max(max(dxy0, dxy1), 0.0);"); + fragBuilder->codeAppendf("float alpha = %s;", clampedCircleDistance.c_str()); break; case CircularRRectEffect::kTopLeft_CornerFlag: - fragBuilder->codeAppendf("\t\tvec2 dxy = max(%s.xy - %s.xy, 0.0);\n", + fragBuilder->codeAppendf("vec2 dxy = max(%s.xy - %s.xy, 0.0);", rectName, fragmentPos); - fragBuilder->codeAppendf("\t\tfloat rightAlpha = clamp(%s.z - %s.x, 0.0, 1.0);\n", + fragBuilder->codeAppendf("float rightAlpha = clamp(%s.z - %s.x, 0.0, 1.0);", rectName, fragmentPos); - fragBuilder->codeAppendf("\t\tfloat bottomAlpha = clamp(%s.w - %s.y, 0.0, 1.0);\n", + fragBuilder->codeAppendf("float bottomAlpha = clamp(%s.w - %s.y, 0.0, 1.0);", rectName, fragmentPos); - fragBuilder->codeAppendf( - "\t\tfloat alpha = bottomAlpha * rightAlpha * clamp(%s - length(dxy), 0.0, 1.0);\n", - radiusPlusHalfName); + fragBuilder->codeAppendf("float alpha = bottomAlpha * rightAlpha * %s;", + clampedCircleDistance.c_str()); break; case CircularRRectEffect::kTopRight_CornerFlag: - fragBuilder->codeAppendf("\t\tvec2 dxy = max(vec2(%s.x - %s.z, %s.y - %s.y), 0.0);\n", + fragBuilder->codeAppendf("vec2 dxy = max(vec2(%s.x - %s.z, %s.y - %s.y), 0.0);", fragmentPos, rectName, rectName, fragmentPos); - fragBuilder->codeAppendf("\t\tfloat leftAlpha = clamp(%s.x - %s.x, 0.0, 1.0);\n", + fragBuilder->codeAppendf("float leftAlpha = clamp(%s.x - %s.x, 0.0, 1.0);", fragmentPos, rectName); - fragBuilder->codeAppendf("\t\tfloat bottomAlpha = clamp(%s.w - %s.y, 0.0, 1.0);\n", + fragBuilder->codeAppendf("float bottomAlpha = clamp(%s.w - %s.y, 0.0, 1.0);", rectName, fragmentPos); - fragBuilder->codeAppendf( - "\t\tfloat alpha = bottomAlpha * leftAlpha * clamp(%s - length(dxy), 0.0, 1.0);\n", - radiusPlusHalfName); + fragBuilder->codeAppendf("float alpha = bottomAlpha * leftAlpha * %s;", + clampedCircleDistance.c_str()); break; case CircularRRectEffect::kBottomRight_CornerFlag: - fragBuilder->codeAppendf("\t\tvec2 dxy = max(%s.xy - %s.zw, 0.0);\n", + fragBuilder->codeAppendf("vec2 dxy = max(%s.xy - %s.zw, 0.0);", fragmentPos, rectName); - fragBuilder->codeAppendf("\t\tfloat leftAlpha = clamp(%s.x - %s.x, 0.0, 1.0);\n", + fragBuilder->codeAppendf("float leftAlpha = clamp(%s.x - %s.x, 0.0, 1.0);", fragmentPos, rectName); - fragBuilder->codeAppendf("\t\tfloat topAlpha = clamp(%s.y - %s.y, 0.0, 1.0);\n", + fragBuilder->codeAppendf("float topAlpha = clamp(%s.y - %s.y, 0.0, 1.0);", fragmentPos, rectName); - fragBuilder->codeAppendf( - "\t\tfloat alpha = topAlpha * leftAlpha * clamp(%s - length(dxy), 0.0, 1.0);\n", - radiusPlusHalfName); + fragBuilder->codeAppendf("float alpha = topAlpha * leftAlpha * %s;", + clampedCircleDistance.c_str()); break; case CircularRRectEffect::kBottomLeft_CornerFlag: - fragBuilder->codeAppendf("\t\tvec2 dxy = max(vec2(%s.x - %s.x, %s.y - %s.w), 0.0);\n", + fragBuilder->codeAppendf("vec2 dxy = max(vec2(%s.x - %s.x, %s.y - %s.w), 0.0);", rectName, fragmentPos, fragmentPos, rectName); - fragBuilder->codeAppendf("\t\tfloat rightAlpha = clamp(%s.z - %s.x, 0.0, 1.0);\n", + fragBuilder->codeAppendf("float rightAlpha = clamp(%s.z - %s.x, 0.0, 1.0);", rectName, fragmentPos); - fragBuilder->codeAppendf("\t\tfloat topAlpha = clamp(%s.y - %s.y, 0.0, 1.0);\n", + fragBuilder->codeAppendf("float topAlpha = clamp(%s.y - %s.y, 0.0, 1.0);", fragmentPos, rectName); - fragBuilder->codeAppendf( - "\t\tfloat alpha = topAlpha * rightAlpha * clamp(%s - length(dxy), 0.0, 1.0);\n", - radiusPlusHalfName); + fragBuilder->codeAppendf("float alpha = topAlpha * rightAlpha * %s;", + clampedCircleDistance.c_str()); break; case CircularRRectEffect::kLeft_CornerFlags: - fragBuilder->codeAppendf("\t\tvec2 dxy0 = %s.xy - %s.xy;\n", rectName, fragmentPos); - fragBuilder->codeAppendf("\t\tfloat dy1 = %s.y - %s.w;\n", fragmentPos, rectName); - fragBuilder->codeAppend("\t\tvec2 dxy = max(vec2(dxy0.x, max(dxy0.y, dy1)), 0.0);\n"); - fragBuilder->codeAppendf("\t\tfloat rightAlpha = clamp(%s.z - %s.x, 0.0, 1.0);\n", + fragBuilder->codeAppendf("vec2 dxy0 = %s.xy - %s.xy;", rectName, fragmentPos); + fragBuilder->codeAppendf("float dy1 = %s.y - %s.w;", fragmentPos, rectName); + fragBuilder->codeAppend("vec2 dxy = max(vec2(dxy0.x, max(dxy0.y, dy1)), 0.0);"); + fragBuilder->codeAppendf("float rightAlpha = clamp(%s.z - %s.x, 0.0, 1.0);", rectName, fragmentPos); - fragBuilder->codeAppendf( - "\t\tfloat alpha = rightAlpha * clamp(%s - length(dxy), 0.0, 1.0);\n", - radiusPlusHalfName); + fragBuilder->codeAppendf("float alpha = rightAlpha * %s;", + clampedCircleDistance.c_str()); break; case CircularRRectEffect::kTop_CornerFlags: - fragBuilder->codeAppendf("\t\tvec2 dxy0 = %s.xy - %s.xy;\n", rectName, fragmentPos); - fragBuilder->codeAppendf("\t\tfloat dx1 = %s.x - %s.z;\n", fragmentPos, rectName); - fragBuilder->codeAppend("\t\tvec2 dxy = max(vec2(max(dxy0.x, dx1), dxy0.y), 0.0);\n"); - fragBuilder->codeAppendf("\t\tfloat bottomAlpha = clamp(%s.w - %s.y, 0.0, 1.0);\n", + fragBuilder->codeAppendf("vec2 dxy0 = %s.xy - %s.xy;", rectName, fragmentPos); + fragBuilder->codeAppendf("float dx1 = %s.x - %s.z;", fragmentPos, rectName); + fragBuilder->codeAppend("vec2 dxy = max(vec2(max(dxy0.x, dx1), dxy0.y), 0.0);"); + fragBuilder->codeAppendf("float bottomAlpha = clamp(%s.w - %s.y, 0.0, 1.0);", rectName, fragmentPos); - fragBuilder->codeAppendf( - "\t\tfloat alpha = bottomAlpha * clamp(%s - length(dxy), 0.0, 1.0);\n", - radiusPlusHalfName); + fragBuilder->codeAppendf("float alpha = bottomAlpha * %s;", + clampedCircleDistance.c_str()); break; case CircularRRectEffect::kRight_CornerFlags: - fragBuilder->codeAppendf("\t\tfloat dy0 = %s.y - %s.y;\n", rectName, fragmentPos); - fragBuilder->codeAppendf("\t\tvec2 dxy1 = %s.xy - %s.zw;\n", fragmentPos, rectName); - fragBuilder->codeAppend("\t\tvec2 dxy = max(vec2(dxy1.x, max(dy0, dxy1.y)), 0.0);\n"); - fragBuilder->codeAppendf("\t\tfloat leftAlpha = clamp(%s.x - %s.x, 0.0, 1.0);\n", + fragBuilder->codeAppendf("float dy0 = %s.y - %s.y;", rectName, fragmentPos); + fragBuilder->codeAppendf("vec2 dxy1 = %s.xy - %s.zw;", fragmentPos, rectName); + fragBuilder->codeAppend("vec2 dxy = max(vec2(dxy1.x, max(dy0, dxy1.y)), 0.0);"); + fragBuilder->codeAppendf("float leftAlpha = clamp(%s.x - %s.x, 0.0, 1.0);", fragmentPos, rectName); - fragBuilder->codeAppendf( - "\t\tfloat alpha = leftAlpha * clamp(%s - length(dxy), 0.0, 1.0);\n", - radiusPlusHalfName); + fragBuilder->codeAppendf("float alpha = leftAlpha * %s;", + clampedCircleDistance.c_str()); break; case CircularRRectEffect::kBottom_CornerFlags: - fragBuilder->codeAppendf("\t\tfloat dx0 = %s.x - %s.x;\n", rectName, fragmentPos); - fragBuilder->codeAppendf("\t\tvec2 dxy1 = %s.xy - %s.zw;\n", fragmentPos, rectName); - fragBuilder->codeAppend("\t\tvec2 dxy = max(vec2(max(dx0, dxy1.x), dxy1.y), 0.0);\n"); - fragBuilder->codeAppendf("\t\tfloat topAlpha = clamp(%s.y - %s.y, 0.0, 1.0);\n", + fragBuilder->codeAppendf("float dx0 = %s.x - %s.x;", rectName, fragmentPos); + fragBuilder->codeAppendf("vec2 dxy1 = %s.xy - %s.zw;", fragmentPos, rectName); + fragBuilder->codeAppend("vec2 dxy = max(vec2(max(dx0, dxy1.x), dxy1.y), 0.0);"); + fragBuilder->codeAppendf("float topAlpha = clamp(%s.y - %s.y, 0.0, 1.0);", fragmentPos, rectName); - fragBuilder->codeAppendf( - "\t\tfloat alpha = topAlpha * clamp(%s - length(dxy), 0.0, 1.0);\n", - radiusPlusHalfName); + fragBuilder->codeAppendf("float alpha = topAlpha * %s;", + clampedCircleDistance.c_str()); break; } if (kInverseFillAA_GrProcessorEdgeType == crre.getEdgeType()) { - fragBuilder->codeAppend("\t\talpha = 1.0 - alpha;\n"); + fragBuilder->codeAppend("alpha = 1.0 - alpha;"); } - fragBuilder->codeAppendf("\t\t%s = %s;\n", args.fOutputColor, + fragBuilder->codeAppendf("%s = %s;", args.fOutputColor, (GrGLSLExpr4(args.fInputColor) * GrGLSLExpr1("alpha")).c_str()); } @@ -365,7 +368,8 @@ void GLCircularRRectEffect::onSetData(const GrGLSLProgramDataManager& pdman, SkFAIL("Should have been one of the above cases."); } pdman.set4f(fInnerRectUniform, rect.fLeft, rect.fTop, rect.fRight, rect.fBottom); - pdman.set1f(fRadiusPlusHalfUniform, radius + 0.5f); + radius += 0.5f; + pdman.set2f(fRadiusPlusHalfUniform, radius, 1.f / radius); fPrevRRect = rrect; } } @@ -494,7 +498,8 @@ protected: private: GrGLSLProgramDataManager::UniformHandle fInnerRectUniform; GrGLSLProgramDataManager::UniformHandle fInvRadiiSqdUniform; - SkRRect fPrevRRect; + GrGLSLProgramDataManager::UniformHandle fScaleUniform; + SkRRect fPrevRRect; typedef GrGLSLFragmentProcessor INHERITED; }; @@ -504,12 +509,13 @@ GLEllipticalRRectEffect::GLEllipticalRRectEffect(const GrProcessor& effect) { void GLEllipticalRRectEffect::emitCode(EmitArgs& args) { const EllipticalRRectEffect& erre = args.fFp.cast(); + GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; const char *rectName; // The inner rect is the rrect bounds inset by the x/y radii - fInnerRectUniform = args.fBuilder->addUniform(GrGLSLProgramBuilder::kFragment_Visibility, - kVec4f_GrSLType, kDefault_GrSLPrecision, - "innerRect", - &rectName); + fInnerRectUniform = uniformHandler->addUniform(GrGLSLUniformHandler::kFragment_Visibility, + kVec4f_GrSLType, kDefault_GrSLPrecision, + "innerRect", + &rectName); GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder; const char* fragmentPos = fragBuilder->fragmentPosition(); @@ -525,55 +531,77 @@ void GLEllipticalRRectEffect::emitCode(EmitArgs& args) { // The code below is a simplified version of the above that performs maxs on the vector // components before computing distances and alpha values so that only one distance computation // need be computed to determine the min alpha. - fragBuilder->codeAppendf("\t\tvec2 dxy0 = %s.xy - %s.xy;\n", rectName, fragmentPos); - fragBuilder->codeAppendf("\t\tvec2 dxy1 = %s.xy - %s.zw;\n", fragmentPos, rectName); + fragBuilder->codeAppendf("vec2 dxy0 = %s.xy - %s.xy;", rectName, fragmentPos); + fragBuilder->codeAppendf("vec2 dxy1 = %s.xy - %s.zw;", fragmentPos, rectName); + + // If we're on a device with a "real" mediump then we'll do the distance computation in a space + // that is normalized by the largest radius. The scale uniform will be scale, 1/scale. The + // radii uniform values are already in this normalized space. + const char* scaleName = nullptr; + if (args.fGLSLCaps->floatPrecisionVaries()) { + fScaleUniform = uniformHandler->addUniform(GrGLSLUniformHandler::kFragment_Visibility, + kVec2f_GrSLType, kDefault_GrSLPrecision, + "scale", &scaleName); + } + // The uniforms with the inv squared radii are highp to prevent underflow. switch (erre.getRRect().getType()) { case SkRRect::kSimple_Type: { const char *invRadiiXYSqdName; - fInvRadiiSqdUniform = args.fBuilder->addUniform( - GrGLSLProgramBuilder::kFragment_Visibility, - kVec2f_GrSLType, kHigh_GrSLPrecision, + fInvRadiiSqdUniform = uniformHandler->addUniform( + GrGLSLUniformHandler::kFragment_Visibility, + kVec2f_GrSLType, kDefault_GrSLPrecision, "invRadiiXY", &invRadiiXYSqdName); - fragBuilder->codeAppend("\t\tvec2 dxy = max(max(dxy0, dxy1), 0.0);\n"); + fragBuilder->codeAppend("vec2 dxy = max(max(dxy0, dxy1), 0.0);"); + if (scaleName) { + fragBuilder->codeAppendf("dxy *= %s.y;", scaleName); + } // Z is the x/y offsets divided by squared radii. - fragBuilder->codeAppendf("\t\tvec2 Z = dxy * %s;\n", invRadiiXYSqdName); + fragBuilder->codeAppendf("vec2 Z = dxy * %s.xy;", invRadiiXYSqdName); break; } case SkRRect::kNinePatch_Type: { const char *invRadiiLTRBSqdName; - fInvRadiiSqdUniform = args.fBuilder->addUniform( - GrGLSLProgramBuilder::kFragment_Visibility, - kVec4f_GrSLType, kHigh_GrSLPrecision, + fInvRadiiSqdUniform = uniformHandler->addUniform( + GrGLSLUniformHandler::kFragment_Visibility, + kVec4f_GrSLType, kDefault_GrSLPrecision, "invRadiiLTRB", &invRadiiLTRBSqdName); - fragBuilder->codeAppend("\t\tvec2 dxy = max(max(dxy0, dxy1), 0.0);\n"); + if (scaleName) { + fragBuilder->codeAppendf("dxy0 *= %s.y;", scaleName); + fragBuilder->codeAppendf("dxy1 *= %s.y;", scaleName); + } + fragBuilder->codeAppend("vec2 dxy = max(max(dxy0, dxy1), 0.0);"); // Z is the x/y offsets divided by squared radii. We only care about the (at most) one // corner where both the x and y offsets are positive, hence the maxes. (The inverse // squared radii will always be positive.) - fragBuilder->codeAppendf("\t\tvec2 Z = max(max(dxy0 * %s.xy, dxy1 * %s.zw), 0.0);\n", + fragBuilder->codeAppendf("vec2 Z = max(max(dxy0 * %s.xy, dxy1 * %s.zw), 0.0);", invRadiiLTRBSqdName, invRadiiLTRBSqdName); + break; } default: SkFAIL("RRect should always be simple or nine-patch."); } // implicit is the evaluation of (x/a)^2 + (y/b)^2 - 1. - fragBuilder->codeAppend("\t\tfloat implicit = dot(Z, dxy) - 1.0;\n"); + fragBuilder->codeAppend("float implicit = dot(Z, dxy) - 1.0;"); // grad_dot is the squared length of the gradient of the implicit. - fragBuilder->codeAppendf("\t\tfloat grad_dot = 4.0 * dot(Z, Z);\n"); + fragBuilder->codeAppend("float grad_dot = 4.0 * dot(Z, Z);"); // avoid calling inversesqrt on zero. - fragBuilder->codeAppend("\t\tgrad_dot = max(grad_dot, 1.0e-4);\n"); - fragBuilder->codeAppendf("\t\tfloat approx_dist = implicit * inversesqrt(grad_dot);\n"); - - if (kFillAA_GrProcessorEdgeType == erre.getEdgeType()) { - fragBuilder->codeAppend("\t\tfloat alpha = clamp(0.5 - approx_dist, 0.0, 1.0);\n"); - } else { - fragBuilder->codeAppend("\t\tfloat alpha = clamp(0.5 + approx_dist, 0.0, 1.0);\n"); + fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);"); + fragBuilder->codeAppend("float approx_dist = implicit * inversesqrt(grad_dot);"); + if (scaleName) { + fragBuilder->codeAppendf("approx_dist *= %s.x;", scaleName); } - fragBuilder->codeAppendf("\t\t%s = %s;\n", args.fOutputColor, + if (kFillAA_GrProcessorEdgeType == erre.getEdgeType()) { + fragBuilder->codeAppend("float alpha = clamp(0.5 - approx_dist, 0.0, 1.0);"); + } else { + fragBuilder->codeAppend("float alpha = clamp(0.5 + approx_dist, 0.0, 1.0);"); + } + + fragBuilder->codeAppendf("%s = %s;", args.fOutputColor, (GrGLSLExpr4(args.fInputColor) * GrGLSLExpr1("alpha")).c_str()); } @@ -588,6 +616,8 @@ void GLEllipticalRRectEffect::onSetData(const GrGLSLProgramDataManager& pdman, const GrProcessor& effect) { const EllipticalRRectEffect& erre = effect.cast(); const SkRRect& rrect = erre.getRRect(); + // If we're using a scale factor to work around precision issues, choose the largest radius + // as the scale factor. The inv radii need to be pre-adjusted by the scale factor. if (rrect != fPrevRRect) { SkRect rect = rrect.getBounds(); const SkVector& r0 = rrect.radii(SkRRect::kUpperLeft_Corner); @@ -596,8 +626,18 @@ void GLEllipticalRRectEffect::onSetData(const GrGLSLProgramDataManager& pdman, switch (erre.getRRect().getType()) { case SkRRect::kSimple_Type: rect.inset(r0.fX, r0.fY); - pdman.set2f(fInvRadiiSqdUniform, 1.f / (r0.fX * r0.fX), - 1.f / (r0.fY * r0.fY)); + if (fScaleUniform.isValid()) { + if (r0.fX > r0.fY) { + pdman.set2f(fInvRadiiSqdUniform, 1.f, (r0.fX * r0.fX) / (r0.fY * r0.fY)); + pdman.set2f(fScaleUniform, r0.fX, 1.f / r0.fX); + } else { + pdman.set2f(fInvRadiiSqdUniform, (r0.fY * r0.fY) / (r0.fX * r0.fX), 1.f); + pdman.set2f(fScaleUniform, r0.fY, 1.f / r0.fY); + } + } else { + pdman.set2f(fInvRadiiSqdUniform, 1.f / (r0.fX * r0.fX), + 1.f / (r0.fY * r0.fY)); + } break; case SkRRect::kNinePatch_Type: { const SkVector& r1 = rrect.radii(SkRRect::kLowerRight_Corner); @@ -607,10 +647,20 @@ void GLEllipticalRRectEffect::onSetData(const GrGLSLProgramDataManager& pdman, rect.fTop += r0.fY; rect.fRight -= r1.fX; rect.fBottom -= r1.fY; - pdman.set4f(fInvRadiiSqdUniform, 1.f / (r0.fX * r0.fX), - 1.f / (r0.fY * r0.fY), - 1.f / (r1.fX * r1.fX), - 1.f / (r1.fY * r1.fY)); + if (fScaleUniform.isValid()) { + float scale = SkTMax(SkTMax(r0.fX, r0.fY), SkTMax(r1.fX, r1.fY)); + float scaleSqd = scale * scale; + pdman.set4f(fInvRadiiSqdUniform, scaleSqd / (r0.fX * r0.fX), + scaleSqd / (r0.fY * r0.fY), + scaleSqd / (r1.fX * r1.fX), + scaleSqd / (r1.fY * r1.fY)); + pdman.set2f(fScaleUniform, scale, 1.f / scale); + } else { + pdman.set4f(fInvRadiiSqdUniform, 1.f / (r0.fX * r0.fX), + 1.f / (r0.fY * r0.fY), + 1.f / (r1.fX * r1.fX), + 1.f / (r1.fY * r1.fY)); + } break; } default: diff --git a/gfx/skia/skia/src/gpu/effects/GrSimpleTextureEffect.cpp b/gfx/skia/skia/src/gpu/effects/GrSimpleTextureEffect.cpp index 4db6f2c509..74e86249b0 100644 --- a/gfx/skia/skia/src/gpu/effects/GrSimpleTextureEffect.cpp +++ b/gfx/skia/skia/src/gpu/effects/GrSimpleTextureEffect.cpp @@ -10,7 +10,6 @@ #include "GrTexture.h" #include "glsl/GrGLSLFragmentProcessor.h" #include "glsl/GrGLSLFragmentShaderBuilder.h" -#include "glsl/GrGLSLProgramBuilder.h" class GrGLSimpleTextureEffect : public GrGLSLFragmentProcessor { public: diff --git a/gfx/skia/skia/src/gpu/effects/GrTextureDomain.cpp b/gfx/skia/skia/src/gpu/effects/GrTextureDomain.cpp index c0902c6176..00e8e82c8e 100644 --- a/gfx/skia/skia/src/gpu/effects/GrTextureDomain.cpp +++ b/gfx/skia/skia/src/gpu/effects/GrTextureDomain.cpp @@ -10,9 +10,11 @@ #include "GrSimpleTextureEffect.h" #include "SkFloatingPoint.h" #include "glsl/GrGLSLFragmentProcessor.h" -#include "glsl/GrGLSLProgramBuilder.h" +#include "glsl/GrGLSLFragmentShaderBuilder.h" #include "glsl/GrGLSLProgramDataManager.h" +#include "glsl/GrGLSLShaderBuilder.h" #include "glsl/GrGLSLTextureSampler.h" +#include "glsl/GrGLSLUniformHandler.h" GrTextureDomain::GrTextureDomain(const SkRect& domain, Mode mode, int index) : fIndex(index) { @@ -42,6 +44,7 @@ GrTextureDomain::GrTextureDomain(const SkRect& domain, Mode mode, int index) ////////////////////////////////////////////////////////////////////////////// void GrTextureDomain::GLDomain::sampleTexture(GrGLSLShaderBuilder* builder, + GrGLSLUniformHandler* uniformHandler, const GrGLSLCaps* glslCaps, const GrTextureDomain& textureDomain, const char* outColor, @@ -51,17 +54,15 @@ void GrTextureDomain::GLDomain::sampleTexture(GrGLSLShaderBuilder* builder, SkASSERT((Mode)-1 == fMode || textureDomain.mode() == fMode); SkDEBUGCODE(fMode = textureDomain.mode();) - GrGLSLProgramBuilder* program = builder->getProgramBuilder(); - if (textureDomain.mode() != kIgnore_Mode && !fDomainUni.isValid()) { const char* name; SkString uniName("TexDom"); if (textureDomain.fIndex >= 0) { uniName.appendS32(textureDomain.fIndex); } - fDomainUni = program->addUniform(GrGLSLProgramBuilder::kFragment_Visibility, - kVec4f_GrSLType, kDefault_GrSLPrecision, - uniName.c_str(), &name); + fDomainUni = uniformHandler->addUniform(GrGLSLUniformHandler::kFragment_Visibility, + kVec4f_GrSLType, kDefault_GrSLPrecision, + uniName.c_str(), &name); fDomainName = name; } @@ -199,6 +200,7 @@ void GrGLTextureDomainEffect::emitCode(EmitArgs& args) { GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder; SkString coords2D = fragBuilder->ensureFSCoords2D(args.fCoords, 0); fGLDomain.sampleTexture(fragBuilder, + args.fUniformHandler, args.fGLSLCaps, domain, args.fOutputColor, diff --git a/gfx/skia/skia/src/gpu/effects/GrTextureDomain.h b/gfx/skia/skia/src/gpu/effects/GrTextureDomain.h index 49e557d18a..891ce10b38 100644 --- a/gfx/skia/skia/src/gpu/effects/GrTextureDomain.h +++ b/gfx/skia/skia/src/gpu/effects/GrTextureDomain.h @@ -16,6 +16,7 @@ class GrGLProgramBuilder; class GrGLSLShaderBuilder; class GrInvariantOutput; class GrGLSLTextureSampler; +class GrGLSLUniformHandler; struct SkRect; /** @@ -114,6 +115,7 @@ public: * expression before being written to outColor. */ void sampleTexture(GrGLSLShaderBuilder* builder, + GrGLSLUniformHandler* uniformHandler, const GrGLSLCaps* glslCaps, const GrTextureDomain& textureDomain, const char* outColor, diff --git a/gfx/skia/skia/src/gpu/effects/GrXfermodeFragmentProcessor.cpp b/gfx/skia/skia/src/gpu/effects/GrXfermodeFragmentProcessor.cpp index 6ff4d32f06..c83d6bee55 100644 --- a/gfx/skia/skia/src/gpu/effects/GrXfermodeFragmentProcessor.cpp +++ b/gfx/skia/skia/src/gpu/effects/GrXfermodeFragmentProcessor.cpp @@ -8,11 +8,11 @@ #include "effects/GrXfermodeFragmentProcessor.h" #include "GrFragmentProcessor.h" +#include "GrInvariantOutput.h" #include "effects/GrConstColorProcessor.h" #include "glsl/GrGLSLFragmentProcessor.h" #include "glsl/GrGLSLBlend.h" #include "glsl/GrGLSLFragmentShaderBuilder.h" -#include "glsl/GrGLSLProgramBuilder.h" #include "SkGrPriv.h" class ComposeTwoFragmentProcessor : public GrFragmentProcessor { diff --git a/gfx/skia/skia/src/gpu/effects/GrYUVtoRGBEffect.cpp b/gfx/skia/skia/src/gpu/effects/GrYUVtoRGBEffect.cpp index 6d1c864b82..16dad8635c 100644 --- a/gfx/skia/skia/src/gpu/effects/GrYUVtoRGBEffect.cpp +++ b/gfx/skia/skia/src/gpu/effects/GrYUVtoRGBEffect.cpp @@ -8,12 +8,13 @@ #include "GrYUVtoRGBEffect.h" #include "GrCoordTransform.h" +#include "GrFragmentProcessor.h" #include "GrInvariantOutput.h" #include "GrProcessor.h" #include "glsl/GrGLSLFragmentProcessor.h" #include "glsl/GrGLSLFragmentShaderBuilder.h" -#include "glsl/GrGLSLProgramBuilder.h" #include "glsl/GrGLSLProgramDataManager.h" +#include "glsl/GrGLSLUniformHandler.h" namespace { @@ -67,9 +68,10 @@ public: GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder; const char* yuvMatrix = nullptr; - fMatrixUni = args.fBuilder->addUniform(GrGLSLProgramBuilder::kFragment_Visibility, - kMat44f_GrSLType, kDefault_GrSLPrecision, - "YUVMatrix", &yuvMatrix); + fMatrixUni = args.fUniformHandler->addUniform( + GrGLSLUniformHandler::kFragment_Visibility, + kMat44f_GrSLType, kDefault_GrSLPrecision, + "YUVMatrix", &yuvMatrix); fragBuilder->codeAppendf("\t%s = vec4(\n\t\t", args.fOutputColor); fragBuilder->appendTextureLookup(args.fSamplers[0], args.fCoords[0].c_str(), args.fCoords[0].getType()); diff --git a/gfx/skia/skia/src/gpu/gl/GrGLAssembleInterface.cpp b/gfx/skia/skia/src/gpu/gl/GrGLAssembleInterface.cpp index dbc54adbe4..6b578f34fc 100644 --- a/gfx/skia/skia/src/gpu/gl/GrGLAssembleInterface.cpp +++ b/gfx/skia/skia/src/gpu/gl/GrGLAssembleInterface.cpp @@ -745,17 +745,52 @@ const GrGLInterface* GrGLAssembleGLESInterface(void* ctx, GrGLGetProc get) { GET_PROC_SUFFIX(ProgramPathFragmentInputGen, NV); } + if (extensions.has("GL_CHROMIUM_path_rendering")) { + GET_PROC_SUFFIX(MatrixLoadf, CHROMIUM); + GET_PROC_SUFFIX(MatrixLoadIdentity, CHROMIUM); + GET_PROC_SUFFIX(PathCommands, CHROMIUM); + GET_PROC_SUFFIX(PathParameteri, CHROMIUM); + GET_PROC_SUFFIX(PathParameterf, CHROMIUM); + GET_PROC_SUFFIX(GenPaths, CHROMIUM); + GET_PROC_SUFFIX(DeletePaths, CHROMIUM); + GET_PROC_SUFFIX(IsPath, CHROMIUM); + GET_PROC_SUFFIX(PathStencilFunc, CHROMIUM); + GET_PROC_SUFFIX(StencilFillPath, CHROMIUM); + GET_PROC_SUFFIX(StencilStrokePath, CHROMIUM); + GET_PROC_SUFFIX(StencilFillPathInstanced, CHROMIUM); + GET_PROC_SUFFIX(StencilStrokePathInstanced, CHROMIUM); + GET_PROC_SUFFIX(CoverFillPath, CHROMIUM); + GET_PROC_SUFFIX(CoverStrokePath, CHROMIUM); + GET_PROC_SUFFIX(CoverFillPathInstanced, CHROMIUM); + GET_PROC_SUFFIX(CoverStrokePathInstanced, CHROMIUM); + GET_PROC_SUFFIX(StencilThenCoverFillPath, CHROMIUM); + GET_PROC_SUFFIX(StencilThenCoverStrokePath, CHROMIUM); + GET_PROC_SUFFIX(StencilThenCoverFillPathInstanced, CHROMIUM); + GET_PROC_SUFFIX(StencilThenCoverStrokePathInstanced, CHROMIUM); + GET_PROC_SUFFIX(ProgramPathFragmentInputGen, CHROMIUM); + // GL_CHROMIUM_path_rendering additions: + GET_PROC_SUFFIX(BindFragmentInputLocation, CHROMIUM); + } + if (extensions.has("GL_NV_framebuffer_mixed_samples")) { GET_PROC_SUFFIX(CoverageModulation, NV); } - - if (version >= GR_GL_VER(3,0) || extensions.has("GL_EXT_draw_instanced")) { - GET_PROC(DrawArraysInstanced); - GET_PROC(DrawElementsInstanced); + if (extensions.has("GL_CHROMIUM_framebuffer_mixed_samples")) { + GET_PROC_SUFFIX(CoverageModulation, CHROMIUM); } - if (version >= GR_GL_VER(3,0) || extensions.has("GL_EXT_instanced_arrays")) { + if (version >= GR_GL_VER(3,0)) { + GET_PROC(DrawArraysInstanced); + GET_PROC(DrawElementsInstanced); + } else if (extensions.has("GL_EXT_draw_instanced")) { + GET_PROC_SUFFIX(DrawArraysInstanced, EXT); + GET_PROC_SUFFIX(DrawElementsInstanced, EXT); + } + + if (version >= GR_GL_VER(3,0)) { GET_PROC(VertexAttribDivisor); + } else if (extensions.has("GL_EXT_instanced_arrays")) { + GET_PROC_SUFFIX(VertexAttribDivisor, EXT); } if (extensions.has("GL_NV_bindless_texture")) { diff --git a/gfx/skia/skia/src/gpu/gl/GrGLBufferImpl.cpp b/gfx/skia/skia/src/gpu/gl/GrGLBufferImpl.cpp index 6cbf4e0d0e..2babce8028 100644 --- a/gfx/skia/skia/src/gpu/gl/GrGLBufferImpl.cpp +++ b/gfx/skia/skia/src/gpu/gl/GrGLBufferImpl.cpp @@ -65,7 +65,7 @@ void* GrGLBufferImpl::map(GrGLGpu* gpu) { if (0 == fDesc.fID) { fMapPtr = fCPUData; } else { - fMapPtr = gpu->mapBuffer(fDesc.fID, fBufferType, fDesc.fDynamic, fGLSizeInBytes, + fMapPtr = gpu->mapBuffer(fDesc.fID, fBufferType, fDesc.fUsage, fGLSizeInBytes, fDesc.fSizeInBytes); fGLSizeInBytes = fDesc.fSizeInBytes; } @@ -89,6 +89,7 @@ bool GrGLBufferImpl::isMapped() const { bool GrGLBufferImpl::updateData(GrGLGpu* gpu, const void* src, size_t srcSizeInBytes) { SkASSERT(!this->isMapped()); + SkASSERT(GR_GL_ARRAY_BUFFER == fBufferType || GR_GL_ELEMENT_ARRAY_BUFFER == fBufferType); VALIDATE(); if (srcSizeInBytes > fDesc.fSizeInBytes) { return false; @@ -97,7 +98,7 @@ bool GrGLBufferImpl::updateData(GrGLGpu* gpu, const void* src, size_t srcSizeInB memcpy(fCPUData, src, srcSizeInBytes); return true; } - gpu->bufferData(fDesc.fID, fBufferType, fDesc.fDynamic, fDesc.fSizeInBytes, src, + gpu->bufferData(fDesc.fID, fBufferType, fDesc.fUsage, fDesc.fSizeInBytes, src, srcSizeInBytes); #if GR_GL_USE_BUFFER_DATA_NULL_HINT fGLSizeInBytes = fDesc.fSizeInBytes; @@ -109,7 +110,10 @@ bool GrGLBufferImpl::updateData(GrGLGpu* gpu, const void* src, size_t srcSizeInB } void GrGLBufferImpl::validate() const { - SkASSERT(GR_GL_ARRAY_BUFFER == fBufferType || GR_GL_ELEMENT_ARRAY_BUFFER == fBufferType); + SkASSERT(GR_GL_ARRAY_BUFFER == fBufferType || GR_GL_ELEMENT_ARRAY_BUFFER == fBufferType || + GR_GL_PIXEL_PACK_BUFFER == fBufferType || GR_GL_PIXEL_UNPACK_BUFFER == fBufferType || + GR_GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM == fBufferType || + GR_GL_PIXEL_UNPACK_TRANSFER_BUFFER_CHROMIUM == fBufferType); // The following assert isn't valid when the buffer has been abandoned: // SkASSERT((0 == fDesc.fID) == (fCPUData)); SkASSERT(nullptr == fCPUData || 0 == fGLSizeInBytes); diff --git a/gfx/skia/skia/src/gpu/gl/GrGLBufferImpl.h b/gfx/skia/skia/src/gpu/gl/GrGLBufferImpl.h index 15d2f48efe..a8f2cced37 100644 --- a/gfx/skia/skia/src/gpu/gl/GrGLBufferImpl.h +++ b/gfx/skia/skia/src/gpu/gl/GrGLBufferImpl.h @@ -19,10 +19,20 @@ class GrGLGpu; */ class GrGLBufferImpl : SkNoncopyable { public: + enum Usage { + kStaticDraw_Usage = 0, + kDynamicDraw_Usage, + kStreamDraw_Usage, + kStreamRead_Usage, + + kLast_Usage = kStreamRead_Usage + }; + static const int kUsageCount = kLast_Usage + 1; + struct Desc { GrGLuint fID; // set to 0 to indicate buffer is CPU-backed and not a VBO. size_t fSizeInBytes; - bool fDynamic; + Usage fUsage; }; GrGLBufferImpl(GrGLGpu*, const Desc&, GrGLenum bufferType); @@ -36,6 +46,7 @@ public: GrGLuint bufferID() const { return fDesc.fID; } size_t baseOffset() const { return reinterpret_cast(fCPUData); } + GrGLenum bufferType() const { return fBufferType; } void* map(GrGLGpu* gpu); void unmap(GrGLGpu* gpu); @@ -46,7 +57,7 @@ private: void validate() const; Desc fDesc; - GrGLenum fBufferType; // GL_ARRAY_BUFFER or GL_ELEMENT_ARRAY_BUFFER + GrGLenum fBufferType; // GL_ARRAY_BUFFER or GL_ELEMENT_ARRAY_BUFFER, e.g. void* fCPUData; void* fMapPtr; size_t fGLSizeInBytes; // In certain cases we make the size of the GL buffer object diff --git a/gfx/skia/skia/src/gpu/gl/GrGLCaps.cpp b/gfx/skia/skia/src/gpu/gl/GrGLCaps.cpp index a768b37105..ddc52cc378 100644 --- a/gfx/skia/skia/src/gpu/gl/GrGLCaps.cpp +++ b/gfx/skia/skia/src/gpu/gl/GrGLCaps.cpp @@ -17,17 +17,14 @@ GrGLCaps::GrGLCaps(const GrContextOptions& contextOptions, const GrGLContextInfo& ctxInfo, const GrGLInterface* glInterface) : INHERITED(contextOptions) { - fVerifiedColorConfigs.reset(); fStencilFormats.reset(); fMSFBOType = kNone_MSFBOType; fInvalidateFBType = kNone_InvalidateFBType; - fLATCAlias = kLATC_LATCAlias; fMapBufferType = kNone_MapBufferType; + fTransferBufferType = kNone_TransferBufferType; fMaxFragmentUniformVectors = 0; fMaxVertexAttributes = 0; fMaxFragmentTextureUnits = 0; - fRGBA8RenderbufferSupport = false; - fBGRAIsInternalFormat = false; fUnpackRowLengthSupport = false; fUnpackFlipYSupport = false; fPackRowLengthSupport = false; @@ -36,9 +33,7 @@ GrGLCaps::GrGLCaps(const GrContextOptions& contextOptions, fTexStorageSupport = false; fTextureRedSupport = false; fImagingSupport = false; - fTwoFormatLimit = false; fVertexArrayObjectSupport = false; - fInstancedDrawingSupport = false; fDirectStateAccessSupport = false; fDebugSupport = false; fES2CompatibilitySupport = false; @@ -47,12 +42,12 @@ GrGLCaps::GrGLCaps(const GrContextOptions& contextOptions, fIsCoreProfile = false; fBindFragDataLocationSupport = false; fExternalTextureSupport = false; + fRectangleTextureSupport = false; + fTextureSwizzleSupport = false; fSRGBWriteControl = false; fRGBA8888PixelsOpsAreSlow = false; fPartialFBOReadIsSlow = false; - fReadPixelsSupportedCache.reset(); - fShaderCaps.reset(new GrGLSLCaps(contextOptions)); this->init(contextOptions, ctxInfo, glInterface); @@ -85,14 +80,6 @@ void GrGLCaps::init(const GrContextOptions& contextOptions, GR_GL_GetIntegerv(gli, GR_GL_MAX_VERTEX_ATTRIBS, &fMaxVertexAttributes); GR_GL_GetIntegerv(gli, GR_GL_MAX_TEXTURE_IMAGE_UNITS, &fMaxFragmentTextureUnits); - if (kGL_GrGLStandard == standard) { - fRGBA8RenderbufferSupport = true; - } else { - fRGBA8RenderbufferSupport = version >= GR_GL_VER(3,0) || - ctxInfo.hasExtension("GL_OES_rgb8_rgba8") || - ctxInfo.hasExtension("GL_ARM_rgba8"); - } - if (kGL_GrGLStandard == standard) { fUnpackRowLengthSupport = true; fUnpackFlipYSupport = false; @@ -145,33 +132,6 @@ void GrGLCaps::init(const GrContextOptions& contextOptions, fImagingSupport = kGL_GrGLStandard == standard && ctxInfo.hasExtension("GL_ARB_imaging"); - // ES 2 only guarantees RGBA/uchar + one other format/type combo for - // ReadPixels. The other format has to checked at run-time since it - // can change based on which render target is bound - fTwoFormatLimit = kGLES_GrGLStandard == standard; - - // We only enable srgb support if both textures and FBOs support srgb. - bool srgbSupport = false; - if (kGL_GrGLStandard == standard) { - if (ctxInfo.version() >= GR_GL_VER(3,0)) { - srgbSupport = true; - } else if (ctxInfo.hasExtension("GL_EXT_texture_sRGB")) { - if (ctxInfo.hasExtension("GL_ARB_framebuffer_sRGB") || - ctxInfo.hasExtension("GL_EXT_framebuffer_sRGB")) { - srgbSupport = true; - } - } - // All the above srgb extensions support toggling srgb writes - fSRGBWriteControl = srgbSupport; - } else { - // See https://bug.skia.org/4148 for PowerVR issue. - srgbSupport = kPowerVRRogue_GrGLRenderer != ctxInfo.renderer() && - (ctxInfo.version() >= GR_GL_VER(3,0) || ctxInfo.hasExtension("GL_EXT_sRGB")); - // ES through 3.1 requires EXT_srgb_write_control to support toggling - // sRGB writing for destinations. - fSRGBWriteControl = ctxInfo.hasExtension("GL_EXT_sRGB_write_control"); - } - // SGX and Mali GPUs that are based on a tiled-deferred architecture that have trouble with // frequently changing VBOs. We've measured a performance increase using non-VBO vertex // data for dynamic content on these GPUs. Perhaps we should read the renderer string and @@ -209,16 +169,6 @@ void GrGLCaps::init(const GrContextOptions& contextOptions, ctxInfo.hasExtension("GL_OES_vertex_array_object"); } - if ((kGL_GrGLStandard == standard && version >= GR_GL_VER(3,2)) || - (kGLES_GrGLStandard == standard && version >= GR_GL_VER(3,0))) { - fInstancedDrawingSupport = true; - } else { - fInstancedDrawingSupport = (ctxInfo.hasExtension("GL_ARB_draw_instanced") || - ctxInfo.hasExtension("GL_EXT_draw_instanced")) && - (ctxInfo.hasExtension("GL_ARB_instanced_arrays") || - ctxInfo.hasExtension("GL_EXT_instanced_arrays")); - } - if (kGL_GrGLStandard == standard) { fDirectStateAccessSupport = ctxInfo.hasExtension("GL_EXT_direct_state_access"); } else { @@ -270,6 +220,21 @@ void GrGLCaps::init(const GrContextOptions& contextOptions, } } + if ((kGL_GrGLStandard == standard && version >= GR_GL_VER(3, 2)) || + ctxInfo.hasExtension("GL_ARB_texture_rectangle")) { + fRectangleTextureSupport = true; + } + + if (kGL_GrGLStandard == standard) { + if (version >= GR_GL_VER(3,3) || ctxInfo.hasExtension("GL_ARB_texture_swizzle")) { + fTextureSwizzleSupport = true; + } + } else { + if (version >= GR_GL_VER(3,0)) { + fTextureSwizzleSupport = true; + } + } + #ifdef SK_BUILD_FOR_WIN // We're assuming that on Windows Chromium we're using ANGLE. bool isANGLE = kANGLE_GrGLDriver == ctxInfo.driver() || @@ -319,7 +284,8 @@ void GrGLCaps::init(const GrContextOptions& contextOptions, // We need dual source blending and the ability to disable multisample in order to support mixed // samples in every corner case. if (fMultisampleDisableSupport && glslCaps->dualSourceBlendingSupport()) { - fMixedSamplesSupport = ctxInfo.hasExtension("GL_NV_framebuffer_mixed_samples"); + fMixedSamplesSupport = ctxInfo.hasExtension("GL_NV_framebuffer_mixed_samples") || + ctxInfo.hasExtension("GL_CHROMIUM_framebuffer_mixed_samples"); // Workaround NVIDIA bug related to glInvalidateFramebuffer and mixed samples. if (fMixedSamplesSupport && kNVIDIA_GrGLDriver == ctxInfo.driver()) { fDiscardRenderTargetSupport = false; @@ -370,6 +336,18 @@ void GrGLCaps::init(const GrContextOptions& contextOptions, } } + if (kGL_GrGLStandard == standard) { + if (version >= GR_GL_VER(3, 0) || ctxInfo.hasExtension("GL_ARB_pixel_buffer_object")) { + fTransferBufferType = kPBO_TransferBufferType; + } + } else { + if (version >= GR_GL_VER(3, 0) || ctxInfo.hasExtension("GL_NV_pixel_buffer_object")) { + fTransferBufferType = kPBO_TransferBufferType; + } else if (ctxInfo.hasExtension("GL_CHROMIUM_pixel_transfer_buffer_object")) { + fTransferBufferType = kChromium_TransferBufferType; + } + } + // On many GPUs, map memory is very expensive, so we effectively disable it here by setting the // threshold to the maximum unless the client gives us a hint that map memory is cheap. if (fGeometryBufferMapThreshold < 0) { @@ -475,11 +453,15 @@ void GrGLCaps::init(const GrContextOptions& contextOptions, ctxInfo.hasExtension("GL_EXT_instanced_arrays")); } - this->initConfigTexturableTable(ctxInfo, gli, srgbSupport); - this->initConfigRenderableTable(ctxInfo, srgbSupport); this->initShaderPrecisionTable(ctxInfo, gli, glslCaps); - // Requires fTexutreSwizzleSupport and fTextureRedSupport to be set before this point. - this->initConfigSwizzleTable(ctxInfo, glslCaps); + + if (contextOptions.fUseShaderSwizzling) { + fTextureSwizzleSupport = false; + } + + // Requires fTextureRedSupport, fTextureSwizzleSupport, msaa support, ES compatibility have + // already been detected. + this->initConfigTable(ctxInfo, gli, glslCaps); this->applyOptionsOverrides(contextOptions); glslCaps->applyOptionsOverrides(contextOptions); @@ -575,9 +557,6 @@ void GrGLCaps::initGLSL(const GrGLContextInfo& ctxInfo) { // from our GrTextureDomain processor. glslCaps->fCanUseAnyFunctionInShader = kImagination_GrGLVendor != ctxInfo.vendor(); - glslCaps->fForceHighPrecisionNDSTransform = kARM_GrGLVendor == ctxInfo.vendor() || - kPowerVR54x_GrGLRenderer == ctxInfo.renderer(); - glslCaps->fVersionDeclString = get_glsl_version_decl_string(standard, glslCaps->fGLSLGeneration, fIsCoreProfile); @@ -652,345 +631,47 @@ bool GrGLCaps::hasPathRenderingSupport(const GrGLContextInfo& ctxInfo, const GrG } return true; } - -void GrGLCaps::initConfigRenderableTable(const GrGLContextInfo& ctxInfo, bool srgbSupport) { - // OpenGL < 3.0 - // no support for render targets unless the GL_ARB_framebuffer_object - // extension is supported (in which case we get ALPHA, RED, RG, RGB, - // RGBA (ALPHA8, RGBA4, RGBA8) for OpenGL > 1.1). Note that we - // probably don't get R8 in this case. - - // OpenGL 3.0 - // base color renderable: ALPHA, RED, RG, RGB, and RGBA - // sized derivatives: ALPHA8, R8, RGBA4, RGBA8 - - // >= OpenGL 3.1 - // base color renderable: RED, RG, RGB, and RGBA - // sized derivatives: R8, RGBA4, RGBA8 - // if the GL_ARB_compatibility extension is supported then we get back - // support for GL_ALPHA and ALPHA8 - - // GL_EXT_bgra adds BGRA render targets to any version - - // ES 2.0 - // color renderable: RGBA4, RGB5_A1, RGB565 - // GL_EXT_texture_rg adds support for R8 as a color render target - // GL_OES_rgb8_rgba8 and/or GL_ARM_rgba8 adds support for RGBA8 - // GL_EXT_texture_format_BGRA8888 and/or GL_APPLE_texture_format_BGRA8888 added BGRA support - - // ES 3.0 - // Same as ES 2.0 except R8 and RGBA8 are supported without extensions (the functions called - // below already account for this). - - GrGLStandard standard = ctxInfo.standard(); - - enum { - kNo_MSAA = 0, - kYes_MSAA = 1, - }; - - if (kGL_GrGLStandard == standard) { - // Post 3.0 we will get R8 - // Prior to 3.0 we will get ALPHA8 (with GL_ARB_framebuffer_object) - if (ctxInfo.version() >= GR_GL_VER(3,0) || - ctxInfo.hasExtension("GL_ARB_framebuffer_object")) { - fConfigRenderSupport[kAlpha_8_GrPixelConfig][kNo_MSAA] = true; - fConfigRenderSupport[kAlpha_8_GrPixelConfig][kYes_MSAA] = true; - } - } else { - // On ES we can only hope for R8 - fConfigRenderSupport[kAlpha_8_GrPixelConfig][kNo_MSAA] = fTextureRedSupport; - fConfigRenderSupport[kAlpha_8_GrPixelConfig][kYes_MSAA] = fTextureRedSupport; - } - - if (kGL_GrGLStandard != standard) { - // only available in ES - fConfigRenderSupport[kRGB_565_GrPixelConfig][kNo_MSAA] = true; - fConfigRenderSupport[kRGB_565_GrPixelConfig][kYes_MSAA] = true; - } - - // we no longer support 444 as a render target - fConfigRenderSupport[kRGBA_4444_GrPixelConfig][kNo_MSAA] = false; - fConfigRenderSupport[kRGBA_4444_GrPixelConfig][kYes_MSAA] = false; - - if (this->fRGBA8RenderbufferSupport) { - fConfigRenderSupport[kRGBA_8888_GrPixelConfig][kNo_MSAA] = true; - fConfigRenderSupport[kRGBA_8888_GrPixelConfig][kYes_MSAA] = true; - } - - if (this->isConfigTexturable(kBGRA_8888_GrPixelConfig)) { - fConfigRenderSupport[kBGRA_8888_GrPixelConfig][kNo_MSAA] = true; - // The GL_EXT_texture_format_BGRA8888 extension does not add BGRA to the list of - // configs that are color-renderable and can be passed to glRenderBufferStorageMultisample. - // Chromium may have an extension to allow BGRA renderbuffers to work on desktop platforms. - if (ctxInfo.hasExtension("GL_CHROMIUM_renderbuffer_format_BGRA8888")) { - fConfigRenderSupport[kBGRA_8888_GrPixelConfig][kYes_MSAA] = true; - } else { - fConfigRenderSupport[kBGRA_8888_GrPixelConfig][kYes_MSAA] = - !fBGRAIsInternalFormat || !this->usesMSAARenderBuffers(); - } - } - - if (this->fRGBA8RenderbufferSupport && srgbSupport) { - fConfigRenderSupport[kSRGBA_8888_GrPixelConfig][kNo_MSAA] = true; - fConfigRenderSupport[kSRGBA_8888_GrPixelConfig][kYes_MSAA] = true; - } - - if (this->isConfigTexturable(kRGBA_float_GrPixelConfig)) { - if (kGL_GrGLStandard == standard) { - fConfigRenderSupport[kRGBA_float_GrPixelConfig][kNo_MSAA] = true; - fConfigRenderSupport[kRGBA_float_GrPixelConfig][kYes_MSAA] = true; - } else { - // for now we only enable this on desktop, because on ES we'd have to solve many - // precision issues and no clients actually want this yet - /* - if (ctxInfo.hasExtension("GL_EXT_color_buffer_float")) { - fConfigRenderSupport[kRGBA_float_GrPixelConfig][kNo_MSAA] = true; - } else { - fConfigRenderSupport[kRGBA_float_GrPixelConfig][kNo_MSAA] = false; - } - // for now we don't support floating point MSAA on ES - fConfigRenderSupport[kRGBA_float_GrPixelConfig][kYes_MSAA] = false;*/ - fConfigRenderSupport[kRGBA_float_GrPixelConfig][kNo_MSAA] = false; - fConfigRenderSupport[kRGBA_float_GrPixelConfig][kYes_MSAA] = false; - } - } - - if (this->isConfigTexturable(kAlpha_half_GrPixelConfig)) { - if (kGL_GrGLStandard == standard) { - fConfigRenderSupport[kAlpha_half_GrPixelConfig][kNo_MSAA] = true; - fConfigRenderSupport[kAlpha_half_GrPixelConfig][kYes_MSAA] = true; - } else if (ctxInfo.version() >= GR_GL_VER(3,0)) { - fConfigRenderSupport[kAlpha_half_GrPixelConfig][kNo_MSAA] = true; - // for now we don't support floating point MSAA on ES - fConfigRenderSupport[kAlpha_half_GrPixelConfig][kYes_MSAA] = false; - } else { - if (ctxInfo.hasExtension("GL_EXT_color_buffer_half_float") && fTextureRedSupport) { - fConfigRenderSupport[kAlpha_half_GrPixelConfig][kNo_MSAA] = true; - } else { - fConfigRenderSupport[kAlpha_half_GrPixelConfig][kNo_MSAA] = false; - } - // for now we don't support floating point MSAA on ES - fConfigRenderSupport[kAlpha_half_GrPixelConfig][kYes_MSAA] = false; - } - } - - if (this->isConfigTexturable(kRGBA_half_GrPixelConfig)) { - if (kGL_GrGLStandard == standard) { - fConfigRenderSupport[kRGBA_half_GrPixelConfig][kNo_MSAA] = true; - fConfigRenderSupport[kRGBA_half_GrPixelConfig][kYes_MSAA] = true; - } else if (ctxInfo.version() >= GR_GL_VER(3, 0)) { - fConfigRenderSupport[kRGBA_half_GrPixelConfig][kNo_MSAA] = true; - // for now we don't support floating point MSAA on ES - fConfigRenderSupport[kRGBA_half_GrPixelConfig][kYes_MSAA] = false; - } else { - if (ctxInfo.hasExtension("GL_EXT_color_buffer_half_float")) { - fConfigRenderSupport[kRGBA_half_GrPixelConfig][kNo_MSAA] = true; - } else { - fConfigRenderSupport[kRGBA_half_GrPixelConfig][kNo_MSAA] = false; - } - // for now we don't support floating point MSAA on ES - fConfigRenderSupport[kRGBA_half_GrPixelConfig][kYes_MSAA] = false; - } - } - - // If we don't support MSAA then undo any places above where we set a config as renderable with - // msaa. - if (kNone_MSFBOType == fMSFBOType) { - for (int i = 0; i < kGrPixelConfigCnt; ++i) { - fConfigRenderSupport[i][kYes_MSAA] = false; - } - } -} - -void GrGLCaps::initConfigTexturableTable(const GrGLContextInfo& ctxInfo, const GrGLInterface* gli, - bool srgbSupport) { - GrGLStandard standard = ctxInfo.standard(); - GrGLVersion version = ctxInfo.version(); - - // Base texture support - fConfigTextureSupport[kAlpha_8_GrPixelConfig] = true; - fConfigTextureSupport[kRGB_565_GrPixelConfig] = true; - fConfigTextureSupport[kRGBA_4444_GrPixelConfig] = true; - fConfigTextureSupport[kRGBA_8888_GrPixelConfig] = true; - - // Disable this for now, while we investigate https://bug.skia.org/4333 - if (false) { - // Check for 8-bit palette.. - GrGLint numFormats; - GR_GL_GetIntegerv(gli, GR_GL_NUM_COMPRESSED_TEXTURE_FORMATS, &numFormats); - if (numFormats) { - SkAutoSTMalloc<10, GrGLint> formats(numFormats); - GR_GL_GetIntegerv(gli, GR_GL_COMPRESSED_TEXTURE_FORMATS, formats); - for (int i = 0; i < numFormats; ++i) { - if (GR_GL_PALETTE8_RGBA8 == formats[i]) { - fConfigTextureSupport[kIndex_8_GrPixelConfig] = true; - break; - } - } - } - } - - // Check for BGRA - if (kGL_GrGLStandard == standard) { - fConfigTextureSupport[kBGRA_8888_GrPixelConfig] = - version >= GR_GL_VER(1,2) || ctxInfo.hasExtension("GL_EXT_bgra"); - } else { - if (ctxInfo.hasExtension("GL_APPLE_texture_format_BGRA8888")) { - fConfigTextureSupport[kBGRA_8888_GrPixelConfig] = true; - } else if (ctxInfo.hasExtension("GL_EXT_texture_format_BGRA8888")) { - fConfigTextureSupport[kBGRA_8888_GrPixelConfig] = true; - fBGRAIsInternalFormat = true; - } - SkASSERT(fConfigTextureSupport[kBGRA_8888_GrPixelConfig] || - kSkia8888_GrPixelConfig != kBGRA_8888_GrPixelConfig); - } - - fConfigTextureSupport[kSRGBA_8888_GrPixelConfig] = srgbSupport; - - // Compressed texture support - - // glCompressedTexImage2D is available on all OpenGL ES devices... - // however, it is only available on standard OpenGL after version 1.3 - bool hasCompressTex2D = (kGL_GrGLStandard != standard || version >= GR_GL_VER(1, 3)); - - fCompressedTexSubImageSupport = - hasCompressTex2D && (gli->fFunctions.fCompressedTexSubImage2D); - - // Check for ETC1 - bool hasETC1 = false; - - // First check version for support - if (kGL_GrGLStandard == standard) { - hasETC1 = hasCompressTex2D && - (version >= GR_GL_VER(4, 3) || - ctxInfo.hasExtension("GL_ARB_ES3_compatibility")); - } else { - hasETC1 = hasCompressTex2D && - (version >= GR_GL_VER(3, 0) || - ctxInfo.hasExtension("GL_OES_compressed_ETC1_RGB8_texture") || - // ETC2 is a superset of ETC1, so we can just check for that, too. - (ctxInfo.hasExtension("GL_OES_compressed_ETC2_RGB8_texture") && - ctxInfo.hasExtension("GL_OES_compressed_ETC2_RGBA8_texture"))); - } - fConfigTextureSupport[kETC1_GrPixelConfig] = hasETC1; - - // Check for LATC under its various forms - LATCAlias alias = kLATC_LATCAlias; - bool hasLATC = hasCompressTex2D && - (ctxInfo.hasExtension("GL_EXT_texture_compression_latc") || - ctxInfo.hasExtension("GL_NV_texture_compression_latc")); - - // Check for RGTC - if (!hasLATC) { - // If we're using OpenGL 3.0 or later, then we have RGTC, an identical compression format. - if (kGL_GrGLStandard == standard) { - hasLATC = version >= GR_GL_VER(3, 0); - } - - if (!hasLATC) { - hasLATC = - ctxInfo.hasExtension("GL_EXT_texture_compression_rgtc") || - ctxInfo.hasExtension("GL_ARB_texture_compression_rgtc"); - } - - if (hasLATC) { - alias = kRGTC_LATCAlias; - } - } - - // Check for 3DC - if (!hasLATC) { - hasLATC = ctxInfo.hasExtension("GL_AMD_compressed_3DC_texture"); - if (hasLATC) { - alias = k3DC_LATCAlias; - } - } - - fConfigTextureSupport[kLATC_GrPixelConfig] = hasLATC; - fLATCAlias = alias; - - // Check for R11_EAC ... We don't support R11_EAC on desktop, as most - // cards default to decompressing the textures in the driver, and is - // generally slower. - if (kGL_GrGLStandard != standard) { - fConfigTextureSupport[kR11_EAC_GrPixelConfig] = version >= GR_GL_VER(3, 0); - } - - // Check for ASTC - fConfigTextureSupport[kASTC_12x12_GrPixelConfig] = - ctxInfo.hasExtension("GL_KHR_texture_compression_astc_hdr") || - ctxInfo.hasExtension("GL_KHR_texture_compression_astc_ldr") || - ctxInfo.hasExtension("GL_OES_texture_compression_astc"); - - // Check for floating point texture support - // NOTE: We disallow floating point textures on ES devices if linear - // filtering modes are not supported. This is for simplicity, but a more - // granular approach is possible. Coincidentally, floating point textures became part of - // the standard in ES3.1 / OGL 3.1, hence the shorthand - bool hasFPTextures = version >= GR_GL_VER(3, 1); - if (!hasFPTextures) { - hasFPTextures = ctxInfo.hasExtension("GL_ARB_texture_float") || - (ctxInfo.hasExtension("GL_OES_texture_float_linear") && - ctxInfo.hasExtension("GL_OES_texture_float")); - } - fConfigTextureSupport[kRGBA_float_GrPixelConfig] = hasFPTextures; - - // Check for fp16 texture support - // NOTE: We disallow floating point textures on ES devices if linear - // filtering modes are not supported. This is for simplicity, but a more - // granular approach is possible. Coincidentally, 16-bit floating point textures became part of - // the standard in ES3.1 / OGL 3.1, hence the shorthand - bool hasHalfFPTextures = version >= GR_GL_VER(3, 1); - if (!hasHalfFPTextures) { - hasHalfFPTextures = ctxInfo.hasExtension("GL_ARB_texture_float") || - (ctxInfo.hasExtension("GL_OES_texture_half_float_linear") && - ctxInfo.hasExtension("GL_OES_texture_half_float")); - } - fConfigTextureSupport[kAlpha_half_GrPixelConfig] = hasHalfFPTextures; - fConfigTextureSupport[kRGBA_half_GrPixelConfig] = hasHalfFPTextures; -} - -bool GrGLCaps::doReadPixelsSupported(const GrGLInterface* intf, - GrGLenum format, - GrGLenum type) const { - if (GR_GL_RGBA == format && GR_GL_UNSIGNED_BYTE == type) { - // ES 2 guarantees this format is supported - return true; - } - - if (!fTwoFormatLimit) { - // not limited by ES 2's constraints - return true; - } - - GrGLint otherFormat = GR_GL_RGBA; - GrGLint otherType = GR_GL_UNSIGNED_BYTE; - - // The other supported format/type combo supported for ReadPixels - // can change based on which render target is bound - GR_GL_GetIntegerv(intf, - GR_GL_IMPLEMENTATION_COLOR_READ_FORMAT, - &otherFormat); - - GR_GL_GetIntegerv(intf, - GR_GL_IMPLEMENTATION_COLOR_READ_TYPE, - &otherType); - - return (GrGLenum)otherFormat == format && (GrGLenum)otherType == type; -} - bool GrGLCaps::readPixelsSupported(const GrGLInterface* intf, - GrGLenum format, - GrGLenum type, - GrGLenum currFboFormat) const { - ReadPixelsSupportedFormat key = {format, type, currFboFormat}; - if (const bool* supported = fReadPixelsSupportedCache.find(key)) { - return *supported; + GrPixelConfig readConfig, + GrPixelConfig currFBOConfig) const { + SkASSERT(this->isConfigRenderable(currFBOConfig, false)); + + GrGLenum readFormat; + GrGLenum readType; + if (!this->getReadPixelsFormat(currFBOConfig, readConfig, &readFormat, &readType)) { + return false; } - bool supported = this->doReadPixelsSupported(intf, format, type); - fReadPixelsSupportedCache.set(key, supported); - return supported; + + if (kGL_GrGLStandard == intf->fStandard) { + // All of our renderable configs can be converted to each other by glReadPixels in OpenGL. + return true; + } + + // See Section 16.1.2 in the ES 3.2 specification. + + if (kNormalizedFixedPoint_FormatType == fConfigTable[currFBOConfig].fFormatType) { + if (GR_GL_RGBA == readFormat && GR_GL_UNSIGNED_BYTE == readType) { + return true; + } + } else { + SkASSERT(kFloat_FormatType == fConfigTable[currFBOConfig].fFormatType); + if (GR_GL_RGBA == readFormat && GR_GL_FLOAT == readType) { + return true; + } + } + + if (0 == fConfigTable[currFBOConfig].fSecondReadPixelsFormat.fFormat) { + ReadPixelsFormat* rpFormat = + const_cast(&fConfigTable[currFBOConfig].fSecondReadPixelsFormat); + GrGLint format = 0, type = 0; + GR_GL_GetIntegerv(intf, GR_GL_IMPLEMENTATION_COLOR_READ_FORMAT, &format); + GR_GL_GetIntegerv(intf, GR_GL_IMPLEMENTATION_COLOR_READ_TYPE, &type); + rpFormat->fFormat = format; + rpFormat->fType = type; + } + + return fConfigTable[currFBOConfig].fSecondReadPixelsFormat.fFormat == readFormat && + fConfigTable[currFBOConfig].fSecondReadPixelsFormat.fType == readType; } void GrGLCaps::initFSAASupport(const GrGLContextInfo& ctxInfo, const GrGLInterface* gli) { @@ -1190,8 +871,6 @@ SkString GrGLCaps::dump() const { r.appendf("Max FS Uniform Vectors: %d\n", fMaxFragmentUniformVectors); r.appendf("Max FS Texture Units: %d\n", fMaxFragmentTextureUnits); r.appendf("Max Vertex Attributes: %d\n", fMaxVertexAttributes); - r.appendf("Support RGBA8 Render Buffer: %s\n", (fRGBA8RenderbufferSupport ? "YES": "NO")); - r.appendf("BGRA is an internal format: %s\n", (fBGRAIsInternalFormat ? "YES": "NO")); r.appendf("Unpack Row length support: %s\n", (fUnpackRowLengthSupport ? "YES": "NO")); r.appendf("Unpack Flip Y support: %s\n", (fUnpackFlipYSupport ? "YES": "NO")); r.appendf("Pack Row length support: %s\n", (fPackRowLengthSupport ? "YES": "NO")); @@ -1201,9 +880,7 @@ SkString GrGLCaps::dump() const { r.appendf("Texture Storage support: %s\n", (fTexStorageSupport ? "YES": "NO")); r.appendf("GL_R support: %s\n", (fTextureRedSupport ? "YES": "NO")); r.appendf("GL_ARB_imaging support: %s\n", (fImagingSupport ? "YES": "NO")); - r.appendf("Two Format Limit: %s\n", (fTwoFormatLimit ? "YES": "NO")); r.appendf("Vertex array object support: %s\n", (fVertexArrayObjectSupport ? "YES": "NO")); - r.appendf("Instanced drawing support: %s\n", (fInstancedDrawingSupport ? "YES": "NO")); r.appendf("Direct state access support: %s\n", (fDirectStateAccessSupport ? "YES": "NO")); r.appendf("Debug support: %s\n", (fDebugSupport ? "YES": "NO")); r.appendf("Multisample disable support: %s\n", (fMultisampleDisableSupport ? "YES" : "NO")); @@ -1213,6 +890,26 @@ SkString GrGLCaps::dump() const { r.appendf("RGBA 8888 pixel ops are slow: %s\n", (fRGBA8888PixelsOpsAreSlow ? "YES" : "NO")); r.appendf("Partial FBO read is slow: %s\n", (fPartialFBOReadIsSlow ? "YES" : "NO")); r.appendf("Bind uniform location support: %s\n", (fBindUniformLocationSupport ? "YES" : "NO")); + r.appendf("External texture support: %s\n", (fExternalTextureSupport ? "YES" : "NO")); + r.appendf("Rectangle texture support: %s\n", (fRectangleTextureSupport? "YES" : "NO")); + r.appendf("Texture swizzle support: %s\n", (fTextureSwizzleSupport ? "YES" : "NO")); + + r.append("Configs\n-------\n"); + for (int i = 0; i < kGrPixelConfigCnt; ++i) { + r.appendf(" cfg: %d flags: 0x%04x, b_internal: 0x%08x s_internal: 0x%08x, e_format: " + "0x%08x, e_format_teximage: 0x%08x, e_type: 0x%08x, i_for_teximage: 0x%08x, " + "i_for_renderbuffer: 0x%08x\n", + i, + fConfigTable[i].fFlags, + fConfigTable[i].fFormats.fBaseInternalFormat, + fConfigTable[i].fFormats.fSizedInternalFormat, + fConfigTable[i].fFormats.fExternalFormat[kOther_ExternalFormatUsage], + fConfigTable[i].fFormats.fExternalFormat[kTexImage_ExternalFormatUsage], + fConfigTable[i].fFormats.fExternalType, + fConfigTable[i].fFormats.fInternalFormatTexImage, + fConfigTable[i].fFormats.fInternalFormatRenderbuffer); + } + return r; } @@ -1300,44 +997,592 @@ void GrGLCaps::initShaderPrecisionTable(const GrGLContextInfo& ctxInfo, } } -void GrGLCaps::initConfigSwizzleTable(const GrGLContextInfo& ctxInfo, GrGLSLCaps* glslCaps) { +bool GrGLCaps::bgraIsInternalFormat() const { + return fConfigTable[kBGRA_8888_GrPixelConfig].fFormats.fBaseInternalFormat == GR_GL_BGRA; +} + +bool GrGLCaps::getTexImageFormats(GrPixelConfig surfaceConfig, GrPixelConfig externalConfig, + GrGLenum* internalFormat, GrGLenum* externalFormat, + GrGLenum* externalType) const { + if (!this->getExternalFormat(surfaceConfig, externalConfig, kTexImage_ExternalFormatUsage, + externalFormat, externalType)) { + return false; + } + *internalFormat = fConfigTable[surfaceConfig].fFormats.fInternalFormatTexImage; + return true; +} + +bool GrGLCaps::getCompressedTexImageFormats(GrPixelConfig surfaceConfig, + GrGLenum* internalFormat) const { + if (!GrPixelConfigIsCompressed(surfaceConfig)) { + return false; + } + *internalFormat = fConfigTable[surfaceConfig].fFormats.fInternalFormatTexImage; + return true; +} + +bool GrGLCaps::getReadPixelsFormat(GrPixelConfig surfaceConfig, GrPixelConfig externalConfig, + GrGLenum* externalFormat, GrGLenum* externalType) const { + if (!this->getExternalFormat(surfaceConfig, externalConfig, kOther_ExternalFormatUsage, + externalFormat, externalType)) { + return false; + } + return true; +} + +bool GrGLCaps::getRenderbufferFormat(GrPixelConfig config, GrGLenum* internalFormat) const { + if (GrPixelConfigIsCompressed(config)) { + return false; + } + *internalFormat = fConfigTable[config].fFormats.fInternalFormatRenderbuffer; + return true; +} + +bool GrGLCaps::getExternalFormat(GrPixelConfig surfaceConfig, GrPixelConfig memoryConfig, + ExternalFormatUsage usage, GrGLenum* externalFormat, + GrGLenum* externalType) const { + SkASSERT(externalFormat && externalType); + if (GrPixelConfigIsCompressed(memoryConfig) || GrPixelConfigIsCompressed(memoryConfig)) { + return false; + } + + bool surfaceIsAlphaOnly = GrPixelConfigIsAlphaOnly(surfaceConfig); + bool memoryIsAlphaOnly = GrPixelConfigIsAlphaOnly(memoryConfig); + + // We don't currently support moving RGBA data into and out of ALPHA surfaces. It could be + // made to work in many cases using glPixelStore and what not but is not needed currently. + if (surfaceIsAlphaOnly && !memoryIsAlphaOnly) { + return false; + } + + *externalFormat = fConfigTable[memoryConfig].fFormats.fExternalFormat[usage]; + *externalType = fConfigTable[memoryConfig].fFormats.fExternalType; + + return true; +} + +void GrGLCaps::initConfigTable(const GrGLContextInfo& ctxInfo, const GrGLInterface* gli, + GrGLSLCaps* glslCaps) { + /* + Comments on renderability of configs on various GL versions. + OpenGL < 3.0: + no built in support for render targets. + GL_EXT_framebuffer_object adds possible support for any sized format with base internal + format RGB, RGBA and NV float formats we don't use. + This is the following: + R3_G3_B2, RGB4, RGB5, RGB8, RGB10, RGB12, RGB16, RGBA2, RGBA4, RGB5_A1, RGBA8 + RGB10_A2, RGBA12,RGBA16 + Though, it is hard to believe the more obscure formats such as RGBA12 would work + since they aren't required by later standards and the driver can simply return + FRAMEBUFFER_UNSUPPORTED for anything it doesn't allow. + GL_ARB_framebuffer_object adds everything added by the EXT extension and additionally + any sized internal format with a base internal format of ALPHA, LUMINANCE, + LUMINANCE_ALPHA, INTENSITY, RED, and RG. + This adds a lot of additional renderable sized formats, including ALPHA8. + The GL_ARB_texture_rg brings in the RED and RG formats (8, 8I, 8UI, 16, 16I, 16UI, + 16F, 32I, 32UI, and 32F variants). + Again, the driver has an escape hatch via FRAMEBUFFER_UNSUPPORTED. + + For both the above extensions we limit ourselves to those that are also required by + OpenGL 3.0. + + OpenGL 3.0: + Any format with base internal format ALPHA, RED, RG, RGB or RGBA is "color-renderable" + but are not required to be supported as renderable textures/renderbuffer. + Required renderable color formats: + - RGBA32F, RGBA32I, RGBA32UI, RGBA16, RGBA16F, RGBA16I, + RGBA16UI, RGBA8, RGBA8I, RGBA8UI, SRGB8_ALPHA8, and + RGB10_A2. + - R11F_G11F_B10F. + - RG32F, RG32I, RG32UI, RG16, RG16F, RG16I, RG16UI, RG8, RG8I, + and RG8UI. + - R32F, R32I, R32UI, R16F, R16I, R16UI, R16, R8, R8I, and R8UI. + - ALPHA8 + + OpenGL 3.1, 3.2, 3.3 + Same as 3.0 except ALPHA8 requires GL_ARB_compatibility/compatibility profile. + OpengGL 3.3, 4.0, 4.1 + Adds RGB10_A2UI. + OpengGL 4.2 + Adds + - RGB5_A1, RGBA4 + - RGB565 + OpenGL 4.4 + Does away with the separate list and adds a column to the sized internal color format + table. However, no new formats become required color renderable. + + ES 2.0 + color renderable: RGBA4, RGB5_A1, RGB565 + GL_EXT_texture_rg adds support for R8, RG5 as a color render target + GL_OES_rgb8_rgba8 adds support for RGB8 and RGBA8 + GL_ARM_rgba8 adds support for RGBA8 (but not RGB8) + GL_EXT_texture_format_BGRA8888 does not add renderbuffer support + GL_CHROMIUM_renderbuffer_format_BGRA8888 adds BGRA8 as color-renderable + GL_APPLE_texture_format_BGRA8888 does not add renderbuffer support + + ES 3.0 + - RGBA32I, RGBA32UI, RGBA16I, RGBA16UI, RGBA8, RGBA8I, + RGBA8UI, SRGB8_ALPHA8, RGB10_A2, RGB10_A2UI, RGBA4, and + RGB5_A1. + - RGB8 and RGB565. + - RG32I, RG32UI, RG16I, RG16UI, RG8, RG8I, and RG8UI. + - R32I, R32UI, R16I, R16UI, R8, R8I, and R8UI + ES 3.1 + Adds RGB10_A2, RGB10_A2UI, + ES 3.2 + Adds R16F, RG16F, RGBA16F, R32F, RG32F, RGBA32F, R11F_G11F_B10F. + */ + uint32_t allRenderFlags = ConfigInfo::kRenderable_Flag; + if (kNone_MSFBOType != fMSFBOType) { + allRenderFlags |= ConfigInfo::kRenderableWithMSAA_Flag; + } + GrGLStandard standard = ctxInfo.standard(); GrGLVersion version = ctxInfo.version(); - glslCaps->fMustSwizzleInShader = true; + fConfigTable[kUnknown_GrPixelConfig].fFormats.fBaseInternalFormat = 0; + fConfigTable[kUnknown_GrPixelConfig].fFormats.fSizedInternalFormat = 0; + fConfigTable[kUnknown_GrPixelConfig].fFormats.fExternalFormat[kOther_ExternalFormatUsage] = 0; + fConfigTable[kUnknown_GrPixelConfig].fFormats.fExternalType = 0; + fConfigTable[kUnknown_GrPixelConfig].fFormatType = kNormalizedFixedPoint_FormatType; + fConfigTable[kUnknown_GrPixelConfig].fSwizzle = GrSwizzle::RGBA(); + + fConfigTable[kRGBA_8888_GrPixelConfig].fFormats.fBaseInternalFormat = GR_GL_RGBA; + fConfigTable[kRGBA_8888_GrPixelConfig].fFormats.fSizedInternalFormat = GR_GL_RGBA8; + fConfigTable[kRGBA_8888_GrPixelConfig].fFormats.fExternalFormat[kOther_ExternalFormatUsage] = + GR_GL_RGBA; + fConfigTable[kRGBA_8888_GrPixelConfig].fFormats.fExternalType = GR_GL_UNSIGNED_BYTE; + fConfigTable[kRGBA_8888_GrPixelConfig].fFormatType = kNormalizedFixedPoint_FormatType; + fConfigTable[kRGBA_8888_GrPixelConfig].fFlags = ConfigInfo::kTextureable_Flag; if (kGL_GrGLStandard == standard) { - if (version >= GR_GL_VER(3,3) || ctxInfo.hasExtension("GL_ARB_texture_swizzle")) { - glslCaps->fMustSwizzleInShader = false; + // We require some form of FBO support and all GLs with FBO support can render to RGBA8 + fConfigTable[kRGBA_8888_GrPixelConfig].fFlags |= allRenderFlags; + } else { + if (version >= GR_GL_VER(3,0) || ctxInfo.hasExtension("GL_OES_rgb8_rgba8") || + ctxInfo.hasExtension("GL_ARM_rgba8")) { + fConfigTable[kRGBA_8888_GrPixelConfig].fFlags |= allRenderFlags; + } + } + fConfigTable[kRGBA_8888_GrPixelConfig].fSwizzle = GrSwizzle::RGBA(); + + fConfigTable[kBGRA_8888_GrPixelConfig].fFormats.fExternalFormat[kOther_ExternalFormatUsage] = + GR_GL_BGRA; + fConfigTable[kBGRA_8888_GrPixelConfig].fFormats.fExternalType = GR_GL_UNSIGNED_BYTE; + fConfigTable[kBGRA_8888_GrPixelConfig].fFormatType = kNormalizedFixedPoint_FormatType; + if (kGL_GrGLStandard == standard) { + fConfigTable[kBGRA_8888_GrPixelConfig].fFormats.fBaseInternalFormat = GR_GL_RGBA; + fConfigTable[kBGRA_8888_GrPixelConfig].fFormats.fSizedInternalFormat = GR_GL_RGBA8; + if (version >= GR_GL_VER(1, 2) || ctxInfo.hasExtension("GL_EXT_bgra")) { + // Since the internal format is RGBA8, it is also renderable. + fConfigTable[kBGRA_8888_GrPixelConfig].fFlags = ConfigInfo::kTextureable_Flag | + allRenderFlags; } } else { - if (version >= GR_GL_VER(3,0)) { - glslCaps->fMustSwizzleInShader = false; + fConfigTable[kBGRA_8888_GrPixelConfig].fFormats.fBaseInternalFormat = GR_GL_BGRA; + fConfigTable[kBGRA_8888_GrPixelConfig].fFormats.fSizedInternalFormat = GR_GL_BGRA8; + if (ctxInfo.hasExtension("GL_APPLE_texture_format_BGRA8888")) { + // The APPLE extension doesn't make this renderable. + fConfigTable[kBGRA_8888_GrPixelConfig].fFlags = ConfigInfo::kTextureable_Flag; + if (version < GR_GL_VER(3,0) && !ctxInfo.hasExtension("GL_EXT_texture_storage")) { + // On ES2 the internal format of a BGRA texture is RGBA with the APPLE extension. + // Though, that seems to not be the case if the texture storage extension is + // present. The specs don't exactly make that clear. + fConfigTable[kBGRA_8888_GrPixelConfig].fFormats.fBaseInternalFormat = GR_GL_RGBA; + fConfigTable[kBGRA_8888_GrPixelConfig].fFormats.fSizedInternalFormat = GR_GL_RGBA8; + } + } else if (ctxInfo.hasExtension("GL_EXT_texture_format_BGRA8888")) { + fConfigTable[kBGRA_8888_GrPixelConfig].fFlags = ConfigInfo::kTextureable_Flag | + ConfigInfo::kRenderable_Flag; + if (ctxInfo.hasExtension("GL_CHROMIUM_renderbuffer_format_BGRA8888") && + this->usesMSAARenderBuffers()) { + fConfigTable[kBGRA_8888_GrPixelConfig].fFlags |= + ConfigInfo::kRenderableWithMSAA_Flag; + } + } + } + fConfigTable[kBGRA_8888_GrPixelConfig].fSwizzle = GrSwizzle::RGBA(); + + // We only enable srgb support if both textures and FBOs support srgb. + bool srgbSupport = false; + if (kGL_GrGLStandard == standard) { + if (ctxInfo.version() >= GR_GL_VER(3,0)) { + srgbSupport = true; + } else if (ctxInfo.hasExtension("GL_EXT_texture_sRGB")) { + if (ctxInfo.hasExtension("GL_ARB_framebuffer_sRGB") || + ctxInfo.hasExtension("GL_EXT_framebuffer_sRGB")) { + srgbSupport = true; + } + } + // All the above srgb extensions support toggling srgb writes + fSRGBWriteControl = srgbSupport; + } else { + // See https://bug.skia.org/4148 for PowerVR issue. + srgbSupport = kPowerVRRogue_GrGLRenderer != ctxInfo.renderer() && + (ctxInfo.version() >= GR_GL_VER(3,0) || ctxInfo.hasExtension("GL_EXT_sRGB")); + // ES through 3.1 requires EXT_srgb_write_control to support toggling + // sRGB writing for destinations. + fSRGBWriteControl = ctxInfo.hasExtension("GL_EXT_sRGB_write_control"); + } + fConfigTable[kSRGBA_8888_GrPixelConfig].fFormats.fBaseInternalFormat = GR_GL_SRGB_ALPHA; + fConfigTable[kSRGBA_8888_GrPixelConfig].fFormats.fSizedInternalFormat = GR_GL_SRGB8_ALPHA8; + // GL does not do srgb<->rgb conversions when transferring between cpu and gpu. Thus, the + // external format is GL_RGBA. See below for note about ES2.0 and glTex[Sub]Image. + fConfigTable[kSRGBA_8888_GrPixelConfig].fFormats.fExternalFormat[kOther_ExternalFormatUsage] = + GR_GL_RGBA; + fConfigTable[kSRGBA_8888_GrPixelConfig].fFormats.fExternalType = GR_GL_UNSIGNED_BYTE; + fConfigTable[kSRGBA_8888_GrPixelConfig].fFormatType = kNormalizedFixedPoint_FormatType; + if (srgbSupport) { + fConfigTable[kSRGBA_8888_GrPixelConfig].fFlags = ConfigInfo::kTextureable_Flag | + allRenderFlags; + } + fConfigTable[kSRGBA_8888_GrPixelConfig].fSwizzle = GrSwizzle::RGBA(); + + fConfigTable[kRGB_565_GrPixelConfig].fFormats.fBaseInternalFormat = GR_GL_RGB; + if (this->ES2CompatibilitySupport()) { + fConfigTable[kRGB_565_GrPixelConfig].fFormats.fSizedInternalFormat = GR_GL_RGB565; + } else { + fConfigTable[kRGB_565_GrPixelConfig].fFormats.fSizedInternalFormat = GR_GL_RGB5; + } + fConfigTable[kRGB_565_GrPixelConfig].fFormats.fExternalFormat[kOther_ExternalFormatUsage] = + GR_GL_RGB; + fConfigTable[kRGB_565_GrPixelConfig].fFormats.fExternalType = GR_GL_UNSIGNED_SHORT_5_6_5; + fConfigTable[kRGB_565_GrPixelConfig].fFormatType = kNormalizedFixedPoint_FormatType; + fConfigTable[kRGB_565_GrPixelConfig].fFlags = ConfigInfo::kTextureable_Flag; + if (kGL_GrGLStandard == standard) { + if (version >= GR_GL_VER(4, 2) || ctxInfo.hasExtension("GL_ES2_compatibility")) { + fConfigTable[kRGB_565_GrPixelConfig].fFlags |= allRenderFlags; + } + } else { + fConfigTable[kRGB_565_GrPixelConfig].fFlags |= allRenderFlags; + } + fConfigTable[kRGB_565_GrPixelConfig].fSwizzle = GrSwizzle::RGBA(); + + fConfigTable[kRGBA_4444_GrPixelConfig].fFormats.fBaseInternalFormat = GR_GL_RGBA; + fConfigTable[kRGBA_4444_GrPixelConfig].fFormats.fSizedInternalFormat = GR_GL_RGBA4; + fConfigTable[kRGBA_4444_GrPixelConfig].fFormats.fExternalFormat[kOther_ExternalFormatUsage] = + GR_GL_RGBA; + fConfigTable[kRGBA_4444_GrPixelConfig].fFormats.fExternalType = GR_GL_UNSIGNED_SHORT_4_4_4_4; + fConfigTable[kRGBA_4444_GrPixelConfig].fFormatType = kNormalizedFixedPoint_FormatType; + fConfigTable[kRGBA_4444_GrPixelConfig].fFlags = ConfigInfo::kTextureable_Flag; + if (kGL_GrGLStandard == standard) { + if (version >= GR_GL_VER(4, 2)) { + fConfigTable[kRGBA_4444_GrPixelConfig].fFlags |= allRenderFlags; + } + } else { + fConfigTable[kRGBA_4444_GrPixelConfig].fFlags |= allRenderFlags; + } + fConfigTable[kRGBA_4444_GrPixelConfig].fSwizzle = GrSwizzle::RGBA(); + + if (this->textureRedSupport()) { + fConfigTable[kAlpha_8_GrPixelConfig].fFormats.fBaseInternalFormat = GR_GL_RED; + fConfigTable[kAlpha_8_GrPixelConfig].fFormats.fSizedInternalFormat = GR_GL_R8; + fConfigTable[kAlpha_8_GrPixelConfig].fFormats.fExternalFormat[kOther_ExternalFormatUsage] = + GR_GL_RED; + fConfigTable[kAlpha_8_GrPixelConfig].fSwizzle = GrSwizzle::RRRR(); + } else { + fConfigTable[kAlpha_8_GrPixelConfig].fFormats.fBaseInternalFormat = GR_GL_ALPHA; + fConfigTable[kAlpha_8_GrPixelConfig].fFormats.fSizedInternalFormat = GR_GL_ALPHA8; + fConfigTable[kAlpha_8_GrPixelConfig].fFormats.fExternalFormat[kOther_ExternalFormatUsage] = + GR_GL_ALPHA; + fConfigTable[kAlpha_8_GrPixelConfig].fSwizzle = GrSwizzle::AAAA(); + } + fConfigTable[kAlpha_8_GrPixelConfig].fFormats.fExternalType = GR_GL_UNSIGNED_BYTE; + fConfigTable[kAlpha_8_GrPixelConfig].fFormatType = kNormalizedFixedPoint_FormatType; + fConfigTable[kAlpha_8_GrPixelConfig].fFlags = ConfigInfo::kTextureable_Flag; + if (this->textureRedSupport() || kDesktop_ARB_MSFBOType == this->msFBOType()) { + // desktop ARB extension/3.0+ supports ALPHA8 as renderable. + // Core profile removes ALPHA8 support, but we should have chosen R8 in that case. + fConfigTable[kAlpha_8_GrPixelConfig].fFlags |= allRenderFlags; + } + + // Check for [half] floating point texture support + // NOTE: We disallow floating point textures on ES devices if linear filtering modes are not + // supported. This is for simplicity, but a more granular approach is possible. Coincidentally, + // [half] floating point textures became part of the standard in ES3.1 / OGL 3.0. + bool hasFPTextures = false; + bool hasHalfFPTextures = false; + // for now we don't support floating point MSAA on ES + uint32_t fpRenderFlags = (kGL_GrGLStandard == standard) ? + allRenderFlags : (uint32_t)ConfigInfo::kRenderable_Flag; + + if (kGL_GrGLStandard == standard) { + if (version >= GR_GL_VER(3, 0) || ctxInfo.hasExtension("GL_ARB_texture_float")) { + hasFPTextures = true; + hasHalfFPTextures = true; + } + } else { + if (version >= GR_GL_VER(3, 1)) { + hasFPTextures = true; + hasHalfFPTextures = true; + } else { + if (ctxInfo.hasExtension("GL_OES_texture_float_linear") && + ctxInfo.hasExtension("GL_OES_texture_float")) { + hasFPTextures = true; + } + if (ctxInfo.hasExtension("GL_OES_texture_half_float_linear") && + ctxInfo.hasExtension("GL_OES_texture_half_float")) { + hasHalfFPTextures = true; + } } } - glslCaps->fConfigSwizzle[kUnknown_GrPixelConfig] = nullptr; - if (fTextureRedSupport) { - glslCaps->fConfigSwizzle[kAlpha_8_GrPixelConfig] = "rrrr"; - glslCaps->fConfigSwizzle[kAlpha_half_GrPixelConfig] = "rrrr"; - } else { - glslCaps->fConfigSwizzle[kAlpha_8_GrPixelConfig] = "aaaa"; - glslCaps->fConfigSwizzle[kAlpha_half_GrPixelConfig] = "aaaa"; + fConfigTable[kRGBA_float_GrPixelConfig].fFormats.fBaseInternalFormat = GR_GL_RGBA; + fConfigTable[kRGBA_float_GrPixelConfig].fFormats.fSizedInternalFormat = GR_GL_RGBA32F; + fConfigTable[kRGBA_float_GrPixelConfig].fFormats.fExternalFormat[kOther_ExternalFormatUsage] = + GR_GL_RGBA; + fConfigTable[kRGBA_float_GrPixelConfig].fFormats.fExternalType = GR_GL_FLOAT; + fConfigTable[kRGBA_float_GrPixelConfig].fFormatType = kFloat_FormatType; + if (hasFPTextures) { + fConfigTable[kRGBA_float_GrPixelConfig].fFlags = ConfigInfo::kTextureable_Flag; + // For now we only enable rendering to float on desktop, because on ES we'd have to solve + // many precision issues and no clients actually want this yet. + if (kGL_GrGLStandard == standard /* || version >= GR_GL_VER(3,2) || + ctxInfo.hasExtension("GL_EXT_color_buffer_float")*/) { + fConfigTable[kRGBA_float_GrPixelConfig].fFlags |= fpRenderFlags; + } } - glslCaps->fConfigSwizzle[kIndex_8_GrPixelConfig] = "rgba"; - glslCaps->fConfigSwizzle[kRGB_565_GrPixelConfig] = "rgba"; - glslCaps->fConfigSwizzle[kRGBA_4444_GrPixelConfig] = "rgba"; - glslCaps->fConfigSwizzle[kRGBA_8888_GrPixelConfig] = "rgba"; - glslCaps->fConfigSwizzle[kBGRA_8888_GrPixelConfig] = "rgba"; - glslCaps->fConfigSwizzle[kSRGBA_8888_GrPixelConfig] = "rgba"; - glslCaps->fConfigSwizzle[kETC1_GrPixelConfig] = "rgba"; - glslCaps->fConfigSwizzle[kLATC_GrPixelConfig] = "rrrr"; - glslCaps->fConfigSwizzle[kR11_EAC_GrPixelConfig] = "rrrr"; - glslCaps->fConfigSwizzle[kASTC_12x12_GrPixelConfig] = "rgba"; - glslCaps->fConfigSwizzle[kRGBA_float_GrPixelConfig] = "rgba"; - glslCaps->fConfigSwizzle[kRGBA_half_GrPixelConfig] = "rgba"; + fConfigTable[kRGBA_float_GrPixelConfig].fSwizzle = GrSwizzle::RGBA(); + if (this->textureRedSupport()) { + fConfigTable[kAlpha_half_GrPixelConfig].fFormats.fBaseInternalFormat = GR_GL_RED; + fConfigTable[kAlpha_half_GrPixelConfig].fFormats.fSizedInternalFormat = GR_GL_R16F; + fConfigTable[kAlpha_half_GrPixelConfig].fFormats.fExternalFormat[kOther_ExternalFormatUsage] + = GR_GL_RED; + fConfigTable[kAlpha_half_GrPixelConfig].fSwizzle = GrSwizzle::RRRR(); + } else { + fConfigTable[kAlpha_half_GrPixelConfig].fFormats.fBaseInternalFormat = GR_GL_ALPHA; + fConfigTable[kAlpha_half_GrPixelConfig].fFormats.fSizedInternalFormat = GR_GL_ALPHA16F; + fConfigTable[kAlpha_half_GrPixelConfig].fFormats.fExternalFormat[kOther_ExternalFormatUsage] + = GR_GL_ALPHA; + fConfigTable[kAlpha_half_GrPixelConfig].fSwizzle = GrSwizzle::AAAA(); + } + if (kGL_GrGLStandard == ctxInfo.standard() || ctxInfo.version() >= GR_GL_VER(3, 0)) { + fConfigTable[kAlpha_half_GrPixelConfig].fFormats.fExternalType = GR_GL_HALF_FLOAT; + } else { + fConfigTable[kAlpha_half_GrPixelConfig].fFormats.fExternalType = GR_GL_HALF_FLOAT_OES; + } + fConfigTable[kAlpha_half_GrPixelConfig].fFormatType = kFloat_FormatType; + if (hasHalfFPTextures) { + fConfigTable[kAlpha_half_GrPixelConfig].fFlags = ConfigInfo::kTextureable_Flag; + // ES requires either 3.2 or the combination of EXT_color_buffer_half_float and support for + // GL_RED internal format. + if (kGL_GrGLStandard == standard || version >= GR_GL_VER(3,2) || + (this->textureRedSupport() && + ctxInfo.hasExtension("GL_EXT_color_buffer_half_float"))) { + fConfigTable[kAlpha_half_GrPixelConfig].fFlags |= fpRenderFlags; + } + } + + fConfigTable[kRGBA_half_GrPixelConfig].fFormats.fBaseInternalFormat = GR_GL_RGBA; + fConfigTable[kRGBA_half_GrPixelConfig].fFormats.fSizedInternalFormat = GR_GL_RGBA16F; + fConfigTable[kRGBA_half_GrPixelConfig].fFormats.fExternalFormat[kOther_ExternalFormatUsage] = + GR_GL_RGBA; + if (kGL_GrGLStandard == ctxInfo.standard() || ctxInfo.version() >= GR_GL_VER(3, 0)) { + fConfigTable[kRGBA_half_GrPixelConfig].fFormats.fExternalType = GR_GL_HALF_FLOAT; + } else { + fConfigTable[kRGBA_half_GrPixelConfig].fFormats.fExternalType = GR_GL_HALF_FLOAT_OES; + } + fConfigTable[kRGBA_half_GrPixelConfig].fFormatType = kFloat_FormatType; + if (hasHalfFPTextures) { + fConfigTable[kRGBA_half_GrPixelConfig].fFlags = ConfigInfo::kTextureable_Flag; + // ES requires 3.2 or EXT_color_buffer_half_float. + if (kGL_GrGLStandard == standard || version >= GR_GL_VER(3,2) || + ctxInfo.hasExtension("GL_EXT_color_buffer_half_float")) { + fConfigTable[kRGBA_half_GrPixelConfig].fFlags |= fpRenderFlags; + } + } + fConfigTable[kRGBA_half_GrPixelConfig].fSwizzle = GrSwizzle::RGBA(); + + // Compressed texture support + + // glCompressedTexImage2D is available on all OpenGL ES devices. It is available on standard + // OpenGL after version 1.3. We'll assume at least that level of OpenGL support. + + // TODO: Fix command buffer bindings and remove this. + fCompressedTexSubImageSupport = SkToBool(gli->fFunctions.fCompressedTexSubImage2D); + + // No sized/unsized internal format distinction for compressed formats, no external format. + // Below we set the external formats and types to 0. + + fConfigTable[kIndex_8_GrPixelConfig].fFormats.fBaseInternalFormat = GR_GL_PALETTE8_RGBA8; + fConfigTable[kIndex_8_GrPixelConfig].fFormats.fSizedInternalFormat = GR_GL_PALETTE8_RGBA8; + fConfigTable[kIndex_8_GrPixelConfig].fFormats.fExternalFormat[kOther_ExternalFormatUsage] = 0; + fConfigTable[kIndex_8_GrPixelConfig].fFormats.fExternalType = 0; + fConfigTable[kIndex_8_GrPixelConfig].fFormatType = kNormalizedFixedPoint_FormatType; + // Disable this for now, while we investigate https://bug.skia.org/4333 + if (false) { + // Check for 8-bit palette.. + GrGLint numFormats; + GR_GL_GetIntegerv(gli, GR_GL_NUM_COMPRESSED_TEXTURE_FORMATS, &numFormats); + if (numFormats) { + SkAutoSTMalloc<10, GrGLint> formats(numFormats); + GR_GL_GetIntegerv(gli, GR_GL_COMPRESSED_TEXTURE_FORMATS, formats); + for (int i = 0; i < numFormats; ++i) { + if (GR_GL_PALETTE8_RGBA8 == formats[i]) { + fConfigTable[kIndex_8_GrPixelConfig].fFlags = ConfigInfo::kTextureable_Flag; + break; + } + } + } + } + fConfigTable[kIndex_8_GrPixelConfig].fSwizzle = GrSwizzle::RGBA(); + + // May change the internal format based on extensions. + fConfigTable[kLATC_GrPixelConfig].fFormats.fBaseInternalFormat = + GR_GL_COMPRESSED_LUMINANCE_LATC1; + fConfigTable[kLATC_GrPixelConfig].fFormats.fSizedInternalFormat = + GR_GL_COMPRESSED_LUMINANCE_LATC1; + if (ctxInfo.hasExtension("GL_EXT_texture_compression_latc") || + ctxInfo.hasExtension("GL_NV_texture_compression_latc")) { + fConfigTable[kLATC_GrPixelConfig].fFlags = ConfigInfo::kTextureable_Flag; + } else if ((kGL_GrGLStandard == standard && version >= GR_GL_VER(3, 0)) || + ctxInfo.hasExtension("GL_EXT_texture_compression_rgtc") || + ctxInfo.hasExtension("GL_ARB_texture_compression_rgtc")) { + // RGTC is identical and available on OpenGL 3.0+ as well as with extensions + fConfigTable[kLATC_GrPixelConfig].fFlags = ConfigInfo::kTextureable_Flag; + fConfigTable[kLATC_GrPixelConfig].fFormats.fBaseInternalFormat = + GR_GL_COMPRESSED_RED_RGTC1; + fConfigTable[kLATC_GrPixelConfig].fFormats.fSizedInternalFormat = + GR_GL_COMPRESSED_RED_RGTC1; + } else if (ctxInfo.hasExtension("GL_AMD_compressed_3DC_texture")) { + fConfigTable[kLATC_GrPixelConfig].fFlags = ConfigInfo::kTextureable_Flag; + fConfigTable[kLATC_GrPixelConfig].fFormats.fBaseInternalFormat = GR_GL_COMPRESSED_3DC_X; + fConfigTable[kLATC_GrPixelConfig].fFormats.fSizedInternalFormat = + GR_GL_COMPRESSED_3DC_X; + + } + fConfigTable[kLATC_GrPixelConfig].fFormats.fExternalFormat[kOther_ExternalFormatUsage] = 0; + fConfigTable[kLATC_GrPixelConfig].fFormats.fExternalType = 0; + fConfigTable[kLATC_GrPixelConfig].fFormatType = kNormalizedFixedPoint_FormatType; + fConfigTable[kLATC_GrPixelConfig].fSwizzle = GrSwizzle::RRRR(); + + fConfigTable[kETC1_GrPixelConfig].fFormats.fBaseInternalFormat = GR_GL_COMPRESSED_ETC1_RGB8; + fConfigTable[kETC1_GrPixelConfig].fFormats.fSizedInternalFormat = GR_GL_COMPRESSED_ETC1_RGB8; + fConfigTable[kETC1_GrPixelConfig].fFormats.fExternalFormat[kOther_ExternalFormatUsage] = 0; + fConfigTable[kETC1_GrPixelConfig].fFormats.fExternalType = 0; + fConfigTable[kETC1_GrPixelConfig].fFormatType = kNormalizedFixedPoint_FormatType; + if (kGL_GrGLStandard == standard) { + if (version >= GR_GL_VER(4, 3) || ctxInfo.hasExtension("GL_ARB_ES3_compatibility")) { + fConfigTable[kETC1_GrPixelConfig].fFlags = ConfigInfo::kTextureable_Flag; + } + } else { + if (version >= GR_GL_VER(3, 0) || + ctxInfo.hasExtension("GL_OES_compressed_ETC1_RGB8_texture") || + // ETC2 is a superset of ETC1, so we can just check for that, too. + (ctxInfo.hasExtension("GL_OES_compressed_ETC2_RGB8_texture") && + ctxInfo.hasExtension("GL_OES_compressed_ETC2_RGBA8_texture"))) { + fConfigTable[kETC1_GrPixelConfig].fFlags = ConfigInfo::kTextureable_Flag; + } + } + fConfigTable[kETC1_GrPixelConfig].fSwizzle = GrSwizzle::RGBA(); + + fConfigTable[kR11_EAC_GrPixelConfig].fFormats.fBaseInternalFormat = GR_GL_COMPRESSED_R11_EAC; + fConfigTable[kR11_EAC_GrPixelConfig].fFormats.fSizedInternalFormat = GR_GL_COMPRESSED_R11_EAC; + fConfigTable[kR11_EAC_GrPixelConfig].fFormats.fExternalFormat[kOther_ExternalFormatUsage] = 0; + fConfigTable[kR11_EAC_GrPixelConfig].fFormats.fExternalType = 0; + fConfigTable[kR11_EAC_GrPixelConfig].fFormatType = kNormalizedFixedPoint_FormatType; + // Check for R11_EAC. We don't support R11_EAC on desktop, as most cards default to + // decompressing the textures in the driver, and is generally slower. + if (kGLES_GrGLStandard == standard && version >= GR_GL_VER(3,0)) { + fConfigTable[kR11_EAC_GrPixelConfig].fFlags = ConfigInfo::kTextureable_Flag; + } + fConfigTable[kR11_EAC_GrPixelConfig].fSwizzle = GrSwizzle::RRRR(); + + fConfigTable[kASTC_12x12_GrPixelConfig].fFormats.fBaseInternalFormat = + GR_GL_COMPRESSED_RGBA_ASTC_12x12; + fConfigTable[kASTC_12x12_GrPixelConfig].fFormats.fSizedInternalFormat = + GR_GL_COMPRESSED_RGBA_ASTC_12x12; + fConfigTable[kASTC_12x12_GrPixelConfig].fFormats.fExternalFormat[kOther_ExternalFormatUsage] = + 0; + fConfigTable[kASTC_12x12_GrPixelConfig].fFormats.fExternalType = 0; + fConfigTable[kASTC_12x12_GrPixelConfig].fFormatType = kNormalizedFixedPoint_FormatType; + if (ctxInfo.hasExtension("GL_KHR_texture_compression_astc_hdr") || + ctxInfo.hasExtension("GL_KHR_texture_compression_astc_ldr") || + ctxInfo.hasExtension("GL_OES_texture_compression_astc")) { + fConfigTable[kASTC_12x12_GrPixelConfig].fFlags = ConfigInfo::kTextureable_Flag; + } + fConfigTable[kASTC_12x12_GrPixelConfig].fSwizzle = GrSwizzle::RGBA(); + + // Bulk populate the texture internal/external formats here and then deal with exceptions below. + + // ES 2.0 requires that the internal/external formats match. + bool useSizedTexFormats = (kGL_GrGLStandard == ctxInfo.standard() || + ctxInfo.version() >= GR_GL_VER(3,0)); + // All ES versions (thus far) require sized internal formats for render buffers. + // TODO: Always use sized internal format? + bool useSizedRbFormats = kGLES_GrGLStandard == ctxInfo.standard(); + + for (int i = 0; i < kGrPixelConfigCnt; ++i) { + // Almost always we want to pass fExternalFormat[kOther_ExternalFormatUsage] as the + // param to glTex[Sub]Image. + fConfigTable[i].fFormats.fExternalFormat[kTexImage_ExternalFormatUsage] = + fConfigTable[i].fFormats.fExternalFormat[kOther_ExternalFormatUsage]; + fConfigTable[i].fFormats.fInternalFormatTexImage = useSizedTexFormats ? + fConfigTable[i].fFormats.fSizedInternalFormat : + fConfigTable[i].fFormats.fBaseInternalFormat; + fConfigTable[i].fFormats.fInternalFormatRenderbuffer = useSizedRbFormats ? + fConfigTable[i].fFormats.fSizedInternalFormat : + fConfigTable[i].fFormats.fBaseInternalFormat; + } + // OpenGL ES 2.0 + GL_EXT_sRGB allows GL_SRGB_ALPHA to be specified as the + // param to Tex(Sub)Image. ES 2.0 requires the and params to match. + // Thus, on ES 2.0 we will use GL_SRGB_ALPHA as the param. + // On OpenGL and ES 3.0+ GL_SRGB_ALPHA does not work for the param to glTexImage. + if (ctxInfo.standard() == kGLES_GrGLStandard && ctxInfo.version() == GR_GL_VER(2,0)) { + fConfigTable[kSRGBA_8888_GrPixelConfig].fFormats.fExternalFormat[kTexImage_ExternalFormatUsage] = + GR_GL_SRGB_ALPHA; + } + + // If BGRA is supported as an internal format it must always be specified to glTex[Sub]Image + // as a base format. + // GL_EXT_texture_format_BGRA8888: + // This extension GL_BGRA as an unsized internal format. However, it is written against ES + // 2.0 and therefore doesn't define a value for GL_BGRA8 as ES 2.0 uses unsized internal + // formats. + // GL_APPLE_texture_format_BGRA8888: + // ES 2.0: the extension makes BGRA an external format but not an internal format. + // ES 3.0: the extension explicitly states GL_BGRA8 is not a valid internal format for + // glTexImage (just for glTexStorage). + if (useSizedTexFormats && this->bgraIsInternalFormat()) { + fConfigTable[kBGRA_8888_GrPixelConfig].fFormats.fInternalFormatTexImage = GR_GL_BGRA; + } + + // If we don't have texture swizzle support then the shader generator must insert the + // swizzle into shader code. + if (!this->textureSwizzleSupport()) { + for (int i = 0; i < kGrPixelConfigCnt; ++i) { + glslCaps->fConfigTextureSwizzle[i] = fConfigTable[i].fSwizzle; + } + } + + // Shader output swizzles will default to RGBA. When we've use GL_RED instead of GL_ALPHA to + // implement kAlpha_8_GrPixelConfig we need to swizzle the shader outputs so the alpha channel + // gets written to the single component. + if (this->textureRedSupport()) { + for (int i = 0; i < kGrPixelConfigCnt; ++i) { + GrPixelConfig config = static_cast(i); + if (GrPixelConfigIsAlphaOnly(config) && + fConfigTable[i].fFormats.fBaseInternalFormat == GR_GL_RED) { + glslCaps->fConfigOutputSwizzle[i] = GrSwizzle::AAAA(); + } + } + } + +#ifdef SK_DEBUG + // Make sure we initialized everything. + ConfigInfo defaultEntry; + for (int i = 0; i < kGrPixelConfigCnt; ++i) { + SkASSERT(defaultEntry.fFormats.fBaseInternalFormat != + fConfigTable[i].fFormats.fBaseInternalFormat); + SkASSERT(defaultEntry.fFormats.fSizedInternalFormat != + fConfigTable[i].fFormats.fSizedInternalFormat); + for (int j = 0; j < kExternalFormatUsageCnt; ++j) { + SkASSERT(defaultEntry.fFormats.fExternalFormat[j] != + fConfigTable[i].fFormats.fExternalFormat[j]); + } + SkASSERT(defaultEntry.fFormats.fExternalType != fConfigTable[i].fFormats.fExternalType); + } +#endif } void GrGLCaps::onApplyOptionsOverrides(const GrContextOptions& options) {} - - diff --git a/gfx/skia/skia/src/gpu/gl/GrGLCaps.h b/gfx/skia/skia/src/gpu/gl/GrGLCaps.h index 0ec6c85b47..c4f42701f1 100644 --- a/gfx/skia/skia/src/gpu/gl/GrGLCaps.h +++ b/gfx/skia/skia/src/gpu/gl/GrGLCaps.h @@ -9,9 +9,10 @@ #ifndef GrGLCaps_DEFINED #define GrGLCaps_DEFINED -#include "GrCaps.h" #include "glsl/GrGLSL.h" +#include "GrCaps.h" #include "GrGLStencilAttachment.h" +#include "GrSwizzle.h" #include "SkChecksum.h" #include "SkTHash.h" #include "SkTArray.h" @@ -90,6 +91,14 @@ public: kLast_MapBufferType = kChromium_MapBufferType, }; + enum TransferBufferType { + kNone_TransferBufferType, + kPBO_TransferBufferType, // ARB_pixel_buffer_object + kChromium_TransferBufferType, // CHROMIUM_pixel_transfer_buffer_object + + kLast_TransferBufferType = kChromium_TransferBufferType, + }; + /** * Initializes the GrGLCaps to the set of features supported in the current * OpenGL context accessible via ctxInfo. @@ -97,13 +106,83 @@ public: GrGLCaps(const GrContextOptions& contextOptions, const GrGLContextInfo& ctxInfo, const GrGLInterface* glInterface); + bool isConfigTexturable(GrPixelConfig config) const override { + SkASSERT(kGrPixelConfigCnt > config); + return SkToBool(fConfigTable[config].fFlags & ConfigInfo::kTextureable_Flag); + } + + bool isConfigRenderable(GrPixelConfig config, bool withMSAA) const override { + SkASSERT(kGrPixelConfigCnt > config); + if (withMSAA) { + return SkToBool(fConfigTable[config].fFlags & ConfigInfo::kRenderableWithMSAA_Flag); + } else { + return SkToBool(fConfigTable[config].fFlags & ConfigInfo::kRenderable_Flag); + } + } + + /** Returns the mapping between GrPixelConfig components and GL internal format components. */ + const GrSwizzle& configSwizzle(GrPixelConfig config) const { + return fConfigTable[config].fSwizzle; + } + + bool getTexImageFormats(GrPixelConfig surfaceConfig, GrPixelConfig externalConfig, + GrGLenum* internalFormat, GrGLenum* externalFormat, + GrGLenum* externalType) const; + + bool getCompressedTexImageFormats(GrPixelConfig surfaceConfig, GrGLenum* internalFormat) const; + + bool getReadPixelsFormat(GrPixelConfig surfaceConfig, GrPixelConfig externalConfig, + GrGLenum* externalFormat, GrGLenum* externalType) const; + + bool getRenderbufferFormat(GrPixelConfig config, GrGLenum* internalFormat) const; + + /** + * Gets an array of legal stencil formats. These formats are not guaranteed + * to be supported by the driver but are legal GLenum names given the GL + * version and extensions supported. + */ + const SkTArray& stencilFormats() const { + return fStencilFormats; + } + + /** + * Has a stencil format index been found for the config (or we've found that no format works). + */ + bool hasStencilFormatBeenDeterminedForConfig(GrPixelConfig config) const { + return fConfigTable[config].fStencilFormatIndex != ConfigInfo::kUnknown_StencilIndex; + } + + /** + * Gets the stencil format index for the config. This assumes + * hasStencilFormatBeenDeterminedForConfig has already been checked. Returns a value < 0 if + * no stencil format is supported with the config. Otherwise, returned index refers to the array + * returned by stencilFormats(). + */ + int getStencilFormatIndexForConfig(GrPixelConfig config) const { + SkASSERT(this->hasStencilFormatBeenDeterminedForConfig(config)); + return fConfigTable[config].fStencilFormatIndex; + } + + /** + * If index is >= 0 this records an index into stencilFormats() as the best stencil format for + * the config. If < 0 it records that the config has no supported stencil format index. + */ + void setStencilFormatIndexForConfig(GrPixelConfig config, int index) { + SkASSERT(!this->hasStencilFormatBeenDeterminedForConfig(config)); + if (index < 0) { + fConfigTable[config].fStencilFormatIndex = ConfigInfo::kUnsupported_StencilFormatIndex; + } else { + fConfigTable[config].fStencilFormatIndex = index; + } + } + /** * Call to note that a color config has been verified as a valid color * attachment. This may save future calls to glCheckFramebufferStatus * using isConfigVerifiedColorAttachment(). */ void markConfigAsValidColorAttachment(GrPixelConfig config) { - fVerifiedColorConfigs.markVerified(config); + fConfigTable[config].fFlags |= ConfigInfo::kVerifiedColorAttachment_Flag; } /** @@ -111,7 +190,7 @@ public: * attachment. */ bool isConfigVerifiedColorAttachment(GrPixelConfig config) const { - return fVerifiedColorConfigs.isVerified(config); + return SkToBool(fConfigTable[config].fFlags & ConfigInfo::kVerifiedColorAttachment_Flag); } /** @@ -143,14 +222,8 @@ public: /// What type of buffer mapping is supported? MapBufferType mapBufferType() const { return fMapBufferType; } - /** - * Gets an array of legal stencil formats. These formats are not guaranteed - * to be supported by the driver but are legal GLenum names given the GL - * version and extensions supported. - */ - const SkTArray& stencilFormats() const { - return fStencilFormats; - } + /// What type of transfer buffer is supported? + TransferBufferType transferBufferType() const { return fTransferBufferType; } /// The maximum number of fragment uniform vectors (GLES has min. 16). int maxFragmentUniformVectors() const { return fMaxFragmentUniformVectors; } @@ -161,15 +234,12 @@ public: /// maximum number of texture units accessible in the fragment shader. int maxFragmentTextureUnits() const { return fMaxFragmentTextureUnits; } - /// ES requires an extension to support RGBA8 in RenderBufferStorage - bool rgba8RenderbufferSupport() const { return fRGBA8RenderbufferSupport; } - /** * Depending on the ES extensions present the BGRA external format may - * correspond either a BGRA or RGBA internalFormat. On desktop GL it is + * correspond to either a BGRA or RGBA internalFormat. On desktop GL it is * RGBA. */ - bool bgraIsInternalFormat() const { return fBGRAIsInternalFormat; } + bool bgraIsInternalFormat() const; /// Is there support for GL_UNPACK_ROW_LENGTH bool unpackRowLengthSupport() const { return fUnpackRowLengthSupport; } @@ -198,9 +268,6 @@ public: /// Is there support for Vertex Array Objects? bool vertexArrayObjectSupport() const { return fVertexArrayObjectSupport; } - /// Is there support for glDraw*Instanced and glVertexAttribDivisor? - bool instancedDrawingSupport() const { return fInstancedDrawingSupport; } - /// Is there support for GL_EXT_direct_state_access? bool directStateAccessSupport() const { return fDirectStateAccessSupport; } @@ -211,20 +278,15 @@ public: bool ES2CompatibilitySupport() const { return fES2CompatibilitySupport; } /// Can we call glDisable(GL_MULTISAMPLE)? - bool multisampleDisableSupport() const { - return fMultisampleDisableSupport; - } + bool multisampleDisableSupport() const { return fMultisampleDisableSupport; } /// Use indices or vertices in CPU arrays rather than VBOs for dynamic content. - bool useNonVBOVertexAndIndexDynamicData() const { - return fUseNonVBOVertexAndIndexDynamicData; - } + bool useNonVBOVertexAndIndexDynamicData() const { return fUseNonVBOVertexAndIndexDynamicData; } /// Does ReadPixels support the provided format/type combo? bool readPixelsSupported(const GrGLInterface* intf, - GrGLenum format, - GrGLenum type, - GrGLenum currFboFormat) const; + GrPixelConfig readConfig, + GrPixelConfig currFBOConfig) const; bool isCoreProfile() const { return fIsCoreProfile; } @@ -235,6 +297,12 @@ public: /// Are textures with GL_TEXTURE_EXTERNAL_OES type supported. bool externalTextureSupport() const { return fExternalTextureSupport; } + /// Are textures with GL_TEXTURE_RECTANGLE type supported. + bool rectangleTextureSupport() const { return fRectangleTextureSupport; } + + /// GL_ARB_texture_swizzle + bool textureSwizzleSupport() const { return fTextureSwizzleSupport; } + /** * Is there support for enabling/disabling sRGB writes for sRGB-capable color attachments? * If false this does not mean sRGB is not supported but rather that if it is supported @@ -247,87 +315,39 @@ public: */ SkString dump() const override; - /** - * LATC can appear under one of three possible names. In order to know - * which GL internal format to use, we need to keep track of which name - * we found LATC under. The default is LATC. - */ - enum LATCAlias { - kLATC_LATCAlias, - kRGTC_LATCAlias, - k3DC_LATCAlias - }; - - LATCAlias latcAlias() const { return fLATCAlias; } - bool rgba8888PixelsOpsAreSlow() const { return fRGBA8888PixelsOpsAreSlow; } bool partialFBOReadIsSlow() const { return fPartialFBOReadIsSlow; } const GrGLSLCaps* glslCaps() const { return reinterpret_cast(fShaderCaps.get()); } private: + enum ExternalFormatUsage { + kTexImage_ExternalFormatUsage, + kOther_ExternalFormatUsage, + + kLast_ExternalFormatUsage = kOther_ExternalFormatUsage + }; + static const int kExternalFormatUsageCnt = kLast_ExternalFormatUsage + 1; + bool getExternalFormat(GrPixelConfig surfaceConfig, GrPixelConfig memoryConfig, + ExternalFormatUsage usage, GrGLenum* externalFormat, + GrGLenum* externalType) const; + void init(const GrContextOptions&, const GrGLContextInfo&, const GrGLInterface*); void initGLSL(const GrGLContextInfo&); bool hasPathRenderingSupport(const GrGLContextInfo&, const GrGLInterface*); void onApplyOptionsOverrides(const GrContextOptions& options) override; - /** - * Maintains a bit per GrPixelConfig. It is used to avoid redundantly - * performing glCheckFrameBufferStatus for the same config. - */ - struct VerifiedColorConfigs { - VerifiedColorConfigs() { - this->reset(); - } - - void reset() { - for (int i = 0; i < kNumUints; ++i) { - fVerifiedColorConfigs[i] = 0; - } - } - - static const int kNumUints = (kGrPixelConfigCnt + 31) / 32; - uint32_t fVerifiedColorConfigs[kNumUints]; - - void markVerified(GrPixelConfig config) { -#if !GR_GL_CHECK_FBO_STATUS_ONCE_PER_FORMAT - return; -#endif - int u32Idx = config / 32; - int bitIdx = config % 32; - fVerifiedColorConfigs[u32Idx] |= 1 << bitIdx; - } - - bool isVerified(GrPixelConfig config) const { -#if !GR_GL_CHECK_FBO_STATUS_ONCE_PER_FORMAT - return false; -#endif - int u32Idx = config / 32; - int bitIdx = config % 32; - return SkToBool(fVerifiedColorConfigs[u32Idx] & (1 << bitIdx)); - } - }; - void initFSAASupport(const GrGLContextInfo&, const GrGLInterface*); void initBlendEqationSupport(const GrGLContextInfo&); void initStencilFormats(const GrGLContextInfo&); // This must be called after initFSAASupport(). - void initConfigRenderableTable(const GrGLContextInfo&, bool srgbSupport); - void initConfigTexturableTable(const GrGLContextInfo&, const GrGLInterface*, bool srgbSupport); - - bool doReadPixelsSupported(const GrGLInterface* intf, GrGLenum format, GrGLenum type) const; + void initConfigTable(const GrGLContextInfo&, const GrGLInterface* gli, GrGLSLCaps* glslCaps); void initShaderPrecisionTable(const GrGLContextInfo& ctxInfo, const GrGLInterface* intf, GrGLSLCaps* glslCaps); - void initConfigSwizzleTable(const GrGLContextInfo& ctxInfo, GrGLSLCaps* glslCaps); - - // tracks configs that have been verified to pass the FBO completeness when - // used as a color attachment - VerifiedColorConfigs fVerifiedColorConfigs; - SkTArray fStencilFormats; int fMaxFragmentUniformVectors; @@ -337,10 +357,8 @@ private: MSFBOType fMSFBOType; InvalidateFBType fInvalidateFBType; MapBufferType fMapBufferType; - LATCAlias fLATCAlias; + TransferBufferType fTransferBufferType; - bool fRGBA8RenderbufferSupport : 1; - bool fBGRAIsInternalFormat : 1; bool fUnpackRowLengthSupport : 1; bool fUnpackFlipYSupport : 1; bool fPackRowLengthSupport : 1; @@ -349,9 +367,7 @@ private: bool fTexStorageSupport : 1; bool fTextureRedSupport : 1; bool fImagingSupport : 1; - bool fTwoFormatLimit : 1; bool fVertexArrayObjectSupport : 1; - bool fInstancedDrawingSupport : 1; bool fDirectStateAccessSupport : 1; bool fDebugSupport : 1; bool fES2CompatibilitySupport : 1; @@ -364,19 +380,78 @@ private: bool fPartialFBOReadIsSlow : 1; bool fBindUniformLocationSupport : 1; bool fExternalTextureSupport : 1; + bool fRectangleTextureSupport : 1; + bool fTextureSwizzleSupport : 1; - struct ReadPixelsSupportedFormat { + /** Number type of the components (with out considering number of bits.) */ + enum FormatType { + kNormalizedFixedPoint_FormatType, + kFloat_FormatType, + }; + + struct ReadPixelsFormat { + ReadPixelsFormat() : fFormat(0), fType(0) {} GrGLenum fFormat; GrGLenum fType; - GrGLenum fFboFormat; - - bool operator==(const ReadPixelsSupportedFormat& rhs) const { - return fFormat == rhs.fFormat - && fType == rhs.fType - && fFboFormat == rhs.fFboFormat; - } }; - mutable SkTHashMap fReadPixelsSupportedCache; + + struct ConfigFormats { + ConfigFormats() { + // Inits to known bad GL enum values. + memset(this, 0xAB, sizeof(ConfigFormats)); + } + GrGLenum fBaseInternalFormat; + GrGLenum fSizedInternalFormat; + + /** The external format and type are to be used when uploading/downloading data using this + config where both the CPU data and GrSurface are the same config. To get the external + format and type when converting between configs while copying to/from memory use + getExternalFormat(). + The kTexImage external format is usually the same as kOther except for kSRGBA on some + GL contexts. */ + GrGLenum fExternalFormat[kExternalFormatUsageCnt]; + GrGLenum fExternalType; + + + // Either the base or sized internal format depending on the GL and config. + GrGLenum fInternalFormatTexImage; + GrGLenum fInternalFormatRenderbuffer; + }; + + struct ConfigInfo { + ConfigInfo() : fStencilFormatIndex(kUnknown_StencilIndex), fFlags(0) {} + + ConfigFormats fFormats; + + FormatType fFormatType; + + // On ES contexts there are restrictions on type type/format that may be used for + // ReadPixels. One is implicitly specified by the current FBO's format. The other is + // queryable. This stores the queried option (lazily). + ReadPixelsFormat fSecondReadPixelsFormat; + + enum { + // This indicates that a stencil format has not yet been determined for the config. + kUnknown_StencilIndex = -1, + // This indicates that there is no supported stencil format for the config. + kUnsupported_StencilFormatIndex = -2 + }; + + // Index fStencilFormats. + int fStencilFormatIndex; + + enum { + kVerifiedColorAttachment_Flag = 0x1, + kTextureable_Flag = 0x2, + kRenderable_Flag = 0x4, + kRenderableWithMSAA_Flag = 0x8, + }; + uint32_t fFlags; + + GrSwizzle fSwizzle; + }; + + ConfigInfo fConfigTable[kGrPixelConfigCnt]; typedef GrCaps INHERITED; }; diff --git a/gfx/skia/skia/src/gpu/gl/GrGLDefines.h b/gfx/skia/skia/src/gpu/gl/GrGLDefines.h index dfab5e7e13..318fcd4d53 100644 --- a/gfx/skia/skia/src/gpu/gl/GrGLDefines.h +++ b/gfx/skia/skia/src/gpu/gl/GrGLDefines.h @@ -110,8 +110,14 @@ #define GR_GL_ELEMENT_ARRAY_BUFFER 0x8893 #define GR_GL_ARRAY_BUFFER_BINDING 0x8894 #define GR_GL_ELEMENT_ARRAY_BUFFER_BINDING 0x8895 +#define GR_GL_PIXEL_PACK_BUFFER 0x88EB +#define GR_GL_PIXEL_UNPACK_BUFFER 0x88EC + +#define GR_GL_PIXEL_UNPACK_TRANSFER_BUFFER_CHROMIUM 0x78EC +#define GR_GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM 0x78ED #define GR_GL_STREAM_DRAW 0x88E0 +#define GR_GL_STREAM_READ 0x88E1 #define GR_GL_STATIC_DRAW 0x88E4 #define GR_GL_DYNAMIC_DRAW 0x88E8 @@ -279,35 +285,35 @@ #define GR_GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT 0x8E8E #define GR_GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT 0x8E8F -#define GR_GL_COMPRESSED_RGBA_ASTC_4x4_KHR 0x93B0 -#define GR_GL_COMPRESSED_RGBA_ASTC_5x4_KHR 0x93B1 -#define GR_GL_COMPRESSED_RGBA_ASTC_5x5_KHR 0x93B2 -#define GR_GL_COMPRESSED_RGBA_ASTC_6x5_KHR 0x93B3 -#define GR_GL_COMPRESSED_RGBA_ASTC_6x6_KHR 0x93B4 -#define GR_GL_COMPRESSED_RGBA_ASTC_8x5_KHR 0x93B5 -#define GR_GL_COMPRESSED_RGBA_ASTC_8x6_KHR 0x93B6 -#define GR_GL_COMPRESSED_RGBA_ASTC_8x8_KHR 0x93B7 -#define GR_GL_COMPRESSED_RGBA_ASTC_10x5_KHR 0x93B8 -#define GR_GL_COMPRESSED_RGBA_ASTC_10x6_KHR 0x93B9 -#define GR_GL_COMPRESSED_RGBA_ASTC_10x8_KHR 0x93BA -#define GR_GL_COMPRESSED_RGBA_ASTC_10x10_KHR 0x93BB -#define GR_GL_COMPRESSED_RGBA_ASTC_12x10_KHR 0x93BC -#define GR_GL_COMPRESSED_RGBA_ASTC_12x12_KHR 0x93BD +#define GR_GL_COMPRESSED_RGBA_ASTC_4x4 0x93B0 +#define GR_GL_COMPRESSED_RGBA_ASTC_5x4 0x93B1 +#define GR_GL_COMPRESSED_RGBA_ASTC_5x5 0x93B2 +#define GR_GL_COMPRESSED_RGBA_ASTC_6x5 0x93B3 +#define GR_GL_COMPRESSED_RGBA_ASTC_6x6 0x93B4 +#define GR_GL_COMPRESSED_RGBA_ASTC_8x5 0x93B5 +#define GR_GL_COMPRESSED_RGBA_ASTC_8x6 0x93B6 +#define GR_GL_COMPRESSED_RGBA_ASTC_8x8 0x93B7 +#define GR_GL_COMPRESSED_RGBA_ASTC_10x5 0x93B8 +#define GR_GL_COMPRESSED_RGBA_ASTC_10x6 0x93B9 +#define GR_GL_COMPRESSED_RGBA_ASTC_10x8 0x93BA +#define GR_GL_COMPRESSED_RGBA_ASTC_10x10 0x93BB +#define GR_GL_COMPRESSED_RGBA_ASTC_12x10 0x93BC +#define GR_GL_COMPRESSED_RGBA_ASTC_12x12 0x93BD -#define GR_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR 0x93D0 -#define GR_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR 0x93D1 -#define GR_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR 0x93D2 -#define GR_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR 0x93D3 -#define GR_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR 0x93D4 -#define GR_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR 0x93D5 -#define GR_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR 0x93D6 -#define GR_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR 0x93D7 -#define GR_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR 0x93D8 -#define GR_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR 0x93D9 -#define GR_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR 0x93DA -#define GR_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR 0x93DB -#define GR_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR 0x93DC -#define GR_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR 0x93DD +#define GR_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4 0x93D0 +#define GR_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4 0x93D1 +#define GR_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5 0x93D2 +#define GR_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5 0x93D3 +#define GR_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6 0x93D4 +#define GR_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5 0x93D5 +#define GR_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6 0x93D6 +#define GR_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8 0x93D7 +#define GR_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5 0x93D8 +#define GR_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6 0x93D9 +#define GR_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8 0x93DA +#define GR_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10 0x93DB +#define GR_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10 0x93DC +#define GR_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12 0x93DD /* HintMode */ #define GR_GL_DONT_CARE 0x1100 @@ -702,7 +708,8 @@ #define GR_GL_T2F_C4F_N3F_V3F 0x2A2C #define GR_GL_T4F_C4F_N3F_V4F 0x2A2D -/* Vertex Buffer Object */ +/* Buffer Object */ +#define GR_GL_READ_ONLY 0x88B8 #define GR_GL_WRITE_ONLY 0x88B9 #define GR_GL_BUFFER_MAPPED 0x88BC @@ -813,6 +820,7 @@ #define GR_GL_STENCIL 0x1802 #define GR_GL_NONE 0 +#define GR_GL_FRAMEBUFFER_DEFAULT 0x8218 #define GR_GL_FRAMEBUFFER_COMPLETE 0x8CD5 #define GR_GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT 0x8CD6 @@ -931,6 +939,9 @@ /* GL_OES_EGL_image_external */ #define GR_GL_TEXTURE_EXTERNAL 0x8D65 +/* GL_ARB_texture_rectangle */ +#define GR_GL_TEXTURE_RECTANGLE 0x84F5 + /* EGL Defines */ #define GR_EGL_NO_DISPLAY ((GrEGLDisplay)0) #define GR_EGL_EXTENSIONS 0x3055 diff --git a/gfx/skia/skia/src/gpu/gl/GrGLGpu.cpp b/gfx/skia/skia/src/gpu/gl/GrGLGpu.cpp index c0581377f1..cbba3d8d94 100644 --- a/gfx/skia/skia/src/gpu/gl/GrGLGpu.cpp +++ b/gfx/skia/skia/src/gpu/gl/GrGLGpu.cpp @@ -160,14 +160,6 @@ bool GrGLGpu::BlendCoeffReferencesConstant(GrBlendCoeff coeff) { /////////////////////////////////////////////////////////////////////////////// -// Used in the map of pixel configs to stencil format indices. This value is used to -// indicate that a stencil format has not yet been set for the given config. -static const int kUnknownStencilIndex = -1; -// This value is used as the stencil index when no stencil configs are supported with the -// given pixel config. -static const int kUnsupportedStencilIndex = -2; - -/////////////////////////////////////////////////////////////////////////////// GrGpu* GrGLGpu::Create(GrBackendContext backendContext, const GrContextOptions& options, GrContext* context) { @@ -221,9 +213,6 @@ GrGLGpu::GrGLGpu(GrGLContext* ctx, GrContext* context) SkASSERT(this->glCaps().maxVertexAttributes() >= GrGeometryProcessor::kMaxVertexAttribs); - for (int i = 0; i < kGrPixelConfigCnt; ++i) { - fPixelConfigToStencilIndex[i] = kUnknownStencilIndex; - } fHWProgramID = 0; fTempSrcFBOID = 0; fTempDstFBOID = 0; @@ -232,11 +221,16 @@ GrGLGpu::GrGLGpu(GrGLContext* ctx, GrContext* context) if (this->glCaps().shaderCaps()->pathRenderingSupport()) { fPathRendering.reset(new GrGLPathRendering(this)); } - this->createCopyPrograms(); + fWireRectProgram.fProgram = 0; + fWireRectArrayBuffer = 0; } GrGLGpu::~GrGLGpu() { + // Delete the path rendering explicitly, since it will need working gpu object to release the + // resources the object itself holds. + fPathRendering.reset(); + if (0 != fHWProgramID) { // detach the current program so there is no confusion on OpenGL's part // that we want it to be deleted @@ -258,10 +252,19 @@ GrGLGpu::~GrGLGpu() { GL_CALL(DeleteProgram(fCopyPrograms[i].fProgram)); } } + if (0 != fCopyProgramArrayBuffer) { GL_CALL(DeleteBuffers(1, &fCopyProgramArrayBuffer)); } + if (0 != fWireRectProgram.fProgram) { + GL_CALL(DeleteProgram(fWireRectProgram.fProgram)); + } + + if (0 != fWireRectArrayBuffer) { + GL_CALL(DeleteBuffers(1, &fWireRectArrayBuffer)); + } + delete fProgramCache; } @@ -276,6 +279,8 @@ void GrGLGpu::contextAbandoned() { for (size_t i = 0; i < SK_ARRAY_COUNT(fCopyPrograms); ++i) { fCopyPrograms[i].fProgram = 0; } + fWireRectProgram.fProgram = 0; + fWireRectArrayBuffer = 0; if (this->glCaps().shaderCaps()->pathRenderingSupport()) { this->glPathRendering()->abandonGpuResources(); } @@ -413,9 +418,6 @@ static GrSurfaceOrigin resolve_origin(GrSurfaceOrigin origin, bool renderTarget) GrTexture* GrGLGpu::onWrapBackendTexture(const GrBackendTextureDesc& desc, GrWrapOwnership ownership) { - if (!this->configToGLFormats(desc.fConfig, false, nullptr, nullptr, nullptr)) { - return nullptr; - } #ifdef SK_IGNORE_GL_TEXTURE_TARGET if (!desc.fTextureHandle) { return nullptr; @@ -445,6 +447,7 @@ GrTexture* GrGLGpu::onWrapBackendTexture(const GrBackendTextureDesc& desc, #else idDesc.fInfo = *info; #endif + if (GR_GL_TEXTURE_EXTERNAL == idDesc.fInfo.fTarget) { if (renderTarget) { // This combination is not supported. @@ -453,8 +456,15 @@ GrTexture* GrGLGpu::onWrapBackendTexture(const GrBackendTextureDesc& desc, if (!this->glCaps().externalTextureSupport()) { return nullptr; } + } else if (GR_GL_TEXTURE_RECTANGLE == idDesc.fInfo.fTarget) { + if (!this->glCaps().rectangleTextureSupport()) { + return nullptr; + } + } else if (GR_GL_TEXTURE_2D != idDesc.fInfo.fTarget) { + return nullptr; } - // Sample count is interpretted to mean the number of samples that Gr code should allocate + + // Sample count is interpreted to mean the number of samples that Gr code should allocate // for a render buffer that resolves to the texture. We don't support MSAA textures. if (desc.fSampleCnt && !renderTarget) { return nullptr; @@ -467,7 +477,7 @@ GrTexture* GrGLGpu::onWrapBackendTexture(const GrBackendTextureDesc& desc, case kBorrow_GrWrapOwnership: idDesc.fLifeCycle = GrGpuResource::kBorrowed_LifeCycle; break; - } + } surfDesc.fFlags = (GrSurfaceFlags) desc.fFlags; surfDesc.fWidth = desc.fWidth; @@ -544,8 +554,8 @@ bool GrGLGpu::onGetWritePixelsInfo(GrSurface* dstSurface, int width, int height, ElevateDrawPreference(drawPreference, kRequireDraw_DrawPreference); } else { GrGLTexture* texture = static_cast(dstSurface->asTexture()); - if (GR_GL_TEXTURE_2D != texture->target()) { - // We don't currently support writing pixels to non-TEXTURE_2D textures. + if (GR_GL_TEXTURE_EXTERNAL == texture->target()) { + // We don't currently support writing pixels to EXTERNAL textures. return false; } } @@ -595,11 +605,8 @@ bool GrGLGpu::onGetWritePixelsInfo(GrSurface* dstSurface, int width, int height, return true; } -bool GrGLGpu::onWritePixels(GrSurface* surface, - int left, int top, int width, int height, - GrPixelConfig config, const void* buffer, - size_t rowBytes) { - GrGLTexture* glTex = static_cast(surface->asTexture()); +static bool check_write_and_transfer_input(GrGLTexture* glTex, GrSurface* surface, + GrPixelConfig config) { if (!glTex) { return false; } @@ -609,8 +616,21 @@ bool GrGLGpu::onWritePixels(GrSurface* surface, return false; } - // Write pixels is only implemented for TEXTURE_2D textures - if (GR_GL_TEXTURE_2D != glTex->target()) { + // Write or transfer of pixels is not implemented for TEXTURE_EXTERNAL textures + if (GR_GL_TEXTURE_EXTERNAL == glTex->target()) { + return false; + } + + return true; +} + +bool GrGLGpu::onWritePixels(GrSurface* surface, + int left, int top, int width, int height, + GrPixelConfig config, const void* buffer, + size_t rowBytes) { + GrGLTexture* glTex = static_cast(surface->asTexture()); + + if (!check_write_and_transfer_input(glTex, surface, config)) { return false; } @@ -621,11 +641,11 @@ bool GrGLGpu::onWritePixels(GrSurface* surface, if (GrPixelConfigIsCompressed(glTex->desc().fConfig)) { // We check that config == desc.fConfig in GrGLGpu::canWriteTexturePixels() SkASSERT(config == glTex->desc().fConfig); - success = this->uploadCompressedTexData(glTex->desc(), glTex->target(), buffer, false, left, - top, width, height); + success = this->uploadCompressedTexData(glTex->desc(), glTex->target(), buffer, + kWrite_UploadType, left, top, width, height); } else { - success = this->uploadTexData(glTex->desc(), glTex->target(), false, left, top, width, - height, config, buffer, rowBytes); + success = this->uploadTexData(glTex->desc(), glTex->target(), kWrite_UploadType, + left, top, width, height, config, buffer, rowBytes); } if (success) { @@ -636,6 +656,64 @@ bool GrGLGpu::onWritePixels(GrSurface* surface, return false; } +bool GrGLGpu::onTransferPixels(GrSurface* surface, + int left, int top, int width, int height, + GrPixelConfig config, GrTransferBuffer* buffer, + size_t offset, size_t rowBytes) { + GrGLTexture* glTex = static_cast(surface->asTexture()); + + if (!check_write_and_transfer_input(glTex, surface, config)) { + return false; + } + + // For the moment, can't transfer compressed data + if (GrPixelConfigIsCompressed(glTex->desc().fConfig)) { + return false; + } + + this->setScratchTextureUnit(); + GL_CALL(BindTexture(glTex->target(), glTex->textureID())); + + SkASSERT(!buffer->isMapped()); + GrGLTransferBuffer* glBuffer = reinterpret_cast(buffer); + // bind the transfer buffer + SkASSERT(GR_GL_PIXEL_UNPACK_BUFFER == glBuffer->bufferType() || + GR_GL_PIXEL_UNPACK_TRANSFER_BUFFER_CHROMIUM == glBuffer->bufferType()); + GL_CALL(BindBuffer(glBuffer->bufferType(), glBuffer->bufferID())); + + bool success = false; + success = this->uploadTexData(glTex->desc(), glTex->target(), kTransfer_UploadType, + left, top, width, height, config, buffer, rowBytes); + + if (success) { + glTex->texturePriv().dirtyMipMaps(true); + return true; + } + + return false; +} + +// For GL_[UN]PACK_ALIGNMENT. +static inline GrGLint config_alignment(GrPixelConfig config) { + SkASSERT(!GrPixelConfigIsCompressed(config)); + switch (config) { + case kAlpha_8_GrPixelConfig: + return 1; + case kRGB_565_GrPixelConfig: + case kRGBA_4444_GrPixelConfig: + case kAlpha_half_GrPixelConfig: + case kRGBA_half_GrPixelConfig: + return 2; + case kRGBA_8888_GrPixelConfig: + case kBGRA_8888_GrPixelConfig: + case kSRGBA_8888_GrPixelConfig: + case kRGBA_float_GrPixelConfig: + return 4; + default: + return 0; + } +} + static inline GrGLenum check_alloc_error(const GrSurfaceDesc& desc, const GrGLInterface* interface) { if (SkToBool(desc.fFlags & kCheckAllocation_GrSurfaceFlag)) { @@ -647,19 +725,22 @@ static inline GrGLenum check_alloc_error(const GrSurfaceDesc& desc, bool GrGLGpu::uploadTexData(const GrSurfaceDesc& desc, GrGLenum target, - bool isNewTexture, + UploadType uploadType, int left, int top, int width, int height, GrPixelConfig dataConfig, - const void* data, + const void* dataOrOffset, size_t rowBytes) { - SkASSERT(data || isNewTexture); + SkASSERT(dataOrOffset || kNewTexture_UploadType == uploadType || + kTransfer_UploadType == uploadType); // If we're uploading compressed data then we should be using uploadCompressedTexData SkASSERT(!GrPixelConfigIsCompressed(dataConfig)); + SkASSERT(this->caps()->isConfigTexturable(desc.fConfig)); + size_t bpp = GrBytesPerPixel(dataConfig); if (!GrSurfacePriv::AdjustWritePixelParams(desc.fWidth, desc.fHeight, bpp, &left, &top, - &width, &height, &data, &rowBytes)) { + &width, &height, &dataOrOffset, &rowBytes)) { return false; } size_t trimRowBytes = width * bpp; @@ -667,43 +748,17 @@ bool GrGLGpu::uploadTexData(const GrSurfaceDesc& desc, // in case we need a temporary, trimmed copy of the src pixels SkAutoMalloc tempStorage; - // We currently lazily create MIPMAPs when the we see a draw with - // GrTextureParams::kMipMap_FilterMode. Using texture storage requires that the - // MIP levels are all created when the texture is created. So for now we don't use - // texture storage. - bool useTexStorage = false && - isNewTexture && - this->glCaps().texStorageSupport(); - - if (useTexStorage && kGL_GrGLStandard == this->glStandard()) { - // 565 is not a sized internal format on desktop GL. So on desktop with - // 565 we always use an unsized internal format to let the system pick - // the best sized format to convert the 565 data to. Since TexStorage - // only allows sized internal formats we will instead use TexImage2D. - useTexStorage = desc.fConfig != kRGB_565_GrPixelConfig; - } - - GrGLenum internalFormat = 0x0; // suppress warning - GrGLenum externalFormat = 0x0; // suppress warning - GrGLenum externalType = 0x0; // suppress warning - - // glTexStorage requires sized internal formats on both desktop and ES. ES2 requires an unsized - // format for glTexImage, unlike ES3 and desktop. - bool useSizedFormat = useTexStorage; - if (kGL_GrGLStandard == this->glStandard() || - (this->glVersion() >= GR_GL_VER(3, 0) && - // ES3 only works with sized BGRA8 format if "GL_APPLE_texture_format_BGRA8888" enabled - (kBGRA_8888_GrPixelConfig != dataConfig || !this->glCaps().bgraIsInternalFormat()))) { - useSizedFormat = true; - } - - if (!this->configToGLFormats(dataConfig, useSizedFormat, &internalFormat, - &externalFormat, &externalType)) { + // Internal format comes from the texture desc. + GrGLenum internalFormat; + // External format and type come from the upload data. + GrGLenum externalFormat; + GrGLenum externalType; + if (!this->glCaps().getTexImageFormats(desc.fConfig, dataConfig, &internalFormat, + &externalFormat, &externalType)) { return false; } - /* - * check whether to allocate a temporary buffer for flipping y or + * Check whether to allocate a temporary buffer for flipping y or * because our srcData has extra bytes past each row. If so, we need * to trim those off here, since GL ES may not let us specify * GL_UNPACK_ROW_LENGTH. @@ -711,7 +766,7 @@ bool GrGLGpu::uploadTexData(const GrSurfaceDesc& desc, bool restoreGLRowLength = false; bool swFlipY = false; bool glFlipY = false; - if (data) { + if (dataOrOffset) { if (kBottomLeft_GrSurfaceOrigin == desc.fOrigin) { if (this->glCaps().unpackFlipYSupport()) { glFlipY = true; @@ -726,11 +781,11 @@ bool GrGLGpu::uploadTexData(const GrSurfaceDesc& desc, GL_CALL(PixelStorei(GR_GL_UNPACK_ROW_LENGTH, rowLength)); restoreGLRowLength = true; } - } else { + } else if (kTransfer_UploadType != uploadType) { if (trimRowBytes != rowBytes || swFlipY) { // copy data into our new storage, skipping the trailing bytes size_t trimSize = height * trimRowBytes; - const char* src = (const char*)data; + const char* src = (const char*)dataOrOffset; if (swFlipY) { src += (height - 1) * rowBytes; } @@ -745,50 +800,29 @@ bool GrGLGpu::uploadTexData(const GrSurfaceDesc& desc, dst += trimRowBytes; } // now point data to our copied version - data = tempStorage.get(); + dataOrOffset = tempStorage.get(); } + } else { + return false; } if (glFlipY) { GL_CALL(PixelStorei(GR_GL_UNPACK_FLIP_Y, GR_GL_TRUE)); } - GL_CALL(PixelStorei(GR_GL_UNPACK_ALIGNMENT, - static_cast(GrUnpackAlignment(dataConfig)))); + GL_CALL(PixelStorei(GR_GL_UNPACK_ALIGNMENT, config_alignment(dataConfig))); } bool succeeded = true; - if (isNewTexture && - 0 == left && 0 == top && - desc.fWidth == width && desc.fHeight == height) { - CLEAR_ERROR_BEFORE_ALLOC(this->glInterface()); - if (useTexStorage) { - // We never resize or change formats of textures. - GL_ALLOC_CALL(this->glInterface(), - TexStorage2D(target, - 1, // levels - internalFormat, - desc.fWidth, desc.fHeight)); - } else { - GL_ALLOC_CALL(this->glInterface(), - TexImage2D(target, - 0, // level - internalFormat, - desc.fWidth, desc.fHeight, - 0, // border - externalFormat, externalType, - data)); - } - GrGLenum error = check_alloc_error(desc, this->glInterface()); - if (error != GR_GL_NO_ERROR) { + if (kNewTexture_UploadType == uploadType) { + if (dataOrOffset && + !(0 == left && 0 == top && desc.fWidth == width && desc.fHeight == height)) { succeeded = false; } else { - // if we have data and we used TexStorage to create the texture, we - // now upload with TexSubImage. - if (data && useTexStorage) { - GL_CALL(TexSubImage2D(target, - 0, // level - left, top, - width, height, - externalFormat, externalType, - data)); + CLEAR_ERROR_BEFORE_ALLOC(this->glInterface()); + GL_ALLOC_CALL(this->glInterface(), TexImage2D(target, 0, internalFormat, desc.fWidth, + desc.fHeight, 0, externalFormat, + externalType, dataOrOffset)); + GrGLenum error = check_alloc_error(desc, this->glInterface()); + if (error != GR_GL_NO_ERROR) { + succeeded = false; } } } else { @@ -799,7 +833,7 @@ bool GrGLGpu::uploadTexData(const GrSurfaceDesc& desc, 0, // level left, top, width, height, - externalFormat, externalType, data)); + externalFormat, externalType, dataOrOffset)); } if (restoreGLRowLength) { @@ -820,9 +854,11 @@ bool GrGLGpu::uploadTexData(const GrSurfaceDesc& desc, bool GrGLGpu::uploadCompressedTexData(const GrSurfaceDesc& desc, GrGLenum target, const void* data, - bool isNewTexture, + UploadType uploadType, int left, int top, int width, int height) { - SkASSERT(data || isNewTexture); + SkASSERT(this->caps()->isConfigTexturable(desc.fConfig)); + SkASSERT(kTransfer_UploadType != uploadType && + (data || kNewTexture_UploadType != uploadType)); // No support for software flip y, yet... SkASSERT(kBottomLeft_GrSurfaceOrigin != desc.fOrigin); @@ -850,12 +886,12 @@ bool GrGLGpu::uploadCompressedTexData(const GrSurfaceDesc& desc, size_t dataSize = GrCompressedFormatDataSize(desc.fConfig, width, height); // We only need the internal format for compressed 2D textures. - GrGLenum internalFormat = 0; - if (!this->configToGLFormats(desc.fConfig, false, &internalFormat, nullptr, nullptr)) { + GrGLenum internalFormat; + if (!this->glCaps().getCompressedTexImageFormats(desc.fConfig, &internalFormat)) { return false; } - if (isNewTexture) { + if (kNewTexture_UploadType == uploadType) { CLEAR_ERROR_BEFORE_ALLOC(this->glInterface()); GL_ALLOC_CALL(this->glInterface(), CompressedTexImage2D(target, @@ -939,7 +975,7 @@ bool GrGLGpu::createRenderTargetObjects(const GrSurfaceDesc& desc, GrGLenum status; - GrGLenum msColorFormat = 0; // suppress warning + GrGLenum colorRenderbufferFormat = 0; // suppress warning if (desc.fSampleCnt > 0 && GrGLCaps::kNone_MSFBOType == this->glCaps().msFBOType()) { goto FAILED; @@ -950,7 +986,6 @@ bool GrGLGpu::createRenderTargetObjects(const GrSurfaceDesc& desc, goto FAILED; } - // If we are using multisampling we will create two FBOS. We render to one and then resolve to // the texture bound to the other. The exception is the IMG multisample extension. With this // extension the texture is multisampled when rendered to and then auto-resolves it when it is @@ -959,15 +994,12 @@ bool GrGLGpu::createRenderTargetObjects(const GrSurfaceDesc& desc, GL_CALL(GenFramebuffers(1, &idDesc->fRTFBOID)); GL_CALL(GenRenderbuffers(1, &idDesc->fMSColorRenderbufferID)); if (!idDesc->fRTFBOID || - !idDesc->fMSColorRenderbufferID || - !this->configToGLFormats(desc.fConfig, - // ES2 and ES3 require sized internal formats for rb storage. - kGLES_GrGLStandard == this->glStandard(), - &msColorFormat, - nullptr, - nullptr)) { + !idDesc->fMSColorRenderbufferID) { goto FAILED; } + if (!this->glCaps().getRenderbufferFormat(desc.fConfig, &colorRenderbufferFormat)) { + return false; + } } else { idDesc->fRTFBOID = idDesc->fTexFBOID; } @@ -979,7 +1011,7 @@ bool GrGLGpu::createRenderTargetObjects(const GrSurfaceDesc& desc, GL_CALL(BindRenderbuffer(GR_GL_RENDERBUFFER, idDesc->fMSColorRenderbufferID)); if (!renderbuffer_storage_msaa(*fGLContext, desc.fSampleCnt, - msColorFormat, + colorRenderbufferFormat, desc.fWidth, desc.fHeight)) { goto FAILED; } @@ -1060,6 +1092,7 @@ GrTexture* GrGLGpu::onCreateTexture(const GrSurfaceDesc& desc, bool renderTarget = SkToBool(desc.fFlags & kRenderTarget_GrSurfaceFlag); GrGLTexture::IDDesc idDesc; + idDesc.fInfo.fID = 0; GL_CALL(GenTextures(1, &idDesc.fInfo.fID)); idDesc.fLifeCycle = lifeCycle; // We only support GL_TEXTURE_2D at the moment. @@ -1101,7 +1134,7 @@ GrTexture* GrGLGpu::onCreateTexture(const GrSurfaceDesc& desc, GL_CALL(TexParameteri(idDesc.fInfo.fTarget, GR_GL_TEXTURE_WRAP_T, initialTexParams.fWrapT)); - if (!this->uploadTexData(desc, idDesc.fInfo.fTarget, true, 0, 0, + if (!this->uploadTexData(desc, idDesc.fInfo.fTarget, kNewTexture_UploadType, 0, 0, desc.fWidth, desc.fHeight, desc.fConfig, srcData, rowBytes)) { GL_CALL(DeleteTextures(1, &idDesc.fInfo.fID)); @@ -1139,6 +1172,7 @@ GrTexture* GrGLGpu::onCreateCompressedTexture(const GrSurfaceDesc& desc, } GrGLTexture::IDDesc idDesc; + idDesc.fInfo.fID = 0; GL_CALL(GenTextures(1, &idDesc.fInfo.fID)); idDesc.fLifeCycle = lifeCycle; // We only support GL_TEXTURE_2D at the moment. @@ -1217,11 +1251,12 @@ void inline get_stencil_rb_sizes(const GrGLInterface* gl, int GrGLGpu::getCompatibleStencilIndex(GrPixelConfig config) { static const int kSize = 16; - if (kUnknownStencilIndex == fPixelConfigToStencilIndex[config]) { - // Default to unsupported - fPixelConfigToStencilIndex[config] = kUnsupportedStencilIndex; + SkASSERT(this->caps()->isConfigRenderable(config, false)); + if (!this->glCaps().hasStencilFormatBeenDeterminedForConfig(config)) { + // Default to unsupported, set this if we find a stencil format that works. + int firstWorkingStencilFormatIndex = -1; // Create color texture - GrGLuint colorID; + GrGLuint colorID = 0; GL_CALL(GenTextures(1, &colorID)); this->setScratchTextureUnit(); GL_CALL(BindTexture(GR_GL_TEXTURE_2D, colorID)); @@ -1238,43 +1273,33 @@ int GrGLGpu::getCompatibleStencilIndex(GrPixelConfig config) { GR_GL_TEXTURE_WRAP_T, GR_GL_CLAMP_TO_EDGE)); - GrGLenum internalFormat = 0x0; // suppress warning - GrGLenum externalFormat = 0x0; // suppress warning - GrGLenum externalType = 0x0; // suppress warning - bool useSizedFormat = false; - if (kGL_GrGLStandard == this->glStandard() || - (this->glVersion() >= GR_GL_VER(3, 0) && - // ES3 only works with sized BGRA8 format if "GL_APPLE_texture_format_BGRA8888" enabled - (kBGRA_8888_GrPixelConfig != config || !this->glCaps().bgraIsInternalFormat()))) { - useSizedFormat = true; + GrGLenum internalFormat; + GrGLenum externalFormat; + GrGLenum externalType; + if (!this->glCaps().getTexImageFormats(config, config, &internalFormat, &externalFormat, + &externalType)) { + return false; } - if (!this->configToGLFormats(config, useSizedFormat, &internalFormat, - &externalFormat, &externalType)) { - GL_CALL(DeleteTextures(1, &colorID)); - fPixelConfigToStencilIndex[config] = kUnsupportedStencilIndex; - return kUnsupportedStencilIndex; - } - CLEAR_ERROR_BEFORE_ALLOC(this->glInterface()); GL_ALLOC_CALL(this->glInterface(), TexImage2D(GR_GL_TEXTURE_2D, - 0, internalFormat, + 0, + internalFormat, kSize, kSize, 0, externalFormat, externalType, NULL)); - if (GR_GL_NO_ERROR != GR_GL_GET_ERROR(this->glInterface())) { + if (GR_GL_NO_ERROR != CHECK_ALLOC_ERROR(this->glInterface())) { GL_CALL(DeleteTextures(1, &colorID)); - fPixelConfigToStencilIndex[config] = kUnsupportedStencilIndex; - return kUnsupportedStencilIndex; + return -1; } // unbind the texture from the texture unit before binding it to the frame buffer GL_CALL(BindTexture(GR_GL_TEXTURE_2D, 0)); // Create Framebuffer - GrGLuint fb; + GrGLuint fb = 0; GL_CALL(GenFramebuffers(1, &fb)); GL_CALL(BindFramebuffer(GR_GL_FRAMEBUFFER, fb)); fHWBoundRenderTargetUniqueID = SK_InvalidUniqueID; @@ -1283,38 +1308,38 @@ int GrGLGpu::getCompatibleStencilIndex(GrPixelConfig config) { GR_GL_TEXTURE_2D, colorID, 0)); + GrGLuint sbRBID = 0; + GL_CALL(GenRenderbuffers(1, &sbRBID)); // look over formats till I find a compatible one int stencilFmtCnt = this->glCaps().stencilFormats().count(); - GrGLuint sbRBID = 0; - for (int i = 0; i < stencilFmtCnt; ++i) { - const GrGLCaps::StencilFormat& sFmt = this->glCaps().stencilFormats()[i]; - - GL_CALL(GenRenderbuffers(1, &sbRBID)); - if (!sbRBID) { - break; - } + if (sbRBID) { GL_CALL(BindRenderbuffer(GR_GL_RENDERBUFFER, sbRBID)); - CLEAR_ERROR_BEFORE_ALLOC(this->glInterface()); - GL_ALLOC_CALL(this->glInterface(), RenderbufferStorage(GR_GL_RENDERBUFFER, - sFmt.fInternalFormat, - kSize, kSize)); - if (GR_GL_NO_ERROR == GR_GL_GET_ERROR(this->glInterface())) { - GL_CALL(FramebufferRenderbuffer(GR_GL_FRAMEBUFFER, - GR_GL_STENCIL_ATTACHMENT, - GR_GL_RENDERBUFFER, sbRBID)); - if (sFmt.fPacked) { + for (int i = 0; i < stencilFmtCnt && sbRBID; ++i) { + const GrGLCaps::StencilFormat& sFmt = this->glCaps().stencilFormats()[i]; + CLEAR_ERROR_BEFORE_ALLOC(this->glInterface()); + GL_ALLOC_CALL(this->glInterface(), RenderbufferStorage(GR_GL_RENDERBUFFER, + sFmt.fInternalFormat, + kSize, kSize)); + if (GR_GL_NO_ERROR == CHECK_ALLOC_ERROR(this->glInterface())) { GL_CALL(FramebufferRenderbuffer(GR_GL_FRAMEBUFFER, - GR_GL_DEPTH_ATTACHMENT, + GR_GL_STENCIL_ATTACHMENT, GR_GL_RENDERBUFFER, sbRBID)); - } else { - GL_CALL(FramebufferRenderbuffer(GR_GL_FRAMEBUFFER, - GR_GL_DEPTH_ATTACHMENT, - GR_GL_RENDERBUFFER, 0)); - } - GrGLenum status; - GL_CALL_RET(status, CheckFramebufferStatus(GR_GL_FRAMEBUFFER)); - if (status != GR_GL_FRAMEBUFFER_COMPLETE) { + if (sFmt.fPacked) { + GL_CALL(FramebufferRenderbuffer(GR_GL_FRAMEBUFFER, + GR_GL_DEPTH_ATTACHMENT, + GR_GL_RENDERBUFFER, sbRBID)); + } else { + GL_CALL(FramebufferRenderbuffer(GR_GL_FRAMEBUFFER, + GR_GL_DEPTH_ATTACHMENT, + GR_GL_RENDERBUFFER, 0)); + } + GrGLenum status; + GL_CALL_RET(status, CheckFramebufferStatus(GR_GL_FRAMEBUFFER)); + if (status == GR_GL_FRAMEBUFFER_COMPLETE) { + firstWorkingStencilFormatIndex = i; + break; + } GL_CALL(FramebufferRenderbuffer(GR_GL_FRAMEBUFFER, GR_GL_STENCIL_ATTACHMENT, GR_GL_RENDERBUFFER, 0)); @@ -1323,20 +1348,16 @@ int GrGLGpu::getCompatibleStencilIndex(GrPixelConfig config) { GR_GL_DEPTH_ATTACHMENT, GR_GL_RENDERBUFFER, 0)); } - } else { - fPixelConfigToStencilIndex[config] = i; - break; } } - sbRBID = 0; + GL_CALL(DeleteRenderbuffers(1, &sbRBID)); } GL_CALL(DeleteTextures(1, &colorID)); - GL_CALL(DeleteRenderbuffers(1, &sbRBID)); GL_CALL(BindFramebuffer(GR_GL_FRAMEBUFFER, 0)); GL_CALL(DeleteFramebuffers(1, &fb)); + fGLContext->caps()->setStencilFormatIndexForConfig(config, firstWorkingStencilFormatIndex); } - SkASSERT(kUnknownStencilIndex != fPixelConfigToStencilIndex[config]); - return fPixelConfigToStencilIndex[config]; + return this->glCaps().getStencilFormatIndexForConfig(config); } GrStencilAttachment* GrGLGpu::createStencilAttachmentForRenderTarget(const GrRenderTarget* rt, @@ -1352,7 +1373,7 @@ GrStencilAttachment* GrGLGpu::createStencilAttachmentForRenderTarget(const GrRen GrGLStencilAttachment::IDDesc sbDesc; int sIdx = this->getCompatibleStencilIndex(rt->config()); - if (sIdx == kUnsupportedStencilIndex) { + if (sIdx < 0) { return nullptr; } @@ -1394,16 +1415,21 @@ GrStencilAttachment* GrGLGpu::createStencilAttachmentForRenderTarget(const GrRen //////////////////////////////////////////////////////////////////////////////// +// GL_STREAM_DRAW triggers an optimization in Chromium's GPU process where a client's vertex buffer +// objects are implemented as client-side-arrays on tile-deferred architectures. +#define DYNAMIC_USAGE_PARAM GR_GL_STREAM_DRAW + GrVertexBuffer* GrGLGpu::onCreateVertexBuffer(size_t size, bool dynamic) { GrGLVertexBuffer::Desc desc; - desc.fDynamic = dynamic; + desc.fUsage = dynamic ? GrGLBufferImpl::kDynamicDraw_Usage : GrGLBufferImpl::kStaticDraw_Usage; desc.fSizeInBytes = size; - if (this->glCaps().useNonVBOVertexAndIndexDynamicData() && desc.fDynamic) { + if (this->glCaps().useNonVBOVertexAndIndexDynamicData() && dynamic) { desc.fID = 0; GrGLVertexBuffer* vertexBuffer = new GrGLVertexBuffer(this, desc); return vertexBuffer; } else { + desc.fID = 0; GL_CALL(GenBuffers(1, &desc.fID)); if (desc.fID) { fHWGeometryState.setVertexBufferID(this, desc.fID); @@ -1413,7 +1439,7 @@ GrVertexBuffer* GrGLGpu::onCreateVertexBuffer(size_t size, bool dynamic) { BufferData(GR_GL_ARRAY_BUFFER, (GrGLsizeiptr) desc.fSizeInBytes, nullptr, // data ptr - desc.fDynamic ? GR_GL_DYNAMIC_DRAW : GR_GL_STATIC_DRAW)); + dynamic ? DYNAMIC_USAGE_PARAM : GR_GL_STATIC_DRAW)); if (CHECK_ALLOC_ERROR(this->glInterface()) != GR_GL_NO_ERROR) { GL_CALL(DeleteBuffers(1, &desc.fID)); this->notifyVertexBufferDelete(desc.fID); @@ -1428,14 +1454,15 @@ GrVertexBuffer* GrGLGpu::onCreateVertexBuffer(size_t size, bool dynamic) { GrIndexBuffer* GrGLGpu::onCreateIndexBuffer(size_t size, bool dynamic) { GrGLIndexBuffer::Desc desc; - desc.fDynamic = dynamic; + desc.fUsage = dynamic ? GrGLBufferImpl::kDynamicDraw_Usage : GrGLBufferImpl::kStaticDraw_Usage; desc.fSizeInBytes = size; - if (this->glCaps().useNonVBOVertexAndIndexDynamicData() && desc.fDynamic) { + if (this->glCaps().useNonVBOVertexAndIndexDynamicData() && dynamic) { desc.fID = 0; GrIndexBuffer* indexBuffer = new GrGLIndexBuffer(this, desc); return indexBuffer; } else { + desc.fID = 0; GL_CALL(GenBuffers(1, &desc.fID)); if (desc.fID) { fHWGeometryState.setIndexBufferIDOnDefaultVertexArray(this, desc.fID); @@ -1445,7 +1472,7 @@ GrIndexBuffer* GrGLGpu::onCreateIndexBuffer(size_t size, bool dynamic) { BufferData(GR_GL_ELEMENT_ARRAY_BUFFER, (GrGLsizeiptr) desc.fSizeInBytes, nullptr, // data ptr - desc.fDynamic ? GR_GL_DYNAMIC_DRAW : GR_GL_STATIC_DRAW)); + dynamic ? DYNAMIC_USAGE_PARAM : GR_GL_STATIC_DRAW)); if (CHECK_ALLOC_ERROR(this->glInterface()) != GR_GL_NO_ERROR) { GL_CALL(DeleteBuffers(1, &desc.fID)); this->notifyIndexBufferDelete(desc.fID); @@ -1458,6 +1485,47 @@ GrIndexBuffer* GrGLGpu::onCreateIndexBuffer(size_t size, bool dynamic) { } } +GrTransferBuffer* GrGLGpu::onCreateTransferBuffer(size_t size, TransferType xferType) { + GrGLCaps::TransferBufferType xferBufferType = this->ctxInfo().caps()->transferBufferType(); + if (GrGLCaps::kNone_TransferBufferType == xferBufferType) { + return nullptr; + } + + GrGLTransferBuffer::Desc desc; + bool toGpu = (kCpuToGpu_TransferType == xferType); + desc.fUsage = toGpu ? GrGLBufferImpl::kStreamDraw_Usage : GrGLBufferImpl::kStreamRead_Usage; + + desc.fSizeInBytes = size; + desc.fID = 0; + GL_CALL(GenBuffers(1, &desc.fID)); + if (desc.fID) { + CLEAR_ERROR_BEFORE_ALLOC(this->glInterface()); + // make sure driver can allocate memory for this bmapuffer + GrGLenum target; + if (GrGLCaps::kChromium_TransferBufferType == xferBufferType) { + target = toGpu ? GR_GL_PIXEL_UNPACK_TRANSFER_BUFFER_CHROMIUM + : GR_GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM; + } else { + SkASSERT(GrGLCaps::kPBO_TransferBufferType == xferBufferType); + target = toGpu ? GR_GL_PIXEL_UNPACK_BUFFER : GR_GL_PIXEL_PACK_BUFFER; + } + GL_CALL(BindBuffer(target, desc.fID)); + GL_ALLOC_CALL(this->glInterface(), + BufferData(target, + (GrGLsizeiptr) desc.fSizeInBytes, + nullptr, // data ptr + (toGpu ? GR_GL_STREAM_DRAW : GR_GL_STREAM_READ))); + if (CHECK_ALLOC_ERROR(this->glInterface()) != GR_GL_NO_ERROR) { + GL_CALL(DeleteBuffers(1, &desc.fID)); + return nullptr; + } + GrTransferBuffer* transferBuffer = new GrGLTransferBuffer(this, desc, target); + return transferBuffer; + } + + return nullptr; +} + void GrGLGpu::flushScissor(const GrScissorState& scissorState, const GrGLIRect& rtViewport, GrSurfaceOrigin rtOrigin) { @@ -1491,7 +1559,7 @@ void GrGLGpu::flushScissor(const GrScissorState& scissorState, bool GrGLGpu::flushGLState(const DrawArgs& args) { GrXferProcessor::BlendInfo blendInfo; const GrPipeline& pipeline = *args.fPipeline; - args.fPipeline->getXferProcessor()->getBlendInfo(&blendInfo); + args.fPipeline->getXferProcessor().getBlendInfo(&blendInfo); this->flushColorWrite(blendInfo.fWriteColor); this->flushDrawFace(pipeline.getDrawFace()); @@ -1509,7 +1577,10 @@ bool GrGLGpu::flushGLState(const DrawArgs& args) { } if (blendInfo.fWriteColor) { - this->flushBlend(blendInfo); + // Swizzle the blend to match what the shader will output. + const GrSwizzle& swizzle = this->glCaps().glslCaps()->configOutputSwizzle( + args.fPipeline->getRenderTarget()->config()); + this->flushBlend(blendInfo, swizzle); } SkSTArray<8, const GrTextureAccess*> textureAccesses; @@ -1589,7 +1660,7 @@ void GrGLGpu::setupGeometry(const GrPrimitiveProcessor& primProc, void GrGLGpu::buildProgramDesc(GrProgramDesc* desc, const GrPrimitiveProcessor& primProc, const GrPipeline& pipeline) const { - if (!GrGLProgramDescBuilder::Build(desc, primProc, pipeline, this)) { + if (!GrGLProgramDescBuilder::Build(desc, primProc, pipeline, *this->glCaps().glslCaps())) { SkDEBUGFAIL("Failed to generate GL program descriptor"); } } @@ -1598,9 +1669,10 @@ void GrGLGpu::bindBuffer(GrGLuint id, GrGLenum type) { this->handleDirtyContext(); if (GR_GL_ARRAY_BUFFER == type) { this->bindVertexBuffer(id); - } else { - SkASSERT(GR_GL_ELEMENT_ARRAY_BUFFER == type); + } else if (GR_GL_ELEMENT_ARRAY_BUFFER == type) { this->bindIndexBufferAndDefaultVertexArray(id); + } else { + GR_GL_CALL(this->glInterface(), BindBuffer(type, id)); } } @@ -1609,19 +1681,29 @@ void GrGLGpu::releaseBuffer(GrGLuint id, GrGLenum type) { GL_CALL(DeleteBuffers(1, &id)); if (GR_GL_ARRAY_BUFFER == type) { this->notifyVertexBufferDelete(id); - } else { - SkASSERT(GR_GL_ELEMENT_ARRAY_BUFFER == type); + } else if (GR_GL_ELEMENT_ARRAY_BUFFER == type) { this->notifyIndexBufferDelete(id); } } -// GL_STREAM_DRAW triggers an optimization in Chromium's GPU process where a client's vertex buffer -// objects are implemented as client-side-arrays on tile-deferred architectures. -#define DYNAMIC_USAGE_PARAM GR_GL_STREAM_DRAW +static GrGLenum get_gl_usage(GrGLBufferImpl::Usage usage) { + static const GrGLenum grToGL[] = { + GR_GL_STATIC_DRAW, // GrGLBufferImpl::kStaticDraw_Usage + DYNAMIC_USAGE_PARAM, // GrGLBufferImpl::kDynamicDraw_Usage + GR_GL_STREAM_DRAW, // GrGLBufferImpl::kStreamDraw_Usage + GR_GL_STREAM_READ, // GrGLBufferImpl::kStreamRead_Usage + }; + static_assert(SK_ARRAY_COUNT(grToGL) == GrGLBufferImpl::kUsageCount, "array_size_mismatch"); -void* GrGLGpu::mapBuffer(GrGLuint id, GrGLenum type, bool dynamic, size_t currentSize, - size_t requestedSize) { + return grToGL[usage]; +} + +void* GrGLGpu::mapBuffer(GrGLuint id, GrGLenum type, GrGLBufferImpl::Usage usage, + size_t currentSize, size_t requestedSize) { void* mapPtr = nullptr; + GrGLenum glUsage = get_gl_usage(usage); + bool readOnly = (GrGLBufferImpl::kStreamRead_Usage == usage); + // Handling dirty context is done in the bindBuffer call switch (this->glCaps().mapBufferType()) { case GrGLCaps::kNone_MapBufferType: @@ -1630,46 +1712,50 @@ void* GrGLGpu::mapBuffer(GrGLuint id, GrGLenum type, bool dynamic, size_t curren this->bindBuffer(id, type); // Let driver know it can discard the old data if (GR_GL_USE_BUFFER_DATA_NULL_HINT || currentSize != requestedSize) { - GL_CALL(BufferData(type, requestedSize, nullptr, - dynamic ? DYNAMIC_USAGE_PARAM : GR_GL_STATIC_DRAW)); + GL_CALL(BufferData(type, requestedSize, nullptr, glUsage)); } - GL_CALL_RET(mapPtr, MapBuffer(type, GR_GL_WRITE_ONLY)); + GL_CALL_RET(mapPtr, MapBuffer(type, readOnly ? GR_GL_READ_ONLY : GR_GL_WRITE_ONLY)); break; case GrGLCaps::kMapBufferRange_MapBufferType: { this->bindBuffer(id, type); // Make sure the GL buffer size agrees with fDesc before mapping. if (currentSize != requestedSize) { - GL_CALL(BufferData(type, requestedSize, nullptr, - dynamic ? DYNAMIC_USAGE_PARAM : GR_GL_STATIC_DRAW)); + GL_CALL(BufferData(type, requestedSize, nullptr, glUsage)); } - static const GrGLbitfield kAccess = GR_GL_MAP_INVALIDATE_BUFFER_BIT | - GR_GL_MAP_WRITE_BIT; - GL_CALL_RET(mapPtr, MapBufferRange(type, 0, requestedSize, kAccess)); + GrGLbitfield writeAccess = GR_GL_MAP_WRITE_BIT; + // TODO: allow the client to specify invalidation in the stream draw case + if (GrGLBufferImpl::kStreamDraw_Usage != usage) { + writeAccess |= GR_GL_MAP_INVALIDATE_BUFFER_BIT; + } + GL_CALL_RET(mapPtr, MapBufferRange(type, 0, requestedSize, readOnly ? + GR_GL_MAP_READ_BIT : + writeAccess)); break; } case GrGLCaps::kChromium_MapBufferType: this->bindBuffer(id, type); // Make sure the GL buffer size agrees with fDesc before mapping. if (currentSize != requestedSize) { - GL_CALL(BufferData(type, requestedSize, nullptr, - dynamic ? DYNAMIC_USAGE_PARAM : GR_GL_STATIC_DRAW)); + GL_CALL(BufferData(type, requestedSize, nullptr, glUsage)); } - GL_CALL_RET(mapPtr, MapBufferSubData(type, 0, requestedSize, GR_GL_WRITE_ONLY)); + GL_CALL_RET(mapPtr, MapBufferSubData(type, 0, requestedSize, readOnly ? + GR_GL_READ_ONLY : + GR_GL_WRITE_ONLY)); break; } return mapPtr; } -void GrGLGpu::bufferData(GrGLuint id, GrGLenum type, bool dynamic, size_t currentSize, - const void* src, size_t srcSizeInBytes) { +void GrGLGpu::bufferData(GrGLuint id, GrGLenum type, GrGLBufferImpl::Usage usage, + size_t currentSize, const void* src, size_t srcSizeInBytes) { SkASSERT(srcSizeInBytes <= currentSize); // bindbuffer handles dirty context this->bindBuffer(id, type); - GrGLenum usage = dynamic ? DYNAMIC_USAGE_PARAM : GR_GL_STATIC_DRAW; + GrGLenum glUsage = get_gl_usage(usage); #if GR_GL_USE_BUFFER_DATA_NULL_HINT if (currentSize == srcSizeInBytes) { - GL_CALL(BufferData(type, (GrGLsizeiptr) srcSizeInBytes, src, usage)); + GL_CALL(BufferData(type, (GrGLsizeiptr) srcSizeInBytes, src, glUsage)); } else { // Before we call glBufferSubData we give the driver a hint using // glBufferData with nullptr. This makes the old buffer contents @@ -1678,14 +1764,14 @@ void GrGLGpu::bufferData(GrGLuint id, GrGLenum type, bool dynamic, size_t curren // assign a different allocation for the new contents to avoid // flushing the gpu past draws consuming the old contents. // TODO I think we actually want to try calling bufferData here - GL_CALL(BufferData(type, currentSize, nullptr, usage)); + GL_CALL(BufferData(type, currentSize, nullptr, glUsage)); GL_CALL(BufferSubData(type, 0, (GrGLsizeiptr) srcSizeInBytes, src)); } #else // Note that we're cheating on the size here. Currently no methods // allow a partial update that preserves contents of non-updated // portions of the buffer (map() does a glBufferData(..size, nullptr..)) - GL_CALL(BufferData(type, srcSizeInBytes, src, usage)); + GL_CALL(BufferData(type, srcSizeInBytes, src, glUsage)); #endif } @@ -1909,8 +1995,7 @@ bool GrGLGpu::onGetReadPixelsInfo(GrSurface* srcSurface, int width, int height, tempDrawInfo->fSwapRAndB = true; ElevateDrawPreference(drawPreference, kGpuPrefersDraw_DrawPreference); } else if (readConfig == kBGRA_8888_GrPixelConfig && - !this->glCaps().readPixelsSupported(this->glInterface(), GR_GL_BGRA, - GR_GL_UNSIGNED_BYTE, srcConfig)) { + !this->glCaps().readPixelsSupported(this->glInterface(), readConfig, srcConfig)) { tempDrawInfo->fTempSurfaceDesc.fConfig = kRGBA_8888_GrPixelConfig; tempDrawInfo->fSwapRAndB = true; ElevateDrawPreference(drawPreference, kRequireDraw_DrawPreference); @@ -1945,18 +2030,13 @@ bool GrGLGpu::onReadPixels(GrSurface* surface, return false; } - GrGLenum format = 0; - GrGLenum type = 0; - bool flipY = kBottomLeft_GrSurfaceOrigin == surface->origin(); - if (!this->configToGLFormats(config, false, nullptr, &format, &type)) { + GrGLenum externalFormat; + GrGLenum externalType; + if (!this->glCaps().getReadPixelsFormat(surface->config(), config, &externalFormat, + &externalType)) { return false; } - - // glReadPixels does not allow GL_SRGB_ALPHA. Instead use GL_RGBA. This will not trigger a - // conversion when the src is srgb. - if (GR_GL_SRGB_ALPHA == format) { - format = GR_GL_RGBA; - } + bool flipY = kBottomLeft_GrSurfaceOrigin == surface->origin(); // resolve the render target if necessary switch (tgt->getResolveType()) { @@ -2004,9 +2084,11 @@ bool GrGLGpu::onReadPixels(GrSurface* surface, if (flipY && this->glCaps().packFlipYSupport()) { GL_CALL(PixelStorei(GR_GL_PACK_REVERSE_ROW_ORDER, 1)); } + GL_CALL(PixelStorei(GR_GL_PACK_ALIGNMENT, config_alignment(config))); + GL_CALL(ReadPixels(readRect.fLeft, readRect.fBottom, readRect.fWidth, readRect.fHeight, - format, type, readDst)); + externalFormat, externalType, readDst)); if (readDstRowBytes != tightRowBytes) { SkASSERT(this->glCaps().packRowLengthSupport()); GL_CALL(PixelStorei(GR_GL_PACK_ROW_LENGTH, 0)); @@ -2323,7 +2405,7 @@ void GrGLGpu::flushHWAAState(GrRenderTarget* rt, bool useHWAA) { } } -void GrGLGpu::flushBlend(const GrXferProcessor::BlendInfo& blendInfo) { +void GrGLGpu::flushBlend(const GrXferProcessor::BlendInfo& blendInfo, const GrSwizzle& swizzle) { // Any optimization to disable blending should have already been applied and // tweaked the equation to "add" or "subtract", and the coeffs to (1, 0). @@ -2376,16 +2458,16 @@ void GrGLGpu::flushBlend(const GrXferProcessor::BlendInfo& blendInfo) { fHWBlendState.fDstCoeff = dstCoeff; } - GrColor blendConst = blendInfo.fBlendConstant; - if ((BlendCoeffReferencesConstant(srcCoeff) || - BlendCoeffReferencesConstant(dstCoeff)) && - (!fHWBlendState.fConstColorValid || - fHWBlendState.fConstColor != blendConst)) { - GrGLfloat c[4]; - GrColorToRGBAFloat(blendConst, c); - GL_CALL(BlendColor(c[0], c[1], c[2], c[3])); - fHWBlendState.fConstColor = blendConst; - fHWBlendState.fConstColorValid = true; + if ((BlendCoeffReferencesConstant(srcCoeff) || BlendCoeffReferencesConstant(dstCoeff))) { + GrColor blendConst = blendInfo.fBlendConstant; + blendConst = swizzle.applyTo(blendConst); + if (!fHWBlendState.fConstColorValid || fHWBlendState.fConstColor != blendConst) { + GrGLfloat c[4]; + GrColorToRGBAFloat(blendConst, c); + GL_CALL(BlendColor(c[0], c[1], c[2], c[3])); + fHWBlendState.fConstColor = blendConst; + fHWBlendState.fConstColorValid = true; + } } } @@ -2421,11 +2503,11 @@ static GrGLenum get_component_enum_from_char(char component) { /** If texture swizzling is available using tex parameters then it is preferred over mangling the generated shader code. This potentially allows greater reuse of cached shaders. */ static void get_tex_param_swizzle(GrPixelConfig config, - const GrGLSLCaps& caps, + const GrGLCaps& caps, GrGLenum* glSwizzle) { - const char* swizzle = caps.getSwizzleMap(config); + const GrSwizzle& swizzle = caps.configSwizzle(config); for (int i = 0; i < 4; ++i) { - glSwizzle[i] = get_component_enum_from_char(swizzle[i]); + glSwizzle[i] = get_component_enum_from_char(swizzle.c_str()[i]); } } @@ -2494,7 +2576,7 @@ void GrGLGpu::bindTexture(int unitIdx, const GrTextureParams& params, GrGLTextur newTexParams.fWrapS = tile_to_gl_wrap(params.getTileModeX()); newTexParams.fWrapT = tile_to_gl_wrap(params.getTileModeY()); - get_tex_param_swizzle(texture->config(), *this->glCaps().glslCaps(), newTexParams.fSwizzleRGBA); + get_tex_param_swizzle(texture->config(), this->glCaps(), newTexParams.fSwizzleRGBA); if (setAll || newTexParams.fMagFilter != oldTexParams.fMagFilter) { this->setTextureUnit(unitIdx); GL_CALL(TexParameteri(target, GR_GL_TEXTURE_MAG_FILTER, newTexParams.fMagFilter)); @@ -2511,7 +2593,7 @@ void GrGLGpu::bindTexture(int unitIdx, const GrTextureParams& params, GrGLTextur this->setTextureUnit(unitIdx); GL_CALL(TexParameteri(target, GR_GL_TEXTURE_WRAP_T, newTexParams.fWrapT)); } - if (!this->glCaps().glslCaps()->mustSwizzleInShader() && + if (this->glCaps().textureSwizzleSupport() && (setAll || memcmp(newTexParams.fSwizzleRGBA, oldTexParams.fSwizzleRGBA, sizeof(newTexParams.fSwizzleRGBA)))) { @@ -2568,194 +2650,6 @@ void GrGLGpu::flushDrawFace(GrPipelineBuilder::DrawFace face) { } } -bool GrGLGpu::configToGLFormats(GrPixelConfig config, - bool getSizedInternalFormat, - GrGLenum* internalFormat, - GrGLenum* externalFormat, - GrGLenum* externalType) const { - GrGLenum dontCare; - if (nullptr == internalFormat) { - internalFormat = &dontCare; - } - if (nullptr == externalFormat) { - externalFormat = &dontCare; - } - if (nullptr == externalType) { - externalType = &dontCare; - } - - if(!this->glCaps().isConfigTexturable(config)) { - return false; - } - - switch (config) { - case kRGBA_8888_GrPixelConfig: - *internalFormat = GR_GL_RGBA; - *externalFormat = GR_GL_RGBA; - if (getSizedInternalFormat) { - *internalFormat = GR_GL_RGBA8; - } else { - *internalFormat = GR_GL_RGBA; - } - *externalType = GR_GL_UNSIGNED_BYTE; - break; - case kBGRA_8888_GrPixelConfig: - if (this->glCaps().bgraIsInternalFormat()) { - if (getSizedInternalFormat) { - *internalFormat = GR_GL_BGRA8; - } else { - *internalFormat = GR_GL_BGRA; - } - } else { - if (getSizedInternalFormat) { - *internalFormat = GR_GL_RGBA8; - } else { - *internalFormat = GR_GL_RGBA; - } - } - *externalFormat = GR_GL_BGRA; - *externalType = GR_GL_UNSIGNED_BYTE; - break; - case kSRGBA_8888_GrPixelConfig: - if (getSizedInternalFormat) { - *internalFormat = GR_GL_SRGB8_ALPHA8; - } else { - *internalFormat = GR_GL_SRGB_ALPHA; - } - // OpenGL ES 2.0 + GL_EXT_sRGB allows GL_SRGB_ALPHA to be specified as the - // param to Tex(Sub)Image2D. ES 2.0 requires the internalFormat and format to match. - // Thus, on ES 2.0 we will use GL_SRGB_ALPHA as the externalFormat. However, - // onReadPixels needs code to override that because GL_SRGB_ALPHA is not allowed as a - // glReadPixels format. - // On OpenGL and ES 3.0 GL_SRGB_ALPHA does not work for the param to - // glReadPixels nor does it work with Tex(Sub)Image2D So we always set the externalFormat - // return to GL_RGBA. - if (this->glStandard() == kGLES_GrGLStandard && - this->glVersion() == GR_GL_VER(2,0)) { - *externalFormat = GR_GL_SRGB_ALPHA; - } else { - *externalFormat = GR_GL_RGBA; - } - *externalType = GR_GL_UNSIGNED_BYTE; - break; - case kRGB_565_GrPixelConfig: - *internalFormat = GR_GL_RGB; - *externalFormat = GR_GL_RGB; - if (getSizedInternalFormat) { - if (!this->glCaps().ES2CompatibilitySupport()) { - *internalFormat = GR_GL_RGB5; - } else { - *internalFormat = GR_GL_RGB565; - } - } else { - *internalFormat = GR_GL_RGB; - } - *externalType = GR_GL_UNSIGNED_SHORT_5_6_5; - break; - case kRGBA_4444_GrPixelConfig: - *internalFormat = GR_GL_RGBA; - *externalFormat = GR_GL_RGBA; - if (getSizedInternalFormat) { - *internalFormat = GR_GL_RGBA4; - } else { - *internalFormat = GR_GL_RGBA; - } - *externalType = GR_GL_UNSIGNED_SHORT_4_4_4_4; - break; - case kIndex_8_GrPixelConfig: - // no sized/unsized internal format distinction here - *internalFormat = GR_GL_PALETTE8_RGBA8; - break; - case kAlpha_8_GrPixelConfig: - if (this->glCaps().textureRedSupport()) { - *internalFormat = GR_GL_RED; - *externalFormat = GR_GL_RED; - if (getSizedInternalFormat) { - *internalFormat = GR_GL_R8; - } else { - *internalFormat = GR_GL_RED; - } - *externalType = GR_GL_UNSIGNED_BYTE; - } else { - *internalFormat = GR_GL_ALPHA; - *externalFormat = GR_GL_ALPHA; - if (getSizedInternalFormat) { - *internalFormat = GR_GL_ALPHA8; - } else { - *internalFormat = GR_GL_ALPHA; - } - *externalType = GR_GL_UNSIGNED_BYTE; - } - break; - case kETC1_GrPixelConfig: - *internalFormat = GR_GL_COMPRESSED_ETC1_RGB8; - break; - case kLATC_GrPixelConfig: - switch(this->glCaps().latcAlias()) { - case GrGLCaps::kLATC_LATCAlias: - *internalFormat = GR_GL_COMPRESSED_LUMINANCE_LATC1; - break; - case GrGLCaps::kRGTC_LATCAlias: - *internalFormat = GR_GL_COMPRESSED_RED_RGTC1; - break; - case GrGLCaps::k3DC_LATCAlias: - *internalFormat = GR_GL_COMPRESSED_3DC_X; - break; - } - break; - case kR11_EAC_GrPixelConfig: - *internalFormat = GR_GL_COMPRESSED_R11_EAC; - break; - - case kASTC_12x12_GrPixelConfig: - *internalFormat = GR_GL_COMPRESSED_RGBA_ASTC_12x12_KHR; - break; - - case kRGBA_float_GrPixelConfig: - *internalFormat = GR_GL_RGBA32F; - *externalFormat = GR_GL_RGBA; - *externalType = GR_GL_FLOAT; - break; - - case kAlpha_half_GrPixelConfig: - if (this->glCaps().textureRedSupport()) { - if (getSizedInternalFormat) { - *internalFormat = GR_GL_R16F; - } else { - *internalFormat = GR_GL_RED; - } - *externalFormat = GR_GL_RED; - } else { - if (getSizedInternalFormat) { - *internalFormat = GR_GL_ALPHA16F; - } else { - *internalFormat = GR_GL_ALPHA; - } - *externalFormat = GR_GL_ALPHA; - } - if (kGL_GrGLStandard == this->glStandard() || this->glVersion() >= GR_GL_VER(3, 0)) { - *externalType = GR_GL_HALF_FLOAT; - } else { - *externalType = GR_GL_HALF_FLOAT_OES; - } - break; - - case kRGBA_half_GrPixelConfig: - *internalFormat = GR_GL_RGBA16F; - *externalFormat = GR_GL_RGBA; - if (kGL_GrGLStandard == this->glStandard() || this->glVersion() >= GR_GL_VER(3, 0)) { - *externalType = GR_GL_HALF_FLOAT; - } else { - *externalType = GR_GL_HALF_FLOAT_OES; - } - break; - - default: - return false; - } - return true; -} - void GrGLGpu::setTextureUnit(int unit) { SkASSERT(unit >= 0 && unit < fHWBoundTextureUniqueIDs.count()); if (unit != fHWActiveTextureUnitIdx) { @@ -2957,6 +2851,12 @@ bool GrGLGpu::onCopySurface(GrSurface* dst, GrSurface* src, const SkIRect& srcRect, const SkIPoint& dstPoint) { + // None of our copy methods can handle a swizzle. TODO: Make copySurfaceAsDraw handle the + // swizzle. + if (this->glCaps().glslCaps()->configOutputSwizzle(src->config()) != + this->glCaps().glslCaps()->configOutputSwizzle(dst->config())) { + return false; + } if (src->asTexture() && dst->asRenderTarget()) { this->copySurfaceAsDraw(dst, src, srcRect, dstPoint); return true; @@ -2974,16 +2874,23 @@ bool GrGLGpu::onCopySurface(GrSurface* dst, return false; } - void GrGLGpu::createCopyPrograms() { for (size_t i = 0; i < SK_ARRAY_COUNT(fCopyPrograms); ++i) { fCopyPrograms[i].fProgram = 0; } const char* version = this->glCaps().glslCaps()->versionDeclString(); - static const GrSLType kSamplerTypes[2] = { kSampler2D_GrSLType, kSamplerExternal_GrSLType }; - SkASSERT(2 == SK_ARRAY_COUNT(fCopyPrograms)); - int programCount = this->glCaps().externalTextureSupport() ? 2 : 1; - for (int i = 0; i < programCount; ++i) { + static const GrSLType kSamplerTypes[3] = { kSampler2D_GrSLType, kSamplerExternal_GrSLType, + kSampler2DRect_GrSLType }; + SkASSERT(3 == SK_ARRAY_COUNT(fCopyPrograms)); + for (int i = 0; i < 3; ++i) { + if (kSamplerExternal_GrSLType == kSamplerTypes[i] && + !this->glCaps().externalTextureSupport()) { + continue; + } + if (kSampler2DRect_GrSLType == kSamplerTypes[i] && + !this->glCaps().rectangleTextureSupport()) { + continue; + } GrGLSLShaderVar aVertex("a_vertex", kVec2f_GrSLType, GrShaderVar::kAttribute_TypeModifier); GrGLSLShaderVar uTexCoordXform("u_texCoordXform", kVec4f_GrSLType, GrShaderVar::kUniform_TypeModifier); @@ -2995,7 +2902,7 @@ void GrGLGpu::createCopyPrograms() { GrShaderVar::kVaryingOut_TypeModifier); GrGLSLShaderVar oFragColor("o_FragColor", kVec4f_GrSLType, GrShaderVar::kOut_TypeModifier); - + SkString vshaderTxt(version); aVertex.appendDecl(this->glCaps().glslCaps(), &vshaderTxt); vshaderTxt.append(";"); @@ -3042,7 +2949,7 @@ void GrGLGpu::createCopyPrograms() { " %s = %s(u_texture, v_texCoord);" "}", fsOutName, - GrGLSLTexture2DFunctionName(kVec2f_GrSLType, this->glslGeneration()) + GrGLSLTexture2DFunctionName(kVec2f_GrSLType, kSamplerTypes[i], this->glslGeneration()) ); GL_CALL_RET(fCopyPrograms[i].fProgram, CreateProgram()); @@ -3075,7 +2982,7 @@ void GrGLGpu::createCopyPrograms() { GL_CALL(DeleteShader(vshader)); GL_CALL(DeleteShader(fshader)); } - + fCopyProgramArrayBuffer = 0; GL_CALL(GenBuffers(1, &fCopyProgramArrayBuffer)); fHWGeometryState.setVertexBufferID(this, fCopyProgramArrayBuffer); static const GrGLfloat vdata[] = { @@ -3091,6 +2998,163 @@ void GrGLGpu::createCopyPrograms() { GR_GL_STATIC_DRAW)); } +void GrGLGpu::createWireRectProgram() { + SkASSERT(!fWireRectProgram.fProgram); + GrGLSLShaderVar uColor("u_color", kVec4f_GrSLType, GrShaderVar::kUniform_TypeModifier); + GrGLSLShaderVar uRect("u_rect", kVec4f_GrSLType, GrShaderVar::kUniform_TypeModifier); + GrGLSLShaderVar aVertex("a_vertex", kVec2f_GrSLType, GrShaderVar::kAttribute_TypeModifier); + const char* version = this->glCaps().glslCaps()->versionDeclString(); + + // The rect uniform specifies the rectangle in NDC space as a vec4 (left,top,right,bottom). The + // program is used with a vbo containing the unit square. Vertices are computed from the rect + // uniform using the 4 vbo vertices. + SkString vshaderTxt(version); + aVertex.appendDecl(this->glCaps().glslCaps(), &vshaderTxt); + vshaderTxt.append(";"); + uRect.appendDecl(this->glCaps().glslCaps(), &vshaderTxt); + vshaderTxt.append(";"); + vshaderTxt.append( + "// Wire Rect Program VS\n" + "void main() {" + " gl_Position.x = u_rect.x + a_vertex.x * (u_rect.z - u_rect.x);" + " gl_Position.y = u_rect.y + a_vertex.y * (u_rect.w - u_rect.y);" + " gl_Position.zw = vec2(0, 1);" + "}" + ); + + GrGLSLShaderVar oFragColor("o_FragColor", kVec4f_GrSLType, GrShaderVar::kOut_TypeModifier); + + SkString fshaderTxt(version); + GrGLSLAppendDefaultFloatPrecisionDeclaration(kDefault_GrSLPrecision, + *this->glCaps().glslCaps(), + &fshaderTxt); + uColor.appendDecl(this->glCaps().glslCaps(), &fshaderTxt); + fshaderTxt.append(";"); + const char* fsOutName; + if (this->glCaps().glslCaps()->mustDeclareFragmentShaderOutput()) { + oFragColor.appendDecl(this->glCaps().glslCaps(), &fshaderTxt); + fshaderTxt.append(";"); + fsOutName = oFragColor.c_str(); + } else { + fsOutName = "gl_FragColor"; + } + fshaderTxt.appendf( + "// Write Rect Program FS\n" + "void main() {" + " %s = %s;" + "}", + fsOutName, + uColor.c_str() + ); + + GL_CALL_RET(fWireRectProgram.fProgram, CreateProgram()); + const char* str; + GrGLint length; + + str = vshaderTxt.c_str(); + length = SkToInt(vshaderTxt.size()); + GrGLuint vshader = GrGLCompileAndAttachShader(*fGLContext, fWireRectProgram.fProgram, + GR_GL_VERTEX_SHADER, &str, &length, 1, + &fStats); + + str = fshaderTxt.c_str(); + length = SkToInt(fshaderTxt.size()); + GrGLuint fshader = GrGLCompileAndAttachShader(*fGLContext, fWireRectProgram.fProgram, + GR_GL_FRAGMENT_SHADER, &str, &length, 1, + &fStats); + + GL_CALL(LinkProgram(fWireRectProgram.fProgram)); + + GL_CALL_RET(fWireRectProgram.fColorUniform, + GetUniformLocation(fWireRectProgram.fProgram, "u_color")); + GL_CALL_RET(fWireRectProgram.fRectUniform, + GetUniformLocation(fWireRectProgram.fProgram, "u_rect")); + GL_CALL(BindAttribLocation(fWireRectProgram.fProgram, 0, "a_vertex")); + + GL_CALL(DeleteShader(vshader)); + GL_CALL(DeleteShader(fshader)); + GL_CALL(GenBuffers(1, &fWireRectArrayBuffer)); + fHWGeometryState.setVertexBufferID(this, fWireRectArrayBuffer); + static const GrGLfloat vdata[] = { + 0, 0, + 0, 1, + 1, 1, + 1, 0, + }; + GL_ALLOC_CALL(this->glInterface(), + BufferData(GR_GL_ARRAY_BUFFER, + (GrGLsizeiptr) sizeof(vdata), + vdata, // data ptr + GR_GL_STATIC_DRAW)); +} + +void GrGLGpu::drawDebugWireRect(GrRenderTarget* rt, const SkIRect& rect, GrColor color) { + // TODO: This should swizzle the output to match dst's config, though it is a debugging + // visualization. + + this->handleDirtyContext(); + if (!fWireRectProgram.fProgram) { + this->createWireRectProgram(); + } + + int w = rt->width(); + int h = rt->height(); + + // Compute the edges of the rectangle (top,left,right,bottom) in NDC space. Must consider + // whether the render target is flipped or not. + GrGLfloat edges[4]; + edges[0] = SkIntToScalar(rect.fLeft) + 0.5f; + edges[2] = SkIntToScalar(rect.fRight) - 0.5f; + if (kBottomLeft_GrSurfaceOrigin == rt->origin()) { + edges[1] = h - (SkIntToScalar(rect.fTop) + 0.5f); + edges[3] = h - (SkIntToScalar(rect.fBottom) - 0.5f); + } else { + edges[1] = SkIntToScalar(rect.fTop) + 0.5f; + edges[3] = SkIntToScalar(rect.fBottom) - 0.5f; + } + edges[0] = 2 * edges[0] / w - 1.0f; + edges[1] = 2 * edges[1] / h - 1.0f; + edges[2] = 2 * edges[2] / w - 1.0f; + edges[3] = 2 * edges[3] / h - 1.0f; + + GrGLfloat channels[4]; + static const GrGLfloat scale255 = 1.f / 255.f; + channels[0] = GrColorUnpackR(color) * scale255; + channels[1] = GrColorUnpackG(color) * scale255; + channels[2] = GrColorUnpackB(color) * scale255; + channels[3] = GrColorUnpackA(color) * scale255; + + GrGLRenderTarget* glRT = static_cast(rt->asRenderTarget()); + this->flushRenderTarget(glRT, &rect); + + GL_CALL(UseProgram(fWireRectProgram.fProgram)); + fHWProgramID = fWireRectProgram.fProgram; + + fHWGeometryState.setVertexArrayID(this, 0); + + GrGLAttribArrayState* attribs = + fHWGeometryState.bindArrayAndBufferToDraw(this, fWireRectArrayBuffer); + attribs->set(this, 0, fWireRectArrayBuffer, 2, GR_GL_FLOAT, false, 2 * sizeof(GrGLfloat), 0); + attribs->disableUnusedArrays(this, 0x1); + + GL_CALL(Uniform4fv(fWireRectProgram.fRectUniform, 1, edges)); + GL_CALL(Uniform4fv(fWireRectProgram.fColorUniform, 1, channels)); + + GrXferProcessor::BlendInfo blendInfo; + blendInfo.reset(); + this->flushBlend(blendInfo, GrSwizzle::RGBA()); + this->flushColorWrite(true); + this->flushDrawFace(GrPipelineBuilder::kBoth_DrawFace); + this->flushHWAAState(glRT, false); + this->disableScissor(); + GrStencilSettings stencil; + stencil.setDisabled(); + this->flushStencil(stencil); + + GL_CALL(DrawArrays(GR_GL_LINE_LOOP, 0, 4)); +} + + void GrGLGpu::copySurfaceAsDraw(GrSurface* dst, GrSurface* src, const SkIRect& srcRect, @@ -3115,8 +3179,7 @@ void GrGLGpu::copySurfaceAsDraw(GrSurface* dst, GrGLAttribArrayState* attribs = fHWGeometryState.bindArrayAndBufferToDraw(this, fCopyProgramArrayBuffer); - attribs->set(this, 0, fCopyProgramArrayBuffer, 2, GR_GL_FLOAT, false, - 2 * sizeof(GrGLfloat), 0); + attribs->set(this, 0, fCopyProgramArrayBuffer, 2, GR_GL_FLOAT, false, 2 * sizeof(GrGLfloat), 0); attribs->disableUnusedArrays(this, 0x1); // dst rect edges in NDC (-1 to 1) @@ -3131,16 +3194,23 @@ void GrGLGpu::copySurfaceAsDraw(GrSurface* dst, dy1 = -dy1; } - // src rect edges in normalized texture space (0 to 1) - int sw = src->width(); + GrGLfloat sx0 = (GrGLfloat)srcRect.fLeft; + GrGLfloat sx1 = (GrGLfloat)(srcRect.fLeft + w); + GrGLfloat sy0 = (GrGLfloat)srcRect.fTop; + GrGLfloat sy1 = (GrGLfloat)(srcRect.fTop + h); int sh = src->height(); - GrGLfloat sx0 = (GrGLfloat)srcRect.fLeft / sw; - GrGLfloat sx1 = (GrGLfloat)(srcRect.fLeft + w) / sw; - GrGLfloat sy0 = (GrGLfloat)srcRect.fTop / sh; - GrGLfloat sy1 = (GrGLfloat)(srcRect.fTop + h) / sh; if (kBottomLeft_GrSurfaceOrigin == src->origin()) { - sy0 = 1.f - sy0; - sy1 = 1.f - sy1; + sy0 = sh - sy0; + sy1 = sh - sy1; + } + // src rect edges in normalized texture space (0 to 1) unless we're using a RECTANGLE texture. + GrGLenum srcTarget = srcTex->target(); + if (GR_GL_TEXTURE_RECTANGLE != srcTarget) { + int sw = src->width(); + sx0 /= sw; + sx1 /= sw; + sy0 /= sh; + sy1 /= sh; } GL_CALL(Uniform4f(fCopyPrograms[progIdx].fPosXformUniform, dx1 - dx0, dy1 - dy0, dx0, dy0)); @@ -3150,7 +3220,7 @@ void GrGLGpu::copySurfaceAsDraw(GrSurface* dst, GrXferProcessor::BlendInfo blendInfo; blendInfo.reset(); - this->flushBlend(blendInfo); + this->flushBlend(blendInfo, GrSwizzle::RGBA()); this->flushColorWrite(true); this->flushDrawFace(GrPipelineBuilder::kBoth_DrawFace); this->flushHWAAState(dstRT, false); @@ -3283,8 +3353,12 @@ void GrGLGpu::xferBarrier(GrRenderTarget* rt, GrXferBarrierType type) { GrBackendObject GrGLGpu::createTestingOnlyBackendTexture(void* pixels, int w, int h, GrPixelConfig config) const { + if (!this->caps()->isConfigTexturable(config)) { + return false; + } GrGLTextureInfo* info = new GrGLTextureInfo; info->fTarget = GR_GL_TEXTURE_2D; + info->fID = 0; GL_CALL(GenTextures(1, &info->fID)); GL_CALL(ActiveTexture(GR_GL_TEXTURE0)); GL_CALL(PixelStorei(GR_GL_UNPACK_ALIGNMENT, 1)); @@ -3294,11 +3368,19 @@ GrBackendObject GrGLGpu::createTestingOnlyBackendTexture(void* pixels, int w, in GL_CALL(TexParameteri(info->fTarget, GR_GL_TEXTURE_WRAP_S, GR_GL_CLAMP_TO_EDGE)); GL_CALL(TexParameteri(info->fTarget, GR_GL_TEXTURE_WRAP_T, GR_GL_CLAMP_TO_EDGE)); - GrGLenum internalFormat = 0x0; // suppress warning - GrGLenum externalFormat = 0x0; // suppress warning - GrGLenum externalType = 0x0; // suppress warning + GrGLenum internalFormat; + GrGLenum externalFormat; + GrGLenum externalType; - this->configToGLFormats(config, false, &internalFormat, &externalFormat, &externalType); + if (!this->glCaps().getTexImageFormats(config, config, &internalFormat, &externalFormat, + &externalType)) { + delete info; +#ifdef SK_IGNORE_GL_TEXTURE_TARGET + return 0; +#else + return reinterpret_cast(nullptr); +#endif + } GL_CALL(TexImage2D(info->fTarget, 0, internalFormat, w, h, 0, externalFormat, externalType, pixels)); diff --git a/gfx/skia/skia/src/gpu/gl/GrGLGpu.h b/gfx/skia/skia/src/gpu/gl/GrGLGpu.h index 2d05396f87..b2eec45835 100644 --- a/gfx/skia/skia/src/gpu/gl/GrGLGpu.h +++ b/gfx/skia/skia/src/gpu/gl/GrGLGpu.h @@ -16,6 +16,7 @@ #include "GrGLRenderTarget.h" #include "GrGLStencilAttachment.h" #include "GrGLTexture.h" +#include "GrGLTransferBuffer.h" #include "GrGLVertexArray.h" #include "GrGLVertexBuffer.h" #include "GrGpu.h" @@ -25,6 +26,7 @@ class GrPipeline; class GrNonInstancedVertices; +class GrSwizzle; #ifdef SK_DEVELOPER #define PROGRAM_CACHE_STATS @@ -101,12 +103,12 @@ public: void releaseBuffer(GrGLuint id, GrGLenum type); // sizes are in bytes - void* mapBuffer(GrGLuint id, GrGLenum type, bool dynamic, size_t currentSize, + void* mapBuffer(GrGLuint id, GrGLenum type, GrGLBufferImpl::Usage usage, size_t currentSize, size_t requestedSize); void unmapBuffer(GrGLuint id, GrGLenum type, void* mapPtr); - void bufferData(GrGLuint id, GrGLenum type, bool dynamic, size_t currentSize, + void bufferData(GrGLuint id, GrGLenum type, GrGLBufferImpl::Usage usage, size_t currentSize, const void* src, size_t srcSizeInBytes); const GrGLContext* glContextForTesting() const override { @@ -130,6 +132,8 @@ public: void resetShaderCacheForTesting() const override; + void drawDebugWireRect(GrRenderTarget*, const SkIRect&, GrColor) override; + private: GrGLGpu(GrGLContext* ctx, GrContext* context); @@ -145,11 +149,12 @@ private: const void* srcData) override; GrVertexBuffer* onCreateVertexBuffer(size_t size, bool dynamic) override; GrIndexBuffer* onCreateIndexBuffer(size_t size, bool dynamic) override; + GrTransferBuffer* onCreateTransferBuffer(size_t size, TransferType type) override; GrTexture* onWrapBackendTexture(const GrBackendTextureDesc&, GrWrapOwnership) override; GrRenderTarget* onWrapBackendRenderTarget(const GrBackendRenderTargetDesc&, GrWrapOwnership) override; // Given a GrPixelConfig return the index into the stencil format array on GrGLCaps to a - // compatible stencil format. + // compatible stencil format, or negative if there is no compatible stencil format. int getCompatibleStencilIndex(GrPixelConfig config); void onClear(GrRenderTarget*, const SkIRect& rect, GrColor color) override; @@ -168,6 +173,11 @@ private: GrPixelConfig config, const void* buffer, size_t rowBytes) override; + bool onTransferPixels(GrSurface*, + int left, int top, int width, int height, + GrPixelConfig config, GrTransferBuffer* buffer, + size_t offset, size_t rowBytes) override; + void onResolveRenderTarget(GrRenderTarget* target) override; void onDraw(const DrawArgs&, const GrNonInstancedVertices&) override; @@ -190,8 +200,7 @@ private: const GrNonInstancedVertices& vertices, size_t* indexOffsetInBytes); - // Subclasses should call this to flush the blend state. - void flushBlend(const GrXferProcessor::BlendInfo& blendInfo); + void flushBlend(const GrXferProcessor::BlendInfo& blendInfo, const GrSwizzle&); bool hasExtension(const char* ext) const { return fGLContext->hasExtension(ext); } @@ -279,15 +288,15 @@ private: void flushStencil(const GrStencilSettings&); void flushHWAAState(GrRenderTarget* rt, bool useHWAA); - bool configToGLFormats(GrPixelConfig config, - bool getSizedInternal, - GrGLenum* internalFormat, - GrGLenum* externalFormat, - GrGLenum* externalType) const; // helper for onCreateTexture and writeTexturePixels + enum UploadType { + kNewTexture_UploadType, // we are creating a new texture + kWrite_UploadType, // we are using TexSubImage2D to copy data to an existing texture + kTransfer_UploadType, // we are using a transfer buffer to copy data + }; bool uploadTexData(const GrSurfaceDesc& desc, GrGLenum target, - bool isNewTexture, + UploadType uploadType, int left, int top, int width, int height, GrPixelConfig dataConfig, const void* data, @@ -302,7 +311,7 @@ private: bool uploadCompressedTexData(const GrSurfaceDesc& desc, GrGLenum target, const void* data, - bool isNewTexture = true, + UploadType uploadType = kNewTexture_UploadType, int left = 0, int top = 0, int width = -1, int height = -1); @@ -326,6 +335,8 @@ private: SkAutoTUnref fGLContext; void createCopyPrograms(); + void createWireRectProgram(); + void createUnitRectBuffer(); // GL program-related state ProgramCache* fProgramCache; @@ -494,24 +505,6 @@ private: } } fHWBlendState; - /** IDs for copy surface program. */ - struct { - GrGLuint fProgram; - GrGLint fTextureUniform; - GrGLint fTexCoordXformUniform; - GrGLint fPosXformUniform; - } fCopyPrograms[2]; - GrGLuint fCopyProgramArrayBuffer; - - static int TextureTargetToCopyProgramIdx(GrGLenum target) { - if (target == GR_GL_TEXTURE_2D) { - return 0; - } else { - SkASSERT(target == GR_GL_TEXTURE_EXTERNAL); - return 1; - } - } - TriState fMSAAEnabled; GrStencilSettings fHWStencilSettings; @@ -523,12 +516,37 @@ private: uint32_t fHWBoundRenderTargetUniqueID; TriState fHWSRGBFramebuffer; SkTArray fHWBoundTextureUniqueIDs; - ///@} - // Mapping of pixel configs to known supported stencil formats to be used - // when adding a stencil buffer to a framebuffer. - int fPixelConfigToStencilIndex[kGrPixelConfigCnt]; + /** IDs for copy surface program. */ + struct { + GrGLuint fProgram; + GrGLint fTextureUniform; + GrGLint fTexCoordXformUniform; + GrGLint fPosXformUniform; + } fCopyPrograms[3]; + GrGLuint fCopyProgramArrayBuffer; + + struct { + GrGLuint fProgram; + GrGLint fColorUniform; + GrGLint fRectUniform; + } fWireRectProgram; + GrGLuint fWireRectArrayBuffer; + + static int TextureTargetToCopyProgramIdx(GrGLenum target) { + switch (target) { + case GR_GL_TEXTURE_2D: + return 0; + case GR_GL_TEXTURE_EXTERNAL: + return 1; + case GR_GL_TEXTURE_RECTANGLE: + return 2; + default: + SkFAIL("Unexpected texture target type."); + return 0; + } + } typedef GrGpu INHERITED; friend class GrGLPathRendering; // For accessing setTextureUnit. diff --git a/gfx/skia/skia/src/gpu/gl/GrGLIndexBuffer.cpp b/gfx/skia/skia/src/gpu/gl/GrGLIndexBuffer.cpp index 230d9a4693..5a794ad824 100644 --- a/gfx/skia/skia/src/gpu/gl/GrGLIndexBuffer.cpp +++ b/gfx/skia/skia/src/gpu/gl/GrGLIndexBuffer.cpp @@ -10,7 +10,8 @@ #include "SkTraceMemoryDump.h" GrGLIndexBuffer::GrGLIndexBuffer(GrGLGpu* gpu, const Desc& desc) - : INHERITED(gpu, desc.fSizeInBytes, desc.fDynamic, 0 == desc.fID) + : INHERITED(gpu, desc.fSizeInBytes, GrGLBufferImpl::kDynamicDraw_Usage == desc.fUsage, + 0 == desc.fID) , fImpl(gpu, desc, GR_GL_ELEMENT_ARRAY_BUFFER) { this->registerWithCache(); } diff --git a/gfx/skia/skia/src/gpu/gl/GrGLInterface.cpp b/gfx/skia/skia/src/gpu/gl/GrGLInterface.cpp index da3c656c18..b8d7db6b2e 100644 --- a/gfx/skia/skia/src/gpu/gl/GrGLInterface.cpp +++ b/gfx/skia/skia/src/gpu/gl/GrGLInterface.cpp @@ -39,6 +39,9 @@ const GrGLInterface* GrGLInterfaceRemoveNVPR(const GrGLInterface* interface) { GrGLInterface* newInterface = GrGLInterface::NewClone(interface); newInterface->fExtensions.remove("GL_NV_path_rendering"); + newInterface->fExtensions.remove("GL_CHROMIUM_path_rendering"); + newInterface->fFunctions.fMatrixLoadf = nullptr; + newInterface->fFunctions.fMatrixLoadIdentity = nullptr; newInterface->fFunctions.fPathCommands = nullptr; newInterface->fFunctions.fPathParameteri = nullptr; newInterface->fFunctions.fPathParameterf = nullptr; @@ -538,7 +541,8 @@ bool GrGLInterface::validate() const { } } - if (fExtensions.has("GL_NV_framebuffer_mixed_samples")) { + if (fExtensions.has("GL_NV_framebuffer_mixed_samples") || + fExtensions.has("GL_CHROMIUM_framebuffer_mixed_samples")) { if (nullptr == fFunctions.fCoverageModulation) { RETURN_FALSE_INTERFACE } diff --git a/gfx/skia/skia/src/gpu/gl/GrGLNameAllocator.cpp b/gfx/skia/skia/src/gpu/gl/GrGLNameAllocator.cpp deleted file mode 100644 index 03123a6d86..0000000000 --- a/gfx/skia/skia/src/gpu/gl/GrGLNameAllocator.cpp +++ /dev/null @@ -1,370 +0,0 @@ - -/* - * Copyright 2014 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "GrGLNameAllocator.h" - -/** - * This is the abstract base class for a nonempty AVL tree that tracks allocated - * names within the half-open range [fFirst, fEnd). The inner nodes can be - * sparse (meaning not every name within the range is necessarily allocated), - * but the bounds are tight, so fFirst *is* guaranteed to be allocated, and so - * is fEnd - 1. - */ -class GrGLNameAllocator::SparseNameRange : public SkRefCnt { -public: - virtual ~SparseNameRange() {} - - /** - * Return the beginning of the range. first() is guaranteed to be allocated. - * - * @return The first name in the range. - */ - GrGLuint first() const { return fFirst; } - - /** - * Return the end of the range. end() - 1 is guaranteed to be allocated. - * - * @return One plus the final name in the range. - */ - GrGLuint end() const { return fEnd; } - - /** - * Return the height of the tree. This can only be nonzero at an inner node. - * - * @return 0 if the implementation is a leaf node, - * The nonzero height of the tree otherwise. - */ - GrGLuint height() const { return fHeight; } - - /** - * Allocate a name from strictly inside this range. The call will fail if - * there is not a free name within. - * - * @param outName A pointer that receives the allocated name. outName will - * be set to zero if there were no free names within the - * range [fFirst, fEnd). - * @return The resulting SparseNameRange after the allocation. Note that - * this call is destructive, so the original SparseNameRange will no - * longer be valid afterward. The caller must always update its - * pointer with the new SparseNameRange. - */ - virtual SparseNameRange* SK_WARN_UNUSED_RESULT internalAllocate(GrGLuint* outName) = 0; - - /** - * Remove the leftmost leaf node from this range (or the entire thing if it - * *is* a leaf node). This is an internal helper method that is used after - * an allocation if one contiguous range became adjacent to another. (The - * range gets removed so the one immediately before can be extended, - * collapsing the two into one.) - * - * @param removedCount A pointer that receives the size of the contiguous - range that was removed. - * @return The resulting SparseNameRange after the removal (or nullptr if it - * became empty). Note that this call is destructive, so the - * original SparseNameRange will no longer be valid afterward. The - * caller must always update its pointer with the new - * SparseNameRange. - */ - virtual SparseNameRange* SK_WARN_UNUSED_RESULT removeLeftmostContiguousRange(GrGLuint* removedCount) = 0; - - /** - * Append adjacent allocated names to the end of this range. This operation - * does not affect the structure of the tree. The caller is responsible for - * ensuring the new names won't overlap sibling ranges, if any. - * - * @param count The number of adjacent names to append. - * @return The first name appended. - */ - virtual GrGLuint appendNames(GrGLuint count) = 0; - - /** - * Prepend adjacent allocated names behind the beginning of this range. This - * operation does not affect the structure of the tree. The caller is - * responsible for ensuring the new names won't overlap sibling ranges, if - * any. - * - * @param count The number of adjacent names to prepend. - * @return The final name prepended (the one with the lowest value). - */ - virtual GrGLuint prependNames(GrGLuint count) = 0; - - /** - * Free a name so it is no longer tracked as allocated. If the name is at - * the very beginning or very end of the range, the boundaries [fFirst, fEnd) - * will be tightened. - * - * @param name The name to free. Not-allocated names are silently ignored - * the same way they are in the OpenGL spec. - * @return The resulting SparseNameRange after the free (or nullptr if it - * became empty). Note that this call is destructive, so the - * original SparseNameRange will no longer be valid afterward. The - * caller must always update its pointer with the new - * SparseNameRange. - */ - virtual SparseNameRange* SK_WARN_UNUSED_RESULT free(GrGLuint name) = 0; - -protected: - SparseNameRange* takeRef() { - this->ref(); - return this; - } - - GrGLuint fFirst; - GrGLuint fEnd; - GrGLuint fHeight; -}; - -/** - * This class is the SparseNameRange implementation for an inner node. It is an - * AVL tree with non-null, non-adjacent left and right children. - */ -class GrGLNameAllocator::SparseNameTree : public SparseNameRange { -public: - SparseNameTree(SparseNameRange* left, SparseNameRange* right) - : fLeft(left), - fRight(right) { - SkASSERT(fLeft.get()); - SkASSERT(fRight.get()); - this->updateStats(); - } - - SparseNameRange* SK_WARN_UNUSED_RESULT internalAllocate(GrGLuint* outName) override { - // Try allocating the range inside fLeft's internal gaps. - fLeft.reset(fLeft->internalAllocate(outName)); - if (0 != *outName) { - this->updateStats(); - return this->rebalance(); - } - - if (fLeft->end() + 1 == fRight->first()) { - // It closed the gap between fLeft and fRight; merge. - GrGLuint removedCount; - fRight.reset(fRight->removeLeftmostContiguousRange(&removedCount)); - *outName = fLeft->appendNames(1 + removedCount); - if (nullptr == fRight.get()) { - return fLeft.detach(); - } - this->updateStats(); - return this->rebalance(); - } - - // There is guaranteed to be a gap between fLeft and fRight, and the - // "size 1" case has already been covered. - SkASSERT(fLeft->end() + 1 < fRight->first()); - *outName = fLeft->appendNames(1); - return this->takeRef(); - } - - SparseNameRange* SK_WARN_UNUSED_RESULT removeLeftmostContiguousRange(GrGLuint* removedCount) override { - fLeft.reset(fLeft->removeLeftmostContiguousRange(removedCount)); - if (nullptr == fLeft) { - return fRight.detach(); - } - this->updateStats(); - return this->rebalance(); - } - - GrGLuint appendNames(GrGLuint count) override { - SkASSERT(fEnd + count > fEnd); // Check for integer wrap. - GrGLuint name = fRight->appendNames(count); - SkASSERT(fRight->end() == fEnd + count); - this->updateStats(); - return name; - } - - GrGLuint prependNames(GrGLuint count) override { - SkASSERT(fFirst > count); // We can't allocate at or below 0. - GrGLuint name = fLeft->prependNames(count); - SkASSERT(fLeft->first() == fFirst - count); - this->updateStats(); - return name; - } - - SparseNameRange* SK_WARN_UNUSED_RESULT free(GrGLuint name) override { - if (name < fLeft->end()) { - fLeft.reset(fLeft->free(name)); - if (nullptr == fLeft) { - // fLeft became empty after the free. - return fRight.detach(); - } - this->updateStats(); - return this->rebalance(); - } else { - fRight.reset(fRight->free(name)); - if (nullptr == fRight) { - // fRight became empty after the free. - return fLeft.detach(); - } - this->updateStats(); - return this->rebalance(); - } - } - -private: - typedef SkAutoTUnref SparseNameTree::* ChildRange; - - SparseNameRange* SK_WARN_UNUSED_RESULT rebalance() { - if (fLeft->height() > fRight->height() + 1) { - return this->rebalanceImpl<&SparseNameTree::fLeft, &SparseNameTree::fRight>(); - } - if (fRight->height() > fLeft->height() + 1) { - return this->rebalanceImpl<&SparseNameTree::fRight, &SparseNameTree::fLeft>(); - } - return this->takeRef(); - } - - /** - * Rebalance the tree using rotations, as described in the AVL algorithm: - * http://en.wikipedia.org/wiki/AVL_tree#Insertion - */ - template - SparseNameRange* SK_WARN_UNUSED_RESULT rebalanceImpl() { - // We should be calling rebalance() enough that the tree never gets more - // than one rotation off balance. - SkASSERT(2 == (this->*Tall)->height() - (this->*Short)->height()); - - // Ensure we are in the 'Left Left' or 'Right Right' case: - // http://en.wikipedia.org/wiki/AVL_tree#Insertion - SparseNameTree* tallChild = static_cast((this->*Tall).get()); - if ((tallChild->*Tall)->height() < (tallChild->*Short)->height()) { - (this->*Tall).reset(tallChild->rotate()); - } - - // Perform a rotation to balance the tree. - return this->rotate(); - } - - /** - * Perform a node rotation, as described in the AVL algorithm: - * http://en.wikipedia.org/wiki/AVL_tree#Insertion - */ - template - SparseNameRange* SK_WARN_UNUSED_RESULT rotate() { - SparseNameTree* newRoot = static_cast((this->*Tall).detach()); - - (this->*Tall).reset((newRoot->*Short).detach()); - this->updateStats(); - - (newRoot->*Short).reset(this->takeRef()); - newRoot->updateStats(); - - return newRoot; - } - - void updateStats() { - SkASSERT(fLeft->end() < fRight->first()); // There must be a gap between left and right. - fFirst = fLeft->first(); - fEnd = fRight->end(); - fHeight = 1 + SkMax32(fLeft->height(), fRight->height()); - } - - SkAutoTUnref fLeft; - SkAutoTUnref fRight; -}; - -/** - * This class is the SparseNameRange implementation for a leaf node. It just a - * contiguous range of allocated names. - */ -class GrGLNameAllocator::ContiguousNameRange : public SparseNameRange { -public: - ContiguousNameRange(GrGLuint first, GrGLuint end) { - SkASSERT(first < end); - fFirst = first; - fEnd = end; - fHeight = 0; - } - - SparseNameRange* SK_WARN_UNUSED_RESULT internalAllocate(GrGLuint* outName) override { - *outName = 0; // No internal gaps, we are contiguous. - return this->takeRef(); - } - - SparseNameRange* SK_WARN_UNUSED_RESULT removeLeftmostContiguousRange(GrGLuint* removedCount) override { - *removedCount = fEnd - fFirst; - return nullptr; - } - - GrGLuint appendNames(GrGLuint count) override { - SkASSERT(fEnd + count > fEnd); // Check for integer wrap. - GrGLuint name = fEnd; - fEnd += count; - return name; - } - - GrGLuint prependNames(GrGLuint count) override { - SkASSERT(fFirst > count); // We can't allocate at or below 0. - fFirst -= count; - return fFirst; - } - - SparseNameRange* SK_WARN_UNUSED_RESULT free(GrGLuint name) override { - if (name < fFirst || name >= fEnd) { - // Not-allocated names are silently ignored. - return this->takeRef(); - } - - if (fFirst == name) { - ++fFirst; - return (fEnd == fFirst) ? nullptr : this->takeRef(); - } - - if (fEnd == name + 1) { - --fEnd; - return this->takeRef(); - } - - SparseNameRange* left = new ContiguousNameRange(fFirst, name); - SparseNameRange* right = this->takeRef(); - fFirst = name + 1; - return new SparseNameTree(left, right); - } -}; - -GrGLNameAllocator::GrGLNameAllocator(GrGLuint firstName, GrGLuint endName) - : fFirstName(firstName), - fEndName(endName) { - SkASSERT(firstName > 0); - SkASSERT(endName > firstName); -} - -GrGLNameAllocator::~GrGLNameAllocator() { -} - -GrGLuint GrGLNameAllocator::allocateName() { - if (nullptr == fAllocatedNames.get()) { - fAllocatedNames.reset(new ContiguousNameRange(fFirstName, fFirstName + 1)); - return fFirstName; - } - - if (fAllocatedNames->first() > fFirstName) { - return fAllocatedNames->prependNames(1); - } - - GrGLuint name; - fAllocatedNames.reset(fAllocatedNames->internalAllocate(&name)); - if (0 != name) { - return name; - } - - if (fAllocatedNames->end() < fEndName) { - return fAllocatedNames->appendNames(1); - } - - // Out of names. - return 0; -} - -void GrGLNameAllocator::free(GrGLuint name) { - if (!fAllocatedNames.get()) { - // Not-allocated names are silently ignored. - return; - } - - fAllocatedNames.reset(fAllocatedNames->free(name)); -} diff --git a/gfx/skia/skia/src/gpu/gl/GrGLNameAllocator.h b/gfx/skia/skia/src/gpu/gl/GrGLNameAllocator.h deleted file mode 100644 index 8b0b2a244a..0000000000 --- a/gfx/skia/skia/src/gpu/gl/GrGLNameAllocator.h +++ /dev/null @@ -1,86 +0,0 @@ - -/* - * Copyright 2014 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#ifndef GrGLNameAllocator_DEFINED -#define GrGLNameAllocator_DEFINED - -#include "SkRefCnt.h" -#include "gl/GrGLTypes.h" - -/** - * This class assumes ownership of an explicit range of OpenGL object names and - * manages allocations within that range. This allows the app to generate new - * objects on the client side without making round trips to the GL server. - */ -class GrGLNameAllocator { -public: - /** - * Constructs a name allocator that produces names within the explicit - * half-open range [firstName, end). Note that the caller will most likely - * need to call glGen* beforehand to reserve a range within the GL driver, - * and then invoke this constructor with that range. - * - * @param firstName The first name in the range owned by this class. Must be - greater than zero. - * @param endName The first past-the-end name beyond the range owned by - this class. Must be >= firstName. - */ - GrGLNameAllocator(GrGLuint firstName, GrGLuint endName); - - /** - * Destructs the name allocator. The caller is responsible for calling the - * appropriate glDelete* on the range if necessary. - */ - ~GrGLNameAllocator(); - - /** - * Return the beginning of this class's range. - * - * @return The first name in the range owned by this class. - */ - GrGLuint firstName() const { return fFirstName; } - - /** - * Return the end of this class's range. Note that endName() is not owned by - * this class. - * - * @return One plus the final name in the range owned by this class. - */ - GrGLuint endName() const { return fEndName; } - - /** - * Allocate an OpenGL object name from within this class's range. - * - * @return The name if one was available, - 0 if all the names in the range were already in use. - */ - GrGLuint allocateName(); - - /** - * Free an OpenGL object name, allowing it to be returned by a future call - * to allocateName(). Note that the caller should most likely redefine the - * object as empty to deallocate any underlying GPU memory before calling - * this method (but not call glDelete*, since that would free up the name - * within the driver itself). - * - * @param name The object name to free. Not-allocated names are silently - * ignored the same way they are in the OpenGL spec. - */ - void free(GrGLuint name); - -private: - class SparseNameRange; - class SparseNameTree; - class ContiguousNameRange; - - const GrGLuint fFirstName; - const GrGLuint fEndName; - SkAutoTUnref fAllocatedNames; -}; - -#endif diff --git a/gfx/skia/skia/src/gpu/gl/GrGLPath.cpp b/gfx/skia/skia/src/gpu/gl/GrGLPath.cpp index d1fc39dffc..067b74e154 100644 --- a/gfx/skia/skia/src/gpu/gl/GrGLPath.cpp +++ b/gfx/skia/skia/src/gpu/gl/GrGLPath.cpp @@ -179,6 +179,23 @@ inline bool init_path_object_for_general_path(GrGLGpu* gpu, GrGLuint pathID, pathCoords.count(), GR_GL_FLOAT, &pathCoords[0])); return true; } + +/* + * For now paths only natively support winding and even odd fill types + */ +static GrPathRendering::FillType convert_skpath_filltype(SkPath::FillType fill) { + switch (fill) { + default: + SkFAIL("Incomplete Switch\n"); + case SkPath::kWinding_FillType: + case SkPath::kInverseWinding_FillType: + return GrPathRendering::kWinding_FillType; + case SkPath::kEvenOdd_FillType: + case SkPath::kInverseEvenOdd_FillType: + return GrPathRendering::kEvenOdd_FillType; + } +} + } // namespace bool GrGLPath::InitPathObjectPathDataCheckingDegenerates(GrGLGpu* gpu, GrGLuint pathID, @@ -292,6 +309,9 @@ GrGLPath::GrGLPath(GrGLGpu* gpu, const SkPath& origSkPath, const GrStrokeInfo& o fShouldFill = stroke->isFillStyle() || stroke->getStyle() == SkStrokeRec::kStrokeAndFill_Style; + fFillType = convert_skpath_filltype(skPath->getFillType()); + fBounds = skPath->getBounds(); + if (fShouldStroke) { InitPathObjectStroke(gpu, fPathID, *stroke); diff --git a/gfx/skia/skia/src/gpu/gl/GrGLPathRendering.cpp b/gfx/skia/skia/src/gpu/gl/GrGLPathRendering.cpp index 4c9ef86786..3cfec749ac 100644 --- a/gfx/skia/skia/src/gpu/gl/GrGLPathRendering.cpp +++ b/gfx/skia/skia/src/gpu/gl/GrGLPathRendering.cpp @@ -6,7 +6,6 @@ */ #include "gl/GrGLPathRendering.h" -#include "gl/GrGLNameAllocator.h" #include "gl/GrGLUtil.h" #include "gl/GrGLGpu.h" @@ -20,6 +19,9 @@ #define GL_CALL(X) GR_GL_CALL(this->gpu()->glInterface(), X) #define GL_CALL_RET(RET, X) GR_GL_CALL_RET(this->gpu()->glInterface(), RET, X) +// Number of paths to allocate per glGenPaths call. The call can be overly slow on command buffer GL +// implementation. The call has a result value, and thus waiting for the call completion is needed. +static const GrGLsizei kPathIDPreallocationAmount = 65536; static const GrGLenum gIndexType2GLType[] = { GR_GL_UNSIGNED_BYTE, @@ -60,17 +62,21 @@ static GrGLenum gr_stencil_op_to_gl_path_rendering_fill_mode(GrStencilOp op) { } GrGLPathRendering::GrGLPathRendering(GrGLGpu* gpu) - : GrPathRendering(gpu) { + : GrPathRendering(gpu) + , fPreallocatedPathCount(0) { const GrGLInterface* glInterface = gpu->glInterface(); fCaps.bindFragmentInputSupport = nullptr != glInterface->fFunctions.fBindFragmentInputLocation; } GrGLPathRendering::~GrGLPathRendering() { + if (fPreallocatedPathCount > 0) { + this->deletePaths(fFirstPreallocatedPathID, fPreallocatedPathCount); + } } void GrGLPathRendering::abandonGpuResources() { - fPathNameAllocator.reset(nullptr); + fPreallocatedPathCount = 0; } void GrGLPathRendering::resetContext() { @@ -230,54 +236,57 @@ void GrGLPathRendering::setProjectionMatrix(const SkMatrix& matrix, } GrGLuint GrGLPathRendering::genPaths(GrGLsizei range) { - if (range > 1) { - GrGLuint name; - GL_CALL_RET(name, GenPaths(range)); - return name; + SkASSERT(range > 0); + GrGLuint firstID; + if (fPreallocatedPathCount >= range) { + firstID = fFirstPreallocatedPathID; + fPreallocatedPathCount -= range; + fFirstPreallocatedPathID += range; + return firstID; + } + // Allocate range + the amount to fill up preallocation amount. If succeed, either join with + // the existing preallocation range or delete the existing and use the new (potentially partial) + // preallocation range. + GrGLsizei allocAmount = range + (kPathIDPreallocationAmount - fPreallocatedPathCount); + if (allocAmount >= range) { + GL_CALL_RET(firstID, GenPaths(allocAmount)); + + if (firstID != 0) { + if (fPreallocatedPathCount > 0 && + firstID == fFirstPreallocatedPathID + fPreallocatedPathCount) { + firstID = fFirstPreallocatedPathID; + fPreallocatedPathCount += allocAmount - range; + fFirstPreallocatedPathID += range; + return firstID; + } + + if (allocAmount > range) { + if (fPreallocatedPathCount > 0) { + this->deletePaths(fFirstPreallocatedPathID, fPreallocatedPathCount); + } + fFirstPreallocatedPathID = firstID + range; + fPreallocatedPathCount = allocAmount - range; + } + // Special case: if allocAmount == range, we have full preallocated range. + return firstID; + } + } + // Failed to allocate with preallocation. Remove existing preallocation and try to allocate just + // the range. + if (fPreallocatedPathCount > 0) { + this->deletePaths(fFirstPreallocatedPathID, fPreallocatedPathCount); + fPreallocatedPathCount = 0; } - if (nullptr == fPathNameAllocator.get()) { - static const int range = 65536; - GrGLuint firstName; - GL_CALL_RET(firstName, GenPaths(range)); - fPathNameAllocator.reset(new GrGLNameAllocator(firstName, firstName + range)); + GL_CALL_RET(firstID, GenPaths(range)); + if (firstID == 0) { + SkDebugf("Warning: Failed to allocate path\n"); } - - // When allocating names one at a time, pull from a client-side pool of - // available names in order to save a round trip to the GL server. - GrGLuint name = fPathNameAllocator->allocateName(); - - if (0 == name) { - // Our reserved path names are all in use. Fall back on GenPaths. - GL_CALL_RET(name, GenPaths(1)); - } - - return name; + return firstID; } void GrGLPathRendering::deletePaths(GrGLuint path, GrGLsizei range) { - if (range > 1) { - // It is not supported to delete names in ranges that were allocated - // individually using GrGLPathNameAllocator. - SkASSERT(nullptr == fPathNameAllocator.get() || - path + range <= fPathNameAllocator->firstName() || - path >= fPathNameAllocator->endName()); - GL_CALL(DeletePaths(path, range)); - return; - } - - if (nullptr == fPathNameAllocator.get() || - path < fPathNameAllocator->firstName() || - path >= fPathNameAllocator->endName()) { - // If we aren't inside fPathNameAllocator's range then this name was - // generated by the GenPaths fallback (or else was never allocated). - GL_CALL(DeletePaths(path, 1)); - return; - } - - // Make the path empty to save memory, but don't free the name in the driver. - GL_CALL(PathCommands(path, 0, nullptr, 0, GR_GL_FLOAT, nullptr)); - fPathNameAllocator->free(path); + GL_CALL(DeletePaths(path, range)); } void GrGLPathRendering::flushPathStencilSettings(const GrStencilSettings& stencilSettings) { diff --git a/gfx/skia/skia/src/gpu/gl/GrGLPathRendering.h b/gfx/skia/skia/src/gpu/gl/GrGLPathRendering.h index 57209fddc1..c3e5317808 100644 --- a/gfx/skia/skia/src/gpu/gl/GrGLPathRendering.h +++ b/gfx/skia/skia/src/gpu/gl/GrGLPathRendering.h @@ -111,7 +111,8 @@ private: }; GrGLGpu* gpu(); - SkAutoTDelete fPathNameAllocator; + GrGLuint fFirstPreallocatedPathID; + GrGLsizei fPreallocatedPathCount; MatrixState fHWProjectionMatrixState; GrStencilSettings fHWPathStencilSettings; Caps fCaps; diff --git a/gfx/skia/skia/src/gpu/gl/GrGLProgram.cpp b/gfx/skia/skia/src/gpu/gl/GrGLProgram.cpp index 3cf9e4dd7c..007d9670bc 100644 --- a/gfx/skia/skia/src/gpu/gl/GrGLProgram.cpp +++ b/gfx/skia/skia/src/gpu/gl/GrGLProgram.cpp @@ -31,15 +31,15 @@ GrGLProgram::GrGLProgram(GrGLGpu* gpu, GrGLuint programID, const UniformInfoArray& uniforms, const VaryingInfoArray& pathProcVaryings, - GrGLInstalledGeoProc* geometryProcessor, - GrGLInstalledXferProc* xferProcessor, - GrGLInstalledFragProcs* fragmentProcessors, + GrGLSLPrimitiveProcessor* geometryProcessor, + GrGLSLXferProcessor* xferProcessor, + const GrGLSLFragProcs& fragmentProcessors, SkTArray* passSamplerUniforms) : fBuiltinUniformHandles(builtinUniforms) , fProgramID(programID) , fGeometryProcessor(geometryProcessor) , fXferProcessor(xferProcessor) - , fFragmentProcessors(SkRef(fragmentProcessors)) + , fFragmentProcessors(fragmentProcessors) , fDesc(desc) , fGpu(gpu) , fProgramDataManager(gpu, programID, uniforms, pathProcVaryings) { @@ -55,6 +55,9 @@ GrGLProgram::~GrGLProgram() { if (fProgramID) { GL_CALL(DeleteProgram(fProgramID)); } + for (int i = 0; i < fFragmentProcessors.count(); ++i) { + delete fFragmentProcessors[i]; + } } void GrGLProgram::abandon() { @@ -63,12 +66,9 @@ void GrGLProgram::abandon() { /////////////////////////////////////////////////////////////////////////////// -template -static void append_texture_bindings(const Proc* ip, - const GrProcessor& processor, +static void append_texture_bindings(const GrProcessor& processor, SkTArray* textureBindings) { if (int numTextures = processor.numTextures()) { - SkASSERT(textureBindings->count() == ip->fSamplersIdx); const GrTextureAccess** bindings = textureBindings->push_back_n(numTextures); int i = 0; do { @@ -84,37 +84,32 @@ void GrGLProgram::setData(const GrPrimitiveProcessor& primProc, // we set the textures, and uniforms for installed processors in a generic way, but subclasses // of GLProgram determine how to set coord transforms - fGeometryProcessor->fGLProc->setData(fProgramDataManager, primProc); - append_texture_bindings(fGeometryProcessor.get(), primProc, textureBindings); + fGeometryProcessor->setData(fProgramDataManager, primProc); + append_texture_bindings(primProc, textureBindings); this->setFragmentData(primProc, pipeline, textureBindings); - const GrXferProcessor& xp = *pipeline.getXferProcessor(); - fXferProcessor->fGLProc->setData(fProgramDataManager, xp); - append_texture_bindings(fXferProcessor.get(), xp, textureBindings); + const GrXferProcessor& xp = pipeline.getXferProcessor(); + fXferProcessor->setData(fProgramDataManager, xp); + append_texture_bindings(xp, textureBindings); } void GrGLProgram::setFragmentData(const GrPrimitiveProcessor& primProc, const GrPipeline& pipeline, SkTArray* textureBindings) { - int numProcessors = fFragmentProcessors->fProcs.count(); + int numProcessors = fFragmentProcessors.count(); for (int i = 0; i < numProcessors; ++i) { const GrFragmentProcessor& processor = pipeline.getFragmentProcessor(i); - fFragmentProcessors->fProcs[i]->fGLProc->setData(fProgramDataManager, processor); - this->setTransformData(primProc, - processor, - i, - fFragmentProcessors->fProcs[i]); - append_texture_bindings(fFragmentProcessors->fProcs[i], processor, textureBindings); + fFragmentProcessors[i]->setData(fProgramDataManager, processor); + this->setTransformData(primProc, processor, i); + append_texture_bindings(processor, textureBindings); } } void GrGLProgram::setTransformData(const GrPrimitiveProcessor& primProc, const GrFragmentProcessor& processor, - int index, - GrGLInstalledFragProc* ip) { - GrGLSLPrimitiveProcessor* gp = fGeometryProcessor.get()->fGLProc.get(); - gp->setTransformData(primProc, fProgramDataManager, index, - processor.coordTransforms()); + int index) { + fGeometryProcessor->setTransformData(primProc, fProgramDataManager, index, + processor.coordTransforms()); } void GrGLProgram::setRenderTargetState(const GrPrimitiveProcessor& primProc, diff --git a/gfx/skia/skia/src/gpu/gl/GrGLProgram.h b/gfx/skia/skia/src/gpu/gl/GrGLProgram.h index 72fa9b01a0..22678cb6a3 100644 --- a/gfx/skia/skia/src/gpu/gl/GrGLProgram.h +++ b/gfx/skia/skia/src/gpu/gl/GrGLProgram.h @@ -14,6 +14,7 @@ #include "GrGLTexture.h" #include "GrGLProgramDataManager.h" #include "glsl/GrGLSLProgramDataManager.h" +#include "glsl/GrGLSLUniformHandler.h" #include "SkString.h" #include "SkXfermode.h" @@ -35,7 +36,7 @@ class GrPipeline; */ class GrGLProgram : public SkRefCnt { public: - typedef GrGLProgramBuilder::BuiltinUniformHandles BuiltinUniformHandles; + typedef GrGLSLProgramBuilder::BuiltinUniformHandles BuiltinUniformHandles; ~GrGLProgram(); @@ -107,9 +108,9 @@ protected: GrGLuint programID, const UniformInfoArray&, const VaryingInfoArray&, // used for NVPR only currently - GrGLInstalledGeoProc* geometryProcessor, - GrGLInstalledXferProc* xferProcessor, - GrGLInstalledFragProcs* fragmentProcessors, + GrGLSLPrimitiveProcessor* geometryProcessor, + GrGLSLXferProcessor* xferProcessor, + const GrGLSLFragProcs& fragmentProcessors, SkTArray* passSamplerUniforms); // A templated helper to loop over effects, set the transforms(via subclass) and bind textures @@ -117,8 +118,7 @@ protected: SkTArray* textureBindings); void setTransformData(const GrPrimitiveProcessor&, const GrFragmentProcessor&, - int index, - GrGLInstalledFragProc*); + int index); // Helper for setData() that sets the view matrix and loads the render target height uniform void setRenderTargetState(const GrPrimitiveProcessor&, const GrPipeline&); @@ -129,9 +129,9 @@ protected: GrGLuint fProgramID; // the installed effects - SkAutoTDelete fGeometryProcessor; - SkAutoTDelete fXferProcessor; - SkAutoTUnref fFragmentProcessors; + SkAutoTDelete fGeometryProcessor; + SkAutoTDelete fXferProcessor; + GrGLSLFragProcs fFragmentProcessors; GrProgramDesc fDesc; GrGLGpu* fGpu; diff --git a/gfx/skia/skia/src/gpu/gl/GrGLProgramDataManager.cpp b/gfx/skia/skia/src/gpu/gl/GrGLProgramDataManager.cpp index f0aba49720..06f568ee26 100644 --- a/gfx/skia/skia/src/gpu/gl/GrGLProgramDataManager.cpp +++ b/gfx/skia/skia/src/gpu/gl/GrGLProgramDataManager.cpp @@ -8,6 +8,7 @@ #include "SkMatrix.h" #include "gl/GrGLProgramDataManager.h" #include "gl/GrGLGpu.h" +#include "glsl/GrGLSLUniformHandler.h" #define ASSERT_ARRAY_UPLOAD_IN_BOUNDS(UNI, COUNT) \ SkASSERT(arrayCount <= uni.fArrayCount || \ @@ -31,12 +32,12 @@ GrGLProgramDataManager::GrGLProgramDataManager(GrGLGpu* gpu, GrGLuint programID, ); // TODO: Move the Xoom uniform array in both FS and VS bug workaround here. - if (GrGLProgramBuilder::kVertex_Visibility & builderUniform.fVisibility) { + if (GrGLSLUniformHandler::kVertex_Visibility & builderUniform.fVisibility) { uniform.fVSLocation = builderUniform.fLocation; } else { uniform.fVSLocation = kUnusedUniform; } - if (GrGLProgramBuilder::kFragment_Visibility & builderUniform.fVisibility) { + if (GrGLSLUniformHandler::kFragment_Visibility & builderUniform.fVisibility) { uniform.fFSLocation = builderUniform.fLocation; } else { uniform.fFSLocation = kUnusedUniform; @@ -62,7 +63,8 @@ GrGLProgramDataManager::GrGLProgramDataManager(GrGLGpu* gpu, GrGLuint programID, void GrGLProgramDataManager::setSampler(UniformHandle u, int texUnit) const { const Uniform& uni = fUniforms[u.toIndex()]; - SkASSERT(uni.fType == kSampler2D_GrSLType || uni.fType == kSamplerExternal_GrSLType); + SkASSERT(uni.fType == kSampler2D_GrSLType || uni.fType == kSamplerExternal_GrSLType || + uni.fType == kSampler2DRect_GrSLType); SkASSERT(GrGLSLShaderVar::kNonArray == uni.fArrayCount); // FIXME: We still insert a single sampler uniform for every stage. If the shader does not // reference the sampler then the compiler may have optimized it out. Uncomment this assert diff --git a/gfx/skia/skia/src/gpu/gl/GrGLProgramDataManager.h b/gfx/skia/skia/src/gpu/gl/GrGLProgramDataManager.h index d47745334d..1fce6ed62d 100644 --- a/gfx/skia/skia/src/gpu/gl/GrGLProgramDataManager.h +++ b/gfx/skia/skia/src/gpu/gl/GrGLProgramDataManager.h @@ -19,7 +19,6 @@ class GrGLGpu; class SkMatrix; class GrGLProgram; -class GrGLProgramBuilder; /** Manages the resources used by a shader program. * The resources are objects the program uses to communicate with the diff --git a/gfx/skia/skia/src/gpu/gl/GrGLProgramDesc.cpp b/gfx/skia/skia/src/gpu/gl/GrGLProgramDesc.cpp index e3d292c062..5edacf5721 100644 --- a/gfx/skia/skia/src/gpu/gl/GrGLProgramDesc.cpp +++ b/gfx/skia/skia/src/gpu/gl/GrGLProgramDesc.cpp @@ -7,42 +7,39 @@ #include "GrGLProgramDesc.h" #include "GrProcessor.h" -#include "GrGLGpu.h" #include "GrPipeline.h" #include "SkChecksum.h" +#include "gl/GrGLDefines.h" +#include "gl/GrGLTexture.h" +#include "gl/GrGLTypes.h" #include "glsl/GrGLSLFragmentProcessor.h" #include "glsl/GrGLSLFragmentShaderBuilder.h" +#include "glsl/GrGLSLCaps.h" -/** - * Do we need to either map r,g,b->a or a->r. configComponentMask indicates which channels are - * present in the texture's config. swizzleComponentMask indicates the channels present in the - * shader swizzle. - */ -static bool swizzle_requires_alpha_remapping(const GrGLSLCaps& caps, GrPixelConfig config) { - if (!caps.mustSwizzleInShader()) { - // Any remapping is handled using texture swizzling not shader modifications. - return false; - } - const char* swizzleMap = caps.getSwizzleMap(config); - - return SkToBool(memcmp(swizzleMap, "rgba", 4)); +static uint16_t texture_target_key(GrGLenum target) { + SkASSERT((uint32_t)target < SK_MaxU16); + return target; } -static uint32_t gen_texture_key(const GrProcessor& proc, const GrGLCaps& caps) { - uint32_t key = 0; +static void add_texture_key(GrProcessorKeyBuilder* b, const GrProcessor& proc, + const GrGLSLCaps& caps) { int numTextures = proc.numTextures(); - int shift = 0; - for (int t = 0; t < numTextures; ++t) { - const GrTextureAccess& access = proc.textureAccess(t); - if (swizzle_requires_alpha_remapping(*caps.glslCaps(), access.getTexture()->config())) { - key |= 1 << shift; - } - if (GR_GL_TEXTURE_EXTERNAL == static_cast(access.getTexture())->target()) { - key |= 2 << shift; - } - shift += 2; + // Need two bytes per key (swizzle and target). + int word32Count = (proc.numTextures() + 1) / 2; + if (0 == word32Count) { + return; + } + uint16_t* k16 = SkTCast(b->add32n(word32Count)); + for (int i = 0; i < numTextures; ++i) { + const GrTextureAccess& access = proc.textureAccess(i); + GrGLTexture* texture = static_cast(access.getTexture()); + k16[i] = caps.configTextureSwizzle(texture->config()).asKey() | + (texture_target_key(texture->target()) << 16); + } + // zero the last 16 bits if the number of textures is odd. + if (numTextures & 0x1) { + k16[numTextures] = 0; } - return key; } /** @@ -51,15 +48,14 @@ static uint32_t gen_texture_key(const GrProcessor& proc, const GrGLCaps& caps) { * in its key (e.g. the pixel format of textures used). So we create a meta-key for * every effect using this function. It is also responsible for inserting the effect's class ID * which must be different for every GrProcessor subclass. It can fail if an effect uses too many - * textures, transforms, etc, for the space allotted in the meta-key. NOTE, both FPs and GPs share - * this function because it is hairy, though FPs do not have attribs, and GPs do not have transforms + * transforms, etc, for the space allotted in the meta-key. NOTE, both FPs and GPs share this + * function because it is hairy, though FPs do not have attribs, and GPs do not have transforms */ static bool gen_meta_key(const GrProcessor& proc, - const GrGLCaps& caps, + const GrGLSLCaps& glslCaps, uint32_t transformKey, GrProcessorKeyBuilder* b) { size_t processorKeySize = b->size(); - uint32_t textureKey = gen_texture_key(proc, caps); uint32_t classID = proc.classID(); // Currently we allow 16 bits for the class id and the overall processor key size. @@ -68,34 +64,34 @@ static bool gen_meta_key(const GrProcessor& proc, return false; } - uint32_t* key = b->add32n(3); + add_texture_key(b, proc, glslCaps); + + uint32_t* key = b->add32n(2); key[0] = (classID << 16) | SkToU32(processorKeySize); - key[1] = textureKey; - key[2] = transformKey; + key[1] = transformKey; return true; } static bool gen_frag_proc_and_meta_keys(const GrPrimitiveProcessor& primProc, const GrFragmentProcessor& fp, - const GrGLCaps& caps, + const GrGLSLCaps& glslCaps, GrProcessorKeyBuilder* b) { for (int i = 0; i < fp.numChildProcessors(); ++i) { - if (!gen_frag_proc_and_meta_keys(primProc, fp.childProcessor(i), caps, b)) { + if (!gen_frag_proc_and_meta_keys(primProc, fp.childProcessor(i), glslCaps, b)) { return false; } } - fp.getGLSLProcessorKey(*caps.glslCaps(), b); + fp.getGLSLProcessorKey(glslCaps, b); - //**** use glslCaps here? - return gen_meta_key(fp, caps, primProc.getTransformKey(fp.coordTransforms(), - fp.numTransformsExclChildren()), b); + return gen_meta_key(fp, glslCaps, primProc.getTransformKey(fp.coordTransforms(), + fp.numTransformsExclChildren()), b); } bool GrGLProgramDescBuilder::Build(GrProgramDesc* desc, const GrPrimitiveProcessor& primProc, const GrPipeline& pipeline, - const GrGLGpu* gpu) { + const GrGLSLCaps& glslCaps) { // The descriptor is used as a cache key. Thus when a field of the // descriptor will not affect program generation (because of the attribute // bindings in use or other descriptor field settings) it should be set @@ -110,25 +106,23 @@ bool GrGLProgramDescBuilder::Build(GrProgramDesc* desc, GrProcessorKeyBuilder b(&glDesc->key()); - primProc.getGLSLProcessorKey(*gpu->glCaps().glslCaps(), &b); - //**** use glslCaps here? - if (!gen_meta_key(primProc, gpu->glCaps(), 0, &b)) { + primProc.getGLSLProcessorKey(glslCaps, &b); + if (!gen_meta_key(primProc, glslCaps, 0, &b)) { glDesc->key().reset(); return false; } for (int i = 0; i < pipeline.numFragmentProcessors(); ++i) { const GrFragmentProcessor& fp = pipeline.getFragmentProcessor(i); - if (!gen_frag_proc_and_meta_keys(primProc, fp, gpu->glCaps(), &b)) { + if (!gen_frag_proc_and_meta_keys(primProc, fp, glslCaps, &b)) { glDesc->key().reset(); return false; } } - const GrXferProcessor& xp = *pipeline.getXferProcessor(); - xp.getGLSLProcessorKey(*gpu->glCaps().glslCaps(), &b); - //**** use glslCaps here? - if (!gen_meta_key(xp, gpu->glCaps(), 0, &b)) { + const GrXferProcessor& xp = pipeline.getXferProcessor(); + xp.getGLSLProcessorKey(glslCaps, &b); + if (!gen_meta_key(xp, glslCaps, 0, &b)) { glDesc->key().reset(); return false; } @@ -148,6 +142,9 @@ bool GrGLProgramDescBuilder::Build(GrProgramDesc* desc, header->fFragPosKey = 0; } + header->fOutputSwizzle = + glslCaps.configOutputSwizzle(pipeline.getRenderTarget()->config()).asKey(); + if (pipeline.ignoresCoverage()) { header->fIgnoresCoverage = 1; } else { diff --git a/gfx/skia/skia/src/gpu/gl/GrGLProgramDesc.h b/gfx/skia/skia/src/gpu/gl/GrGLProgramDesc.h index 7ce79ecae9..0ebf6a228e 100644 --- a/gfx/skia/skia/src/gpu/gl/GrGLProgramDesc.h +++ b/gfx/skia/skia/src/gpu/gl/GrGLProgramDesc.h @@ -52,14 +52,13 @@ public: * general draw information, as well as the specific color, geometry, * and coverage stages which will be used to generate the GL Program for * this optstate. - * @param GrGLGpu A GL Gpu, the caps and Gpu object are used to output processor specific - * parts of the descriptor. + * @param GrGLSLCaps Capabilities of the GLSL backend. * @param GrProgramDesc The built and finalized descriptor **/ static bool Build(GrProgramDesc*, const GrPrimitiveProcessor&, const GrPipeline&, - const GrGLGpu*); + const GrGLSLCaps&); }; #endif diff --git a/gfx/skia/skia/src/gpu/gl/GrGLTransferBuffer.cpp b/gfx/skia/skia/src/gpu/gl/GrGLTransferBuffer.cpp new file mode 100644 index 0000000000..b7ee766d92 --- /dev/null +++ b/gfx/skia/skia/src/gpu/gl/GrGLTransferBuffer.cpp @@ -0,0 +1,51 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "GrGLTransferBuffer.h" +#include "GrGLGpu.h" +#include "SkTraceMemoryDump.h" + +GrGLTransferBuffer::GrGLTransferBuffer(GrGLGpu* gpu, const Desc& desc, GrGLenum type) + : INHERITED(gpu, desc.fSizeInBytes) + , fImpl(gpu, desc, type) { + this->registerWithCache(); +} + +void GrGLTransferBuffer::onRelease() { + if (!this->wasDestroyed()) { + fImpl.release(this->getGpuGL()); + } + + INHERITED::onRelease(); +} + +void GrGLTransferBuffer::onAbandon() { + fImpl.abandon(); + INHERITED::onAbandon(); +} + +void* GrGLTransferBuffer::onMap() { + if (!this->wasDestroyed()) { + return fImpl.map(this->getGpuGL()); + } else { + return nullptr; + } +} + +void GrGLTransferBuffer::onUnmap() { + if (!this->wasDestroyed()) { + fImpl.unmap(this->getGpuGL()); + } +} + +void GrGLTransferBuffer::setMemoryBacking(SkTraceMemoryDump* traceMemoryDump, + const SkString& dumpName) const { + SkString buffer_id; + buffer_id.appendU32(this->bufferID()); + traceMemoryDump->setMemoryBacking(dumpName.c_str(), "gl_buffer", + buffer_id.c_str()); +} diff --git a/gfx/skia/skia/src/gpu/gl/GrGLTransferBuffer.h b/gfx/skia/skia/src/gpu/gl/GrGLTransferBuffer.h new file mode 100644 index 0000000000..e01d4447df --- /dev/null +++ b/gfx/skia/skia/src/gpu/gl/GrGLTransferBuffer.h @@ -0,0 +1,48 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef GrGLTransferBuffer_DEFINED +#define GrGLTransferBuffer_DEFINED + +#include "GrTransferBuffer.h" +#include "GrGLBufferImpl.h" +#include "gl/GrGLInterface.h" + +class GrGLGpu; + +class GrGLTransferBuffer : public GrTransferBuffer { + +public: + typedef GrGLBufferImpl::Desc Desc; + + GrGLTransferBuffer(GrGLGpu* gpu, const Desc& desc, GrGLenum type); + + GrGLuint bufferID() const { return fImpl.bufferID(); } + size_t baseOffset() const { return fImpl.baseOffset(); } + GrGLenum bufferType() const { return fImpl.bufferType(); } + +protected: + void onAbandon() override; + void onRelease() override; + void setMemoryBacking(SkTraceMemoryDump* traceMemoryDump, + const SkString& dumpName) const override; + +private: + void* onMap() override; + void onUnmap() override; + + GrGLGpu* getGpuGL() const { + SkASSERT(!this->wasDestroyed()); + return (GrGLGpu*)(this->getGpu()); + } + + GrGLBufferImpl fImpl; + + typedef GrTransferBuffer INHERITED; +}; + +#endif diff --git a/gfx/skia/skia/src/gpu/gl/GrGLUniformHandler.cpp b/gfx/skia/skia/src/gpu/gl/GrGLUniformHandler.cpp new file mode 100644 index 0000000000..1ddb789668 --- /dev/null +++ b/gfx/skia/skia/src/gpu/gl/GrGLUniformHandler.cpp @@ -0,0 +1,90 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "gl/GrGLUniformHandler.h" + +#include "gl/GrGLCaps.h" +#include "gl/GrGLGpu.h" +#include "gl/builders/GrGLProgramBuilder.h" + +#define GL_CALL(X) GR_GL_CALL(this->glGpu()->glInterface(), X) +#define GL_CALL_RET(R, X) GR_GL_CALL_RET(this->glGpu()->glInterface(), R, X) + +GrGLSLUniformHandler::UniformHandle GrGLUniformHandler::internalAddUniformArray( + uint32_t visibility, + GrSLType type, + GrSLPrecision precision, + const char* name, + bool mangleName, + int arrayCount, + const char** outName) { + SkASSERT(name && strlen(name)); + SkDEBUGCODE(static const uint32_t kVisibilityMask = kVertex_Visibility | kFragment_Visibility); + SkASSERT(0 == (~kVisibilityMask & visibility)); + SkASSERT(0 != visibility); + SkASSERT(kDefault_GrSLPrecision == precision || GrSLTypeIsFloatType(type)); + + UniformInfo& uni = fUniforms.push_back(); + uni.fVariable.setType(type); + uni.fVariable.setTypeModifier(GrGLSLShaderVar::kUniform_TypeModifier); + // TODO this is a bit hacky, lets think of a better way. Basically we need to be able to use + // the uniform view matrix name in the GP, and the GP is immutable so it has to tell the PB + // exactly what name it wants to use for the uniform view matrix. If we prefix anythings, then + // the names will mismatch. I think the correct solution is to have all GPs which need the + // uniform view matrix, they should upload the view matrix in their setData along with regular + // uniforms. + char prefix = 'u'; + if ('u' == name[0]) { + prefix = '\0'; + } + fProgramBuilder->nameVariable(uni.fVariable.accessName(), prefix, name, mangleName); + uni.fVariable.setArrayCount(arrayCount); + uni.fVisibility = visibility; + uni.fVariable.setPrecision(precision); + + if (outName) { + *outName = uni.fVariable.c_str(); + } + return GrGLSLUniformHandler::UniformHandle(fUniforms.count() - 1); +} + +void GrGLUniformHandler::appendUniformDecls(ShaderVisibility visibility, SkString* out) const { + for (int i = 0; i < fUniforms.count(); ++i) { + if (fUniforms[i].fVisibility & visibility) { + fUniforms[i].fVariable.appendDecl(fProgramBuilder->glslCaps(), out); + out->append(";\n"); + } + } +} + +void GrGLUniformHandler::bindUniformLocations(GrGLuint programID, const GrGLCaps& caps) { + if (caps.bindUniformLocationSupport()) { + int count = fUniforms.count(); + for (int i = 0; i < count; ++i) { + GL_CALL(BindUniformLocation(programID, i, fUniforms[i].fVariable.c_str())); + fUniforms[i].fLocation = i; + } + } +} + +void GrGLUniformHandler::getUniformLocations(GrGLuint programID, const GrGLCaps& caps) { + if (!caps.bindUniformLocationSupport()) { + int count = fUniforms.count(); + for (int i = 0; i < count; ++i) { + GrGLint location; + GL_CALL_RET(location, GetUniformLocation(programID, fUniforms[i].fVariable.c_str())); + fUniforms[i].fLocation = location; + } + } +} + +const GrGLGpu* GrGLUniformHandler::glGpu() const { + GrGLProgramBuilder* glPB = (GrGLProgramBuilder*) fProgramBuilder; + return glPB->gpu(); +} + + diff --git a/gfx/skia/skia/src/gpu/gl/GrGLUniformHandler.h b/gfx/skia/skia/src/gpu/gl/GrGLUniformHandler.h new file mode 100644 index 0000000000..a782bcb1c3 --- /dev/null +++ b/gfx/skia/skia/src/gpu/gl/GrGLUniformHandler.h @@ -0,0 +1,61 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef GrGLUniformHandler_DEFINED +#define GrGLUniformHandler_DEFINED + +#include "glsl/GrGLSLUniformHandler.h" + +#include "gl/GrGLProgramDataManager.h" + +class GrGLCaps; + +static const int kUniformsPerBlock = 8; + +class GrGLUniformHandler : public GrGLSLUniformHandler { +public: + const GrGLSLShaderVar& getUniformVariable(UniformHandle u) const override { + return fUniforms[u.toIndex()].fVariable; + } + + const char* getUniformCStr(UniformHandle u) const override { + return this->getUniformVariable(u).c_str(); + } +private: + explicit GrGLUniformHandler(GrGLSLProgramBuilder* program) + : INHERITED(program) + , fUniforms(kUniformsPerBlock) {} + + UniformHandle internalAddUniformArray(uint32_t visibility, + GrSLType type, + GrSLPrecision precision, + const char* name, + bool mangleName, + int arrayCount, + const char** outName) override; + + void appendUniformDecls(ShaderVisibility, SkString*) const override; + + // Manually set uniform locations for all our uniforms. + void bindUniformLocations(GrGLuint programID, const GrGLCaps& caps); + + // Updates the loction of the Uniforms if we cannot bind uniform locations manually + void getUniformLocations(GrGLuint programID, const GrGLCaps& caps); + + const GrGLGpu* glGpu() const; + + typedef GrGLProgramDataManager::UniformInfo UniformInfo; + typedef GrGLProgramDataManager::UniformInfoArray UniformInfoArray; + + UniformInfoArray fUniforms; + + friend class GrGLProgramBuilder; + + typedef GrGLSLUniformHandler INHERITED; +}; + +#endif diff --git a/gfx/skia/skia/src/gpu/gl/GrGLUtil.cpp b/gfx/skia/skia/src/gpu/gl/GrGLUtil.cpp index 71d54c35c0..b5e5855401 100644 --- a/gfx/skia/skia/src/gpu/gl/GrGLUtil.cpp +++ b/gfx/skia/skia/src/gpu/gl/GrGLUtil.cpp @@ -261,9 +261,27 @@ GrGLRenderer GrGLGetRendererFromString(const char* rendererString) { if (1 == n && lastDigit >= 0 && lastDigit <= 9) { return kPowerVR54x_GrGLRenderer; } + // certain iOS devices also use PowerVR54x GPUs + static const char kAppleA4Str[] = "Apple A4"; + static const char kAppleA5Str[] = "Apple A5"; + static const char kAppleA6Str[] = "Apple A6"; + if (0 == strncmp(rendererString, kAppleA4Str, + SK_ARRAY_COUNT(kAppleA4Str)-1) || + 0 == strncmp(rendererString, kAppleA5Str, + SK_ARRAY_COUNT(kAppleA5Str)-1) || + 0 == strncmp(rendererString, kAppleA6Str, + SK_ARRAY_COUNT(kAppleA6Str)-1)) { + return kPowerVR54x_GrGLRenderer; + } static const char kPowerVRRogueStr[] = "PowerVR Rogue"; + static const char kAppleA7Str[] = "Apple A7"; + static const char kAppleA8Str[] = "Apple A8"; if (0 == strncmp(rendererString, kPowerVRRogueStr, - SK_ARRAY_COUNT(kPowerVRRogueStr)-1)) { + SK_ARRAY_COUNT(kPowerVRRogueStr)-1) || + 0 == strncmp(rendererString, kAppleA7Str, + SK_ARRAY_COUNT(kAppleA7Str)-1) || + 0 == strncmp(rendererString, kAppleA8Str, + SK_ARRAY_COUNT(kAppleA8Str)-1)) { return kPowerVRRogue_GrGLRenderer; } int adrenoNumber; diff --git a/gfx/skia/skia/src/gpu/gl/GrGLUtil.h b/gfx/skia/skia/src/gpu/gl/GrGLUtil.h index 70af3a36f4..8a3021cd57 100644 --- a/gfx/skia/skia/src/gpu/gl/GrGLUtil.h +++ b/gfx/skia/skia/src/gpu/gl/GrGLUtil.h @@ -81,6 +81,12 @@ enum GrGLDriver { GR_GL_CALL(gl, GetFramebufferAttachmentParameteriv(t, a, pname, p)); \ } while (0) +#define GR_GL_GetNamedFramebufferAttachmentParameteriv(gl, fb, a, pname, p) \ + do { \ + *(p) = GR_GL_INIT_ZERO; \ + GR_GL_CALL(gl, GetNamedFramebufferAttachmentParameteriv(fb, a, pname, p)); \ + } while (0) + #define GR_GL_GetRenderbufferParameteriv(gl, t, pname, p) \ do { \ *(p) = GR_GL_INIT_ZERO; \ diff --git a/gfx/skia/skia/src/gpu/gl/GrGLVaryingHandler.h b/gfx/skia/skia/src/gpu/gl/GrGLVaryingHandler.h index ab931debd7..50a87adc03 100644 --- a/gfx/skia/skia/src/gpu/gl/GrGLVaryingHandler.h +++ b/gfx/skia/skia/src/gpu/gl/GrGLVaryingHandler.h @@ -21,7 +21,7 @@ public: // This function is used by the NVPR PathProcessor to add a varying directly into the fragment // shader since there is no vertex shader. VaryingHandle addPathProcessingVarying(const char* name, GrGLSLVertToFrag*, - GrSLPrecision fsPrecision = kDefault_GrSLPrecision); + GrSLPrecision fsPrecision = kDefault_GrSLPrecision); private: typedef GrGLProgramDataManager::VaryingInfo VaryingInfo; diff --git a/gfx/skia/skia/src/gpu/gl/GrGLVertexBuffer.cpp b/gfx/skia/skia/src/gpu/gl/GrGLVertexBuffer.cpp index 1f8951654f..2294844fc4 100644 --- a/gfx/skia/skia/src/gpu/gl/GrGLVertexBuffer.cpp +++ b/gfx/skia/skia/src/gpu/gl/GrGLVertexBuffer.cpp @@ -10,7 +10,8 @@ #include "SkTraceMemoryDump.h" GrGLVertexBuffer::GrGLVertexBuffer(GrGLGpu* gpu, const Desc& desc) - : INHERITED(gpu, desc.fSizeInBytes, desc.fDynamic, 0 == desc.fID) + : INHERITED(gpu, desc.fSizeInBytes, GrGLBufferImpl::kDynamicDraw_Usage == desc.fUsage, + 0 == desc.fID) , fImpl(gpu, desc, GR_GL_ARRAY_BUFFER) { this->registerWithCache(); } diff --git a/gfx/skia/skia/src/gpu/gl/SkGLContext.cpp b/gfx/skia/skia/src/gpu/gl/SkGLContext.cpp index ec318cb80d..07c61f7aa0 100644 --- a/gfx/skia/skia/src/gpu/gl/SkGLContext.cpp +++ b/gfx/skia/skia/src/gpu/gl/SkGLContext.cpp @@ -152,3 +152,26 @@ void SkGLContext::GLFenceSync::deleteFence(SkPlatformGpuFence fence) const { GLsync glsync = static_cast(fence); fGLDeleteSync(glsync); } + +GrGLint SkGLContext::createTextureRectangle(int width, int height, GrGLenum internalFormat, + GrGLenum externalFormat, GrGLenum externalType, + GrGLvoid* data) { + if (!(kGL_GrGLStandard == fGL->fStandard && GrGLGetVersion(fGL) >= GR_GL_VER(3, 2)) && + !fGL->fExtensions.has("GL_ARB_texture_rectangle")) { + return 0; + } + GrGLuint id; + GR_GL_CALL(fGL, GenTextures(1, &id)); + GR_GL_CALL(fGL, BindTexture(GR_GL_TEXTURE_RECTANGLE, id)); + GR_GL_CALL(fGL, TexParameteri(GR_GL_TEXTURE_RECTANGLE, GR_GL_TEXTURE_MAG_FILTER, + GR_GL_NEAREST)); + GR_GL_CALL(fGL, TexParameteri(GR_GL_TEXTURE_RECTANGLE, GR_GL_TEXTURE_MIN_FILTER, + GR_GL_NEAREST)); + GR_GL_CALL(fGL, TexParameteri(GR_GL_TEXTURE_RECTANGLE, GR_GL_TEXTURE_WRAP_S, + GR_GL_CLAMP_TO_EDGE)); + GR_GL_CALL(fGL, TexParameteri(GR_GL_TEXTURE_RECTANGLE, GR_GL_TEXTURE_WRAP_T, + GR_GL_CLAMP_TO_EDGE)); + GR_GL_CALL(fGL, TexImage2D(GR_GL_TEXTURE_RECTANGLE, 0, internalFormat, width, height, 0, + externalFormat, externalType, data)); + return id; +} diff --git a/gfx/skia/skia/src/gpu/gl/SkNullGLContext.cpp b/gfx/skia/skia/src/gpu/gl/SkNullGLContext.cpp index e2a80ce2b3..dafa1ef0bc 100644 --- a/gfx/skia/skia/src/gpu/gl/SkNullGLContext.cpp +++ b/gfx/skia/skia/src/gpu/gl/SkNullGLContext.cpp @@ -122,6 +122,8 @@ public: BufferManager fBufferManager; GrGLuint fCurrArrayBuffer; GrGLuint fCurrElementArrayBuffer; + GrGLuint fCurrPixelPackBuffer; + GrGLuint fCurrPixelUnpackBuffer; GrGLuint fCurrProgramID; GrGLuint fCurrShaderID; @@ -129,6 +131,8 @@ public: ContextState() : fCurrArrayBuffer(0) , fCurrElementArrayBuffer(0) + , fCurrPixelPackBuffer(0) + , fCurrPixelUnpackBuffer(0) , fCurrProgramID(0) , fCurrShaderID(0) {} @@ -172,6 +176,12 @@ GrGLvoid GR_GL_FUNCTION_TYPE nullGLBufferData(GrGLenum target, case GR_GL_ELEMENT_ARRAY_BUFFER: id = state->fCurrElementArrayBuffer; break; + case GR_GL_PIXEL_PACK_BUFFER: + id = state->fCurrPixelPackBuffer; + break; + case GR_GL_PIXEL_UNPACK_BUFFER: + id = state->fCurrPixelUnpackBuffer; + break; default: SkFAIL("Unexpected target to nullGLBufferData"); break; @@ -215,6 +225,12 @@ GrGLvoid GR_GL_FUNCTION_TYPE nullGLBindBuffer(GrGLenum target, GrGLuint buffer) case GR_GL_ELEMENT_ARRAY_BUFFER: state->fCurrElementArrayBuffer = buffer; break; + case GR_GL_PIXEL_PACK_BUFFER: + state->fCurrPixelPackBuffer = buffer; + break; + case GR_GL_PIXEL_UNPACK_BUFFER: + state->fCurrPixelUnpackBuffer = buffer; + break; } } @@ -228,6 +244,12 @@ GrGLvoid GR_GL_FUNCTION_TYPE nullGLDeleteBuffers(GrGLsizei n, const GrGLuint* id if (ids[i] == state->fCurrElementArrayBuffer) { state->fCurrElementArrayBuffer = 0; } + if (ids[i] == state->fCurrPixelPackBuffer) { + state->fCurrPixelPackBuffer = 0; + } + if (ids[i] == state->fCurrPixelUnpackBuffer) { + state->fCurrPixelUnpackBuffer = 0; + } BufferObj* buffer = state->fBufferManager.lookUp(ids[i]); state->fBufferManager.free(buffer); @@ -245,6 +267,12 @@ GrGLvoid* GR_GL_FUNCTION_TYPE nullGLMapBufferRange(GrGLenum target, GrGLintptr o case GR_GL_ELEMENT_ARRAY_BUFFER: id = state->fCurrElementArrayBuffer; break; + case GR_GL_PIXEL_PACK_BUFFER: + id = state->fCurrPixelPackBuffer; + break; + case GR_GL_PIXEL_UNPACK_BUFFER: + id = state->fCurrPixelUnpackBuffer; + break; } if (id > 0) { @@ -267,6 +295,12 @@ GrGLvoid* GR_GL_FUNCTION_TYPE nullGLMapBuffer(GrGLenum target, GrGLenum access) case GR_GL_ELEMENT_ARRAY_BUFFER: id = state->fCurrElementArrayBuffer; break; + case GR_GL_PIXEL_PACK_BUFFER: + id = state->fCurrPixelPackBuffer; + break; + case GR_GL_PIXEL_UNPACK_BUFFER: + id = state->fCurrPixelUnpackBuffer; + break; } if (id > 0) { @@ -295,6 +329,12 @@ GrGLboolean GR_GL_FUNCTION_TYPE nullGLUnmapBuffer(GrGLenum target) { case GR_GL_ELEMENT_ARRAY_BUFFER: id = state->fCurrElementArrayBuffer; break; + case GR_GL_PIXEL_PACK_BUFFER: + id = state->fCurrPixelPackBuffer; + break; + case GR_GL_PIXEL_UNPACK_BUFFER: + id = state->fCurrPixelUnpackBuffer; + break; } if (id > 0) { BufferObj* buffer = state->fBufferManager.lookUp(id); @@ -320,6 +360,12 @@ GrGLvoid GR_GL_FUNCTION_TYPE nullGLGetBufferParameteriv(GrGLenum target, GrGLenu case GR_GL_ELEMENT_ARRAY_BUFFER: id = state->fCurrElementArrayBuffer; break; + case GR_GL_PIXEL_PACK_BUFFER: + id = state->fCurrPixelPackBuffer; + break; + case GR_GL_PIXEL_UNPACK_BUFFER: + id = state->fCurrPixelUnpackBuffer; + break; } if (id > 0) { BufferObj* buffer = state->fBufferManager.lookUp(id); @@ -532,10 +578,7 @@ static void set_current_context_from_interface(const GrGLInterface* interface) { } #endif -SkNullGLContext* SkNullGLContext::Create(GrGLStandard forcedGpuAPI) { - if (kGLES_GrGLStandard == forcedGpuAPI) { - return nullptr; - } +SkNullGLContext* SkNullGLContext::Create() { SkNullGLContext* ctx = new SkNullGLContext; if (!ctx->isValid()) { delete ctx; diff --git a/gfx/skia/skia/src/gpu/gl/angle/GrGLCreateANGLEInterface.cpp b/gfx/skia/skia/src/gpu/gl/angle/GrGLCreateANGLEInterface.cpp index 05afb2cb1c..d7ba2cd82a 100644 --- a/gfx/skia/skia/src/gpu/gl/angle/GrGLCreateANGLEInterface.cpp +++ b/gfx/skia/skia/src/gpu/gl/angle/GrGLCreateANGLEInterface.cpp @@ -45,7 +45,7 @@ const GrGLInterface* GrGLCreateANGLEInterface() { gLibs.fEGLLib = DynamicLoadLibrary("libEGL.dylib"); #else gLibs.fGLLib = DynamicLoadLibrary("libGLESv2.so"); - gLibs.fGLLib = DynamicLoadLibrary("libEGL.so"); + gLibs.fEGLLib = DynamicLoadLibrary("libEGL.so"); #endif } diff --git a/gfx/skia/skia/src/gpu/gl/angle/SkANGLEGLContext.cpp b/gfx/skia/skia/src/gpu/gl/angle/SkANGLEGLContext.cpp index 54ef02d492..2c9f38e1d9 100644 --- a/gfx/skia/skia/src/gpu/gl/angle/SkANGLEGLContext.cpp +++ b/gfx/skia/skia/src/gpu/gl/angle/SkANGLEGLContext.cpp @@ -180,7 +180,12 @@ GrGLuint SkANGLEGLContext::eglImageToExternalTexture(GrEGLImage image) const { } SkGLContext* SkANGLEGLContext::createNew() const { - SkGLContext* ctx = SkANGLEGLContext::Create(this->gl()->fStandard, fIsGLBackend); +#ifdef SK_BUILD_FOR_WIN + SkGLContext* ctx = fIsGLBackend ? SkANGLEGLContext::CreateOpenGL() + : SkANGLEGLContext::CreateDirectX(); +#else + SkGLContext* ctx = SkANGLEGLContext::CreateOpenGL(); +#endif if (ctx) { ctx->makeCurrent(); } diff --git a/gfx/skia/skia/src/gpu/gl/builders/GrGLProgramBuilder.cpp b/gfx/skia/skia/src/gpu/gl/builders/GrGLProgramBuilder.cpp index 86264c02de..4a1b2e9ac5 100644 --- a/gfx/skia/skia/src/gpu/gl/builders/GrGLProgramBuilder.cpp +++ b/gfx/skia/skia/src/gpu/gl/builders/GrGLProgramBuilder.cpp @@ -10,6 +10,7 @@ #include "GrAutoLocaleSetter.h" #include "GrCoordTransform.h" #include "GrGLProgramBuilder.h" +#include "GrSwizzle.h" #include "GrTexture.h" #include "SkRTConf.h" #include "SkTraceEvent.h" @@ -41,7 +42,10 @@ GrGLProgram* GrGLProgramBuilder::CreateProgram(const DrawArgs& args, GrGLGpu* gp GrGLSLExpr4 inputColor; GrGLSLExpr4 inputCoverage; - if (!pb->emitAndInstallProcs(&inputColor, &inputCoverage)) { + if (!pb->emitAndInstallProcs(&inputColor, + &inputCoverage, + gpu->glCaps().maxFragmentTextureUnits())) { + pb->cleanupFragmentProcessors(); return nullptr; } @@ -52,299 +56,44 @@ GrGLProgram* GrGLProgramBuilder::CreateProgram(const DrawArgs& args, GrGLGpu* gp GrGLProgramBuilder::GrGLProgramBuilder(GrGLGpu* gpu, const DrawArgs& args) : INHERITED(args) - , fGeometryProcessor(nullptr) - , fXferProcessor(nullptr) , fGpu(gpu) - , fUniforms(kVarsPerBlock) , fSamplerUniforms(4) - , fVaryingHandler(this) { + , fVaryingHandler(this) + , fUniformHandler(this) { } -GrGLSLProgramDataManager::UniformHandle GrGLProgramBuilder::internalAddUniformArray( - uint32_t visibility, - GrSLType type, - GrSLPrecision precision, - const char* name, - bool mangleName, - int count, - const char** outName) { - SkASSERT(name && strlen(name)); - SkDEBUGCODE(static const uint32_t kVisibilityMask = kVertex_Visibility | kFragment_Visibility); - SkASSERT(0 == (~kVisibilityMask & visibility)); - SkASSERT(0 != visibility); - SkASSERT(kDefault_GrSLPrecision == precision || GrSLTypeIsFloatType(type)); - - UniformInfo& uni = fUniforms.push_back(); - uni.fVariable.setType(type); - uni.fVariable.setTypeModifier(GrGLSLShaderVar::kUniform_TypeModifier); - // TODO this is a bit hacky, lets think of a better way. Basically we need to be able to use - // the uniform view matrix name in the GP, and the GP is immutable so it has to tell the PB - // exactly what name it wants to use for the uniform view matrix. If we prefix anythings, then - // the names will mismatch. I think the correct solution is to have all GPs which need the - // uniform view matrix, they should upload the view matrix in their setData along with regular - // uniforms. - char prefix = 'u'; - if ('u' == name[0]) { - prefix = '\0'; - } - this->nameVariable(uni.fVariable.accessName(), prefix, name, mangleName); - uni.fVariable.setArrayCount(count); - uni.fVisibility = visibility; - uni.fVariable.setPrecision(precision); - - if (outName) { - *outName = uni.fVariable.c_str(); - } - return GrGLSLProgramDataManager::UniformHandle(fUniforms.count() - 1); -} - -void GrGLProgramBuilder::onAppendUniformDecls(ShaderVisibility visibility, SkString* out) const { - for (int i = 0; i < fUniforms.count(); ++i) { - if (fUniforms[i].fVisibility & visibility) { - fUniforms[i].fVariable.appendDecl(this->glslCaps(), out); - out->append(";\n"); - } - } +const GrCaps* GrGLProgramBuilder::caps() const { + return fGpu->caps(); } const GrGLSLCaps* GrGLProgramBuilder::glslCaps() const { - return this->fGpu->ctxInfo().caps()->glslCaps(); -} - -bool GrGLProgramBuilder::emitAndInstallProcs(GrGLSLExpr4* inputColor, GrGLSLExpr4* inputCoverage) { - // First we loop over all of the installed processors and collect coord transforms. These will - // be sent to the GrGLSLPrimitiveProcessor in its emitCode function - const GrPrimitiveProcessor& primProc = this->primitiveProcessor(); - int totalTextures = primProc.numTextures(); - const int maxTextureUnits = fGpu->glCaps().maxFragmentTextureUnits(); - - for (int i = 0; i < this->pipeline().numFragmentProcessors(); i++) { - const GrFragmentProcessor& processor = this->pipeline().getFragmentProcessor(i); - - if (!primProc.hasTransformedLocalCoords()) { - SkTArray& procCoords = fCoordTransforms.push_back(); - processor.gatherCoordTransforms(&procCoords); - } - - totalTextures += processor.numTextures(); - if (totalTextures >= maxTextureUnits) { - GrCapsDebugf(fGpu->caps(), "Program would use too many texture units\n"); - return false; - } - } - - this->emitAndInstallProc(primProc, inputColor, inputCoverage); - - fFragmentProcessors.reset(new GrGLInstalledFragProcs); - int numProcs = this->pipeline().numFragmentProcessors(); - this->emitAndInstallFragProcs(0, this->pipeline().numColorFragmentProcessors(), inputColor); - this->emitAndInstallFragProcs(this->pipeline().numColorFragmentProcessors(), numProcs, - inputCoverage); - this->emitAndInstallXferProc(*this->pipeline().getXferProcessor(), *inputColor, *inputCoverage, - this->pipeline().ignoresCoverage()); - return true; -} - -void GrGLProgramBuilder::emitAndInstallFragProcs(int procOffset, - int numProcs, - GrGLSLExpr4* inOut) { - for (int i = procOffset; i < numProcs; ++i) { - GrGLSLExpr4 output; - const GrFragmentProcessor& fp = this->pipeline().getFragmentProcessor(i); - this->emitAndInstallProc(fp, i, *inOut, &output); - *inOut = output; - } -} - -void GrGLProgramBuilder::nameExpression(GrGLSLExpr4* output, const char* baseName) { - // create var to hold stage result. If we already have a valid output name, just use that - // otherwise create a new mangled one. This name is only valid if we are reordering stages - // and have to tell stage exactly where to put its output. - SkString outName; - if (output->isValid()) { - outName = output->c_str(); - } else { - this->nameVariable(&outName, '\0', baseName); - } - fFS.codeAppendf("vec4 %s;", outName.c_str()); - *output = outName; -} - -// TODO Processors cannot output zeros because an empty string is all 1s -// the fix is to allow effects to take the GrGLSLExpr4 directly -void GrGLProgramBuilder::emitAndInstallProc(const GrFragmentProcessor& fp, - int index, - const GrGLSLExpr4& input, - GrGLSLExpr4* output) { - // Program builders have a bit of state we need to clear with each effect - AutoStageAdvance adv(this); - this->nameExpression(output, "output"); - - // Enclose custom code in a block to avoid namespace conflicts - SkString openBrace; - openBrace.printf("{ // Stage %d, %s\n", fStageIndex, fp.name()); - fFS.codeAppend(openBrace.c_str()); - - this->emitAndInstallProc(fp, index, output->c_str(), input.isOnes() ? nullptr : input.c_str()); - - fFS.codeAppend("}"); -} - -void GrGLProgramBuilder::emitAndInstallProc(const GrPrimitiveProcessor& proc, - GrGLSLExpr4* outputColor, - GrGLSLExpr4* outputCoverage) { - // Program builders have a bit of state we need to clear with each effect - AutoStageAdvance adv(this); - this->nameExpression(outputColor, "outputColor"); - this->nameExpression(outputCoverage, "outputCoverage"); - - // Enclose custom code in a block to avoid namespace conflicts - SkString openBrace; - openBrace.printf("{ // Stage %d, %s\n", fStageIndex, proc.name()); - fFS.codeAppend(openBrace.c_str()); - fVS.codeAppendf("// Primitive Processor %s\n", proc.name()); - - this->emitAndInstallProc(proc, outputColor->c_str(), outputCoverage->c_str()); - - fFS.codeAppend("}"); -} - -void GrGLProgramBuilder::emitAndInstallProc(const GrFragmentProcessor& fp, - int index, - const char* outColor, - const char* inColor) { - GrGLInstalledFragProc* ifp = new GrGLInstalledFragProc; - - ifp->fGLProc.reset(fp.createGLSLInstance()); - - SkSTArray<4, GrGLSLTextureSampler> samplers(fp.numTextures()); - this->emitSamplers(fp, &samplers, ifp); - - GrGLSLFragmentProcessor::EmitArgs args(this, - &fFS, - this->glslCaps(), - fp, - outColor, - inColor, - fOutCoords[index], - samplers); - ifp->fGLProc->emitCode(args); - - // We have to check that effects and the code they emit are consistent, ie if an effect - // asks for dst color, then the emit code needs to follow suit - verify(fp); - fFragmentProcessors->fProcs.push_back(ifp); -} - -void GrGLProgramBuilder::emitAndInstallProc(const GrPrimitiveProcessor& gp, - const char* outColor, - const char* outCoverage) { - SkASSERT(!fGeometryProcessor); - fGeometryProcessor = new GrGLInstalledGeoProc; - - fGeometryProcessor->fGLProc.reset(gp.createGLSLInstance(*fGpu->glCaps().glslCaps())); - - SkSTArray<4, GrGLSLTextureSampler> samplers(gp.numTextures()); - this->emitSamplers(gp, &samplers, fGeometryProcessor); - - GrGLSLGeometryProcessor::EmitArgs args(this, - &fVS, - &fFS, - &fVaryingHandler, - this->glslCaps(), - gp, - outColor, - outCoverage, - samplers, - fCoordTransforms, - &fOutCoords); - fGeometryProcessor->fGLProc->emitCode(args); - - // We have to check that effects and the code they emit are consistent, ie if an effect - // asks for dst color, then the emit code needs to follow suit - verify(gp); -} - -void GrGLProgramBuilder::emitAndInstallXferProc(const GrXferProcessor& xp, - const GrGLSLExpr4& colorIn, - const GrGLSLExpr4& coverageIn, - bool ignoresCoverage) { - // Program builders have a bit of state we need to clear with each effect - AutoStageAdvance adv(this); - - SkASSERT(!fXferProcessor); - fXferProcessor = new GrGLInstalledXferProc; - - fXferProcessor->fGLProc.reset(xp.createGLSLInstance()); - - // Enable dual source secondary output if we have one - if (xp.hasSecondaryOutput()) { - fFS.enableSecondaryOutput(); - } - - if (this->glslCaps()->mustDeclareFragmentShaderOutput()) { - fFS.enableCustomOutput(); - } - - SkString openBrace; - openBrace.printf("{ // Xfer Processor: %s\n", xp.name()); - fFS.codeAppend(openBrace.c_str()); - - SkSTArray<4, GrGLSLTextureSampler> samplers(xp.numTextures()); - this->emitSamplers(xp, &samplers, fXferProcessor); - - GrGLSLXferProcessor::EmitArgs args(this, - &fFS, - this->glslCaps(), - xp, colorIn.c_str(), - ignoresCoverage ? nullptr : coverageIn.c_str(), - fFS.getPrimaryColorOutputName(), - fFS.getSecondaryColorOutputName(), - samplers); - fXferProcessor->fGLProc->emitCode(args); - - // We have to check that effects and the code they emit are consistent, ie if an effect - // asks for dst color, then the emit code needs to follow suit - verify(xp); - fFS.codeAppend("}"); -} - -void GrGLProgramBuilder::verify(const GrPrimitiveProcessor& gp) { - SkASSERT(fFS.hasReadFragmentPosition() == gp.willReadFragmentPosition()); -} - -void GrGLProgramBuilder::verify(const GrXferProcessor& xp) { - SkASSERT(fFS.hasReadDstColor() == xp.willReadDstColor()); -} - -void GrGLProgramBuilder::verify(const GrFragmentProcessor& fp) { - SkASSERT(fFS.hasReadFragmentPosition() == fp.willReadFragmentPosition()); + return fGpu->ctxInfo().caps()->glslCaps(); } static GrSLType get_sampler_type(const GrTextureAccess& access) { GrGLTexture* glTexture = static_cast(access.getTexture()); if (glTexture->target() == GR_GL_TEXTURE_EXTERNAL) { return kSamplerExternal_GrSLType; + } else if (glTexture->target() == GR_GL_TEXTURE_RECTANGLE) { + return kSampler2DRect_GrSLType; } else { SkASSERT(glTexture->target() == GR_GL_TEXTURE_2D); return kSampler2D_GrSLType; } } -template void GrGLProgramBuilder::emitSamplers(const GrProcessor& processor, - GrGLSLTextureSampler::TextureSamplerArray* outSamplers, - GrGLInstalledProc* ip) { - SkDEBUGCODE(ip->fSamplersIdx = fSamplerUniforms.count();) + GrGLSLTextureSampler::TextureSamplerArray* outSamplers) { int numTextures = processor.numTextures(); UniformHandle* localSamplerUniforms = fSamplerUniforms.push_back_n(numTextures); SkString name; for (int t = 0; t < numTextures; ++t) { name.printf("Sampler%d", t); GrSLType samplerType = get_sampler_type(processor.textureAccess(t)); - localSamplerUniforms[t] = this->addUniform(GrGLProgramBuilder::kFragment_Visibility, - samplerType, kDefault_GrSLPrecision, - name.c_str()); + localSamplerUniforms[t] = + fUniformHandler.addUniform(GrGLSLUniformHandler::kFragment_Visibility, + samplerType, kDefault_GrSLPrecision, + name.c_str()); SkNEW_APPEND_TO_TARRAY(outSamplers, GrGLSLTextureSampler, (localSamplerUniforms[t], processor.textureAccess(t))); if (kSamplerExternal_GrSLType == samplerType) { @@ -384,12 +133,13 @@ GrGLProgram* GrGLProgramBuilder::finalize() { GrGLuint programID; GL_CALL_RET(programID, CreateProgram()); if (0 == programID) { + this->cleanupFragmentProcessors(); return nullptr; } // compile shaders and bind attributes / uniforms SkTDArray shadersToDelete; - fVS.finalize(kVertex_Visibility); + fVS.finalize(GrGLSLUniformHandler::kVertex_Visibility); if (!this->compileAndAttachShaders(fVS, programID, GR_GL_VERTEX_SHADER, &shadersToDelete)) { this->cleanupProgram(programID, shadersToDelete); return nullptr; @@ -406,7 +156,7 @@ GrGLProgram* GrGLProgramBuilder::finalize() { } } - fFS.finalize(kFragment_Visibility); + fFS.finalize(GrGLSLUniformHandler::kFragment_Visibility); if (!this->compileAndAttachShaders(fFS, programID, GR_GL_FRAGMENT_SHADER, &shadersToDelete)) { this->cleanupProgram(programID, shadersToDelete); return nullptr; @@ -432,13 +182,7 @@ GrGLProgram* GrGLProgramBuilder::finalize() { } void GrGLProgramBuilder::bindProgramResourceLocations(GrGLuint programID) { - if (fGpu->glCaps().bindUniformLocationSupport()) { - int count = fUniforms.count(); - for (int i = 0; i < count; ++i) { - GL_CALL(BindUniformLocation(programID, i, fUniforms[i].fVariable.c_str())); - fUniforms[i].fLocation = i; - } - } + fUniformHandler.bindUniformLocations(programID, fGpu->glCaps()); const GrGLCaps& caps = this->gpu()->glCaps(); if (fFS.hasCustomColorOutput() && caps.bindFragDataLocationSupport()) { @@ -488,14 +232,7 @@ bool GrGLProgramBuilder::checkLinkStatus(GrGLuint programID) { } void GrGLProgramBuilder::resolveProgramResourceLocations(GrGLuint programID) { - if (!fGpu->glCaps().bindUniformLocationSupport()) { - int count = fUniforms.count(); - for (int i = 0; i < count; ++i) { - GrGLint location; - GL_CALL_RET(location, GetUniformLocation(programID, fUniforms[i].fVariable.c_str())); - fUniforms[i].fLocation = location; - } - } + fUniformHandler.getUniformLocations(programID, fGpu->glCaps()); // handle NVPR separable varyings if (!fGpu->glCaps().shaderCaps()->pathRenderingSupport() || @@ -515,7 +252,8 @@ void GrGLProgramBuilder::resolveProgramResourceLocations(GrGLuint programID) { void GrGLProgramBuilder::cleanupProgram(GrGLuint programID, const SkTDArray& shaderIDs) { GL_CALL(DeleteProgram(programID)); - cleanupShaders(shaderIDs); + this->cleanupShaders(shaderIDs); + this->cleanupFragmentProcessors(); } void GrGLProgramBuilder::cleanupShaders(const SkTDArray& shaderIDs) { for (int i = 0; i < shaderIDs.count(); ++i) { @@ -524,17 +262,15 @@ void GrGLProgramBuilder::cleanupShaders(const SkTDArray& shaderIDs) { } GrGLProgram* GrGLProgramBuilder::createProgram(GrGLuint programID) { - return new GrGLProgram(fGpu, this->desc(), fUniformHandles, programID, fUniforms, + return new GrGLProgram(fGpu, + this->desc(), + fUniformHandles, + programID, + fUniformHandler.fUniforms, fVaryingHandler.fPathProcVaryingInfos, - fGeometryProcessor, fXferProcessor, fFragmentProcessors.get(), + fGeometryProcessor, + fXferProcessor, + fFragmentProcessors, &fSamplerUniforms); } -/////////////////////////////////////////////////////////////////////////////////////////////////// - -GrGLInstalledFragProcs::~GrGLInstalledFragProcs() { - int numProcs = fProcs.count(); - for (int i = 0; i < numProcs; ++i) { - delete fProcs[i]; - } -} diff --git a/gfx/skia/skia/src/gpu/gl/builders/GrGLProgramBuilder.h b/gfx/skia/skia/src/gpu/gl/builders/GrGLProgramBuilder.h index 329e5d7d08..20879253ca 100644 --- a/gfx/skia/skia/src/gpu/gl/builders/GrGLProgramBuilder.h +++ b/gfx/skia/skia/src/gpu/gl/builders/GrGLProgramBuilder.h @@ -10,43 +10,16 @@ #include "GrPipeline.h" #include "gl/GrGLProgramDataManager.h" +#include "gl/GrGLUniformHandler.h" #include "gl/GrGLVaryingHandler.h" -#include "glsl/GrGLSLPrimitiveProcessor.h" #include "glsl/GrGLSLProgramBuilder.h" #include "glsl/GrGLSLProgramDataManager.h" -#include "glsl/GrGLSLTextureSampler.h" -#include "glsl/GrGLSLXferProcessor.h" class GrFragmentProcessor; class GrGLContextInfo; class GrGLSLShaderBuilder; class GrGLSLCaps; -/** - * The below struct represent processors installed in programs. - */ -template -struct GrGLInstalledProc { - SkDEBUGCODE(int fSamplersIdx;) - SkAutoTDelete fGLProc; -}; - -typedef GrGLInstalledProc GrGLInstalledGeoProc; -typedef GrGLInstalledProc GrGLInstalledXferProc; -typedef GrGLInstalledProc GrGLInstalledFragProc; - -struct GrGLInstalledFragProcs : public SkRefCnt { - virtual ~GrGLInstalledFragProcs(); - SkSTArray<8, GrGLInstalledFragProc*, true> fProcs; -}; - -/* - * Please note - no diamond problems because of virtual inheritance. Also, both base classes - * are pure virtual with no data members. This is the base class for program building. - * Subclasses are nearly identical but each has their own way of emitting transforms. State for - * each of the elements of the shader pipeline, ie vertex, fragment, geometry, etc, lives in those - * respective builders -*/ class GrGLProgramBuilder : public GrGLSLProgramBuilder { public: /** Generates a shader program. @@ -58,66 +31,16 @@ public: */ static GrGLProgram* CreateProgram(const DrawArgs&, GrGLGpu*); - const GrGLSLShaderVar& getUniformVariable(UniformHandle u) const override { - return fUniforms[u.toIndex()].fVariable; - } - - const char* getUniformCStr(UniformHandle u) const override { - return this->getUniformVariable(u).c_str(); - } - + const GrCaps* caps() const override; const GrGLSLCaps* glslCaps() const override; GrGLGpu* gpu() const { return fGpu; } private: - typedef GrGLProgramDataManager::UniformInfo UniformInfo; - typedef GrGLProgramDataManager::UniformInfoArray UniformInfoArray; - GrGLProgramBuilder(GrGLGpu*, const DrawArgs&); - UniformHandle internalAddUniformArray(uint32_t visibility, - GrSLType type, - GrSLPrecision precision, - const char* name, - bool mangleName, - int arrayCount, - const char** outName) override; - - // Generates a possibly mangled name for a stage variable and writes it to the fragment shader. - // If GrGLSLExpr4 has a valid name then it will use that instead - void nameExpression(GrGLSLExpr4*, const char* baseName); - bool emitAndInstallProcs(GrGLSLExpr4* inputColor, GrGLSLExpr4* inputCoverage); - void emitAndInstallFragProcs(int procOffset, int numProcs, GrGLSLExpr4* inOut); - void emitAndInstallProc(const GrFragmentProcessor&, - int index, - const GrGLSLExpr4& input, - GrGLSLExpr4* output); - - void emitAndInstallProc(const GrPrimitiveProcessor&, - GrGLSLExpr4* outputColor, - GrGLSLExpr4* outputCoverage); - - // these emit functions help to keep the createAndEmitProcessors template general - void emitAndInstallProc(const GrFragmentProcessor&, - int index, - const char* outColor, - const char* inColor); - void emitAndInstallProc(const GrPrimitiveProcessor&, - const char* outColor, - const char* outCoverage); - void emitAndInstallXferProc(const GrXferProcessor&, - const GrGLSLExpr4& colorIn, - const GrGLSLExpr4& coverageIn, - bool ignoresCoverage); - - void verify(const GrPrimitiveProcessor&); - void verify(const GrXferProcessor&); - void verify(const GrFragmentProcessor&); - template void emitSamplers(const GrProcessor&, - GrGLSLTextureSampler::TextureSamplerArray* outSamplers, - GrGLInstalledProc*); + GrGLSLTextureSampler::TextureSamplerArray* outSamplers) override; bool compileAndAttachShaders(GrGLSLShaderBuilder& shader, GrGLuint programId, @@ -133,45 +56,17 @@ private: // Subclasses create different programs GrGLProgram* createProgram(GrGLuint programID); - void onAppendUniformDecls(ShaderVisibility visibility, SkString* out) const override; - + GrGLSLUniformHandler* uniformHandler() override { return &fUniformHandler; } + const GrGLSLUniformHandler* uniformHandler() const override { return &fUniformHandler; } GrGLSLVaryingHandler* varyingHandler() override { return &fVaryingHandler; } - // reset is called by program creator between each processor's emit code. It increments the - // stage offset for variable name mangling, and also ensures verfication variables in the - // fragment shader are cleared. - void reset() { - this->addStage(); - fFS.reset(); - } - void addStage() { fStageIndex++; } - - class AutoStageAdvance { - public: - AutoStageAdvance(GrGLProgramBuilder* pb) - : fPB(pb) { - fPB->reset(); - // Each output to the fragment processor gets its own code section - fPB->fFS.nextStage(); - } - ~AutoStageAdvance() {} - private: - GrGLProgramBuilder* fPB; - }; - - GrGLInstalledGeoProc* fGeometryProcessor; - GrGLInstalledXferProc* fXferProcessor; - SkAutoTUnref fFragmentProcessors; GrGLGpu* fGpu; - UniformInfoArray fUniforms; - GrGLSLPrimitiveProcessor::TransformsIn fCoordTransforms; - GrGLSLPrimitiveProcessor::TransformsOut fOutCoords; + typedef GrGLSLUniformHandler::UniformHandle UniformHandle; SkTArray fSamplerUniforms; GrGLVaryingHandler fVaryingHandler; - - friend class GrGLVaryingHandler; + GrGLUniformHandler fUniformHandler; typedef GrGLSLProgramBuilder INHERITED; }; diff --git a/gfx/skia/skia/src/gpu/gl/command_buffer/SkCommandBufferGLContext.cpp b/gfx/skia/skia/src/gpu/gl/command_buffer/SkCommandBufferGLContext.cpp index 585f1d079e..cf9da9365b 100644 --- a/gfx/skia/skia/src/gpu/gl/command_buffer/SkCommandBufferGLContext.cpp +++ b/gfx/skia/skia/src/gpu/gl/command_buffer/SkCommandBufferGLContext.cpp @@ -9,6 +9,7 @@ #include "gl/GrGLInterface.h" #include "gl/GrGLAssembleInterface.h" #include "gl/command_buffer/SkCommandBufferGLContext.h" +#include "../ports/SkOSEnvironment.h" #include "../ports/SkOSLibrary.h" #if defined SK_BUILD_FOR_MAC @@ -185,46 +186,75 @@ void SkCommandBufferGLContext::initializeGLContext(void* nativeWindow, const int const int* surfaceAttribs) { LoadCommandBufferOnce(); if (!gfFunctionsLoadedSuccessfully) { + SkDebugf("Command Buffer: Could not load EGL functions.\n"); return; } - fDisplay = gfGetDisplay(static_cast(EGL_DEFAULT_DISPLAY)); + // Make sure CHROMIUM_path_rendering is enabled for NVPR support. + sk_setenv("CHROME_COMMAND_BUFFER_GLES2_ARGS", "--enable-gl-path-rendering"); + fDisplay = gfGetDisplay(EGL_DEFAULT_DISPLAY); if (EGL_NO_DISPLAY == fDisplay) { - SkDebugf("Could not create EGL display!"); + SkDebugf("Command Buffer: Could not create EGL display.\n"); return; } EGLint majorVersion; EGLint minorVersion; - gfInitialize(fDisplay, &majorVersion, &minorVersion); + if (!gfInitialize(fDisplay, &majorVersion, &minorVersion)) { + SkDebugf("Command Buffer: Could not initialize EGL display.\n"); + this->destroyGLContext(); + return; + } - EGLConfig surfaceConfig = static_cast(fConfig); EGLint numConfigs; - gfChooseConfig(fDisplay, configAttribs, &surfaceConfig, 1, &numConfigs); + if (!gfChooseConfig(fDisplay, configAttribs, static_cast(&fConfig), 1, + &numConfigs) || numConfigs != 1) { + SkDebugf("Command Buffer: Could not choose EGL config.\n"); + this->destroyGLContext(); + return; + } if (nativeWindow) { - fSurface = gfCreateWindowSurface(fDisplay, surfaceConfig, - (EGLNativeWindowType)nativeWindow, surfaceAttribs); + fSurface = gfCreateWindowSurface(fDisplay, + static_cast(fConfig), + (EGLNativeWindowType)nativeWindow, + surfaceAttribs); } else { - fSurface = gfCreatePbufferSurface(fDisplay, surfaceConfig, surfaceAttribs); + fSurface = gfCreatePbufferSurface(fDisplay, + static_cast(fConfig), + surfaceAttribs); + } + if (EGL_NO_SURFACE == fSurface) { + SkDebugf("Command Buffer: Could not create EGL surface.\n"); + this->destroyGLContext(); + return; } static const EGLint contextAttribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE }; - fContext = gfCreateContext(fDisplay, surfaceConfig, nullptr, contextAttribs); + fContext = gfCreateContext(fDisplay, static_cast(fConfig), nullptr, contextAttribs); + if (EGL_NO_CONTEXT == fContext) { + SkDebugf("Command Buffer: Could not create EGL context.\n"); + this->destroyGLContext(); + return; + } - gfMakeCurrent(fDisplay, fSurface, fSurface, fContext); + if (!gfMakeCurrent(fDisplay, fSurface, fSurface, fContext)) { + SkDebugf("Command Buffer: Could not make EGL context current.\n"); + this->destroyGLContext(); + return; + } SkAutoTUnref gl(GrGLCreateCommandBufferInterface()); if (nullptr == gl.get()) { - SkDebugf("Could not create CommandBuffer GL interface!\n"); + SkDebugf("Command Buffer: Could not create CommandBuffer GL interface.\n"); this->destroyGLContext(); return; } if (!gl->validate()) { - SkDebugf("Could not validate CommandBuffer GL interface!\n"); + SkDebugf("Command Buffer: Could not validate CommandBuffer GL interface.\n"); this->destroyGLContext(); return; } @@ -264,7 +294,7 @@ void SkCommandBufferGLContext::onPlatformMakeCurrent() const { return; } if (!gfMakeCurrent(fDisplay, fSurface, fSurface, fContext)) { - SkDebugf("Could not set the context.\n"); + SkDebugf("Command Buffer: Could not make EGL context current.\n"); } } @@ -273,7 +303,7 @@ void SkCommandBufferGLContext::onPlatformSwapBuffers() const { return; } if (!gfSwapBuffers(fDisplay, fSurface)) { - SkDebugf("Could not complete gfSwapBuffers.\n"); + SkDebugf("Command Buffer: Could not complete gfSwapBuffers.\n"); } } @@ -298,14 +328,12 @@ bool SkCommandBufferGLContext::makeCurrent() { int SkCommandBufferGLContext::getStencilBits() { EGLint result = 0; - EGLConfig surfaceConfig = static_cast(fConfig); - gfGetConfigAttrib(fDisplay, surfaceConfig, EGL_STENCIL_SIZE, &result); + gfGetConfigAttrib(fDisplay, static_cast(fConfig), EGL_STENCIL_SIZE, &result); return result; } int SkCommandBufferGLContext::getSampleCount() { EGLint result = 0; - EGLConfig surfaceConfig = static_cast(fConfig); - gfGetConfigAttrib(fDisplay, surfaceConfig, EGL_SAMPLES, &result); + gfGetConfigAttrib(fDisplay, static_cast(fConfig), EGL_SAMPLES, &result); return result; } diff --git a/gfx/skia/skia/src/gpu/gl/debug/GrGLCreateDebugInterface.cpp b/gfx/skia/skia/src/gpu/gl/debug/GrGLCreateDebugInterface.cpp index f5440fe313..bcc3007650 100644 --- a/gfx/skia/skia/src/gpu/gl/debug/GrGLCreateDebugInterface.cpp +++ b/gfx/skia/skia/src/gpu/gl/debug/GrGLCreateDebugInterface.cpp @@ -513,9 +513,10 @@ GrGLvoid debugGenObjs(GrDebugGL::GrObjTypes type, GrGLuint* ids) { for (int i = 0; i < n; ++i) { - GrFakeRefObj *obj = GrDebugGL::getInstance()->createObj(type); - GrAlwaysAssert(obj); - ids[i] = obj->getID(); + GrAlwaysAssert(ids[i] == 0); + GrFakeRefObj *obj = GrDebugGL::getInstance()->createObj(type); + GrAlwaysAssert(obj); + ids[i] = obj->getID(); } } diff --git a/gfx/skia/skia/src/gpu/gl/debug/SkDebugGLContext.h b/gfx/skia/skia/src/gpu/gl/debug/SkDebugGLContext.h index abbcf559c5..113a254e0b 100644 --- a/gfx/skia/skia/src/gpu/gl/debug/SkDebugGLContext.h +++ b/gfx/skia/skia/src/gpu/gl/debug/SkDebugGLContext.h @@ -14,10 +14,7 @@ class SkDebugGLContext : public SkGLContext { public: ~SkDebugGLContext() override; - static SkDebugGLContext* Create(GrGLStandard forcedGpuAPI) { - if (kGLES_GrGLStandard == forcedGpuAPI) { - return nullptr; - } + static SkDebugGLContext* Create() { return new SkDebugGLContext; } private: diff --git a/gfx/skia/skia/src/gpu/gl/egl/SkCreatePlatformGLContext_egl.cpp b/gfx/skia/skia/src/gpu/gl/egl/SkCreatePlatformGLContext_egl.cpp index 591cae349c..3b2488dfc6 100644 --- a/gfx/skia/skia/src/gpu/gl/egl/SkCreatePlatformGLContext_egl.cpp +++ b/gfx/skia/skia/src/gpu/gl/egl/SkCreatePlatformGLContext_egl.cpp @@ -277,7 +277,7 @@ GrGLFuncPtr EGLGLContext::onPlatformGetProcAddress(const char* procName) const { } static bool supports_egl_extension(EGLDisplay display, const char* extension) { - int extensionLength = strlen(extension); + size_t extensionLength = strlen(extension); const char* extensionsStr = eglQueryString(display, EGL_EXTENSIONS); while (const char* match = strstr(extensionsStr, extension)) { // Ensure the string we found is its own extension, not a substring of a larger extension diff --git a/gfx/skia/skia/src/gpu/gl/mesa/SkMesaGLContext.h b/gfx/skia/skia/src/gpu/gl/mesa/SkMesaGLContext.h index a58f1c890e..a9c77a81da 100644 --- a/gfx/skia/skia/src/gpu/gl/mesa/SkMesaGLContext.h +++ b/gfx/skia/skia/src/gpu/gl/mesa/SkMesaGLContext.h @@ -19,10 +19,7 @@ private: public: ~SkMesaGLContext() override; - static SkMesaGLContext* Create(GrGLStandard forcedGpuAPI) { - if (kGLES_GrGLStandard == forcedGpuAPI) { - return nullptr; - } + static SkMesaGLContext* Create() { SkMesaGLContext* ctx = new SkMesaGLContext; if (!ctx->isValid()) { delete ctx; diff --git a/gfx/skia/skia/src/gpu/glsl/GrGLSL.h b/gfx/skia/skia/src/gpu/glsl/GrGLSL.h index ac38522ca1..f2accc54e9 100644 --- a/gfx/skia/skia/src/gpu/glsl/GrGLSL.h +++ b/gfx/skia/skia/src/gpu/glsl/GrGLSL.h @@ -48,12 +48,23 @@ bool GrGLSLSupportsNamedFragmentShaderOutputs(GrGLSLGeneration); * Gets the name of the function that should be used to sample a 2D texture. Coord type is used * to indicate whether the texture is sampled using projective textured (kVec3f) or not (kVec2f). */ -inline const char* GrGLSLTexture2DFunctionName(GrSLType coordType, GrGLSLGeneration glslGen) { +inline const char* GrGLSLTexture2DFunctionName(GrSLType coordType, GrSLType samplerType, + GrGLSLGeneration glslGen) { + SkASSERT(GrSLTypeIsSamplerType(samplerType)); + SkASSERT(kVec2f_GrSLType == coordType || kVec3f_GrSLType == coordType); + // GL_TEXTURE_RECTANGLE_ARB is written against OpenGL 2.0/GLSL 1.10. At that time there were + // separate texture*() functions. In OpenGL 3.0/GLSL 1.30 the different texture*() functions + // were deprecated in favor or the unified texture() function. RECTANGLE textures became + // standard in OpenGL 3.2/GLSL 1.50 and use texture(). It isn't completely clear what function + // should be used for RECTANGLE textures in GLSL versions >= 1.30 && < 1.50. We're going with + // using texture(). + if (glslGen >= k130_GrGLSLGeneration) { + return (kVec2f_GrSLType == coordType) ? "texture" : "textureProj"; + } if (kVec2f_GrSLType == coordType) { - return glslGen >= k130_GrGLSLGeneration ? "texture" : "texture2D"; + return (samplerType == kSampler2DRect_GrSLType) ? "texture2DRect" : "texture2D"; } else { - SkASSERT(kVec3f_GrSLType == coordType); - return glslGen >= k130_GrGLSLGeneration ? "textureProj" : "texture2DProj"; + return (samplerType == kSampler2DRect_GrSLType) ? "texture2DRectProj" : "texture2DProj"; } } @@ -87,6 +98,8 @@ static inline const char* GrGLSLTypeString(GrSLType t) { return "sampler2D"; case kSamplerExternal_GrSLType: return "samplerExternalOES"; + case kSampler2DRect_GrSLType: + return "sampler2DRect"; default: SkFAIL("Unknown shader var type."); return ""; // suppress warning diff --git a/gfx/skia/skia/src/gpu/glsl/GrGLSLCaps.cpp b/gfx/skia/skia/src/gpu/glsl/GrGLSLCaps.cpp index 191fb567e6..c82d8333df 100644 --- a/gfx/skia/skia/src/gpu/glsl/GrGLSLCaps.cpp +++ b/gfx/skia/skia/src/gpu/glsl/GrGLSLCaps.cpp @@ -21,7 +21,6 @@ GrGLSLCaps::GrGLSLCaps(const GrContextOptions& options) { fBindlessTextureSupport = false; fUsesPrecisionModifiers = false; fCanUseAnyFunctionInShader = true; - fForceHighPrecisionNDSTransform = false; fCanUseMinAndAbsTogether = true; fMustForceNegatedAtanParamToFloat = false; fVersionDeclString = nullptr; @@ -32,9 +31,6 @@ GrGLSLCaps::GrGLSLCaps(const GrContextOptions& options) { fFBFetchColorName = nullptr; fFBFetchExtensionString = nullptr; fAdvBlendEqInteraction = kNotSupported_AdvBlendEqInteraction; - - fMustSwizzleInShader = false; - memset(fConfigSwizzle, 0, sizeof(fConfigSwizzle)); } SkString GrGLSLCaps::dump() const { @@ -59,8 +55,6 @@ SkString GrGLSLCaps::dump() const { r.appendf("Bindless texture support: %s\n", (fBindlessTextureSupport ? "YES" : "NO")); r.appendf("Uses precision modifiers: %s\n", (fUsesPrecisionModifiers ? "YES" : "NO")); r.appendf("Can use any() function: %s\n", (fCanUseAnyFunctionInShader ? "YES" : "NO")); - r.appendf("Force high precision on NDS transform: %s\n", (fForceHighPrecisionNDSTransform ? - "YES" : "NO")); r.appendf("Can use min() and abs() together: %s\n", (fCanUseMinAndAbsTogether ? "YES" : "NO")); r.appendf("Must force negated atan param to float: %s\n", (fMustForceNegatedAtanParamToFloat ? "YES" : "NO")); @@ -70,8 +64,5 @@ SkString GrGLSLCaps::dump() const { } void GrGLSLCaps::onApplyOptionsOverrides(const GrContextOptions& options) { - if (options.fUseShaderSwizzling) { - fMustSwizzleInShader = true; - } } diff --git a/gfx/skia/skia/src/gpu/glsl/GrGLSLCaps.h b/gfx/skia/skia/src/gpu/glsl/GrGLSLCaps.h index 9924773139..060539645e 100644 --- a/gfx/skia/skia/src/gpu/glsl/GrGLSLCaps.h +++ b/gfx/skia/skia/src/gpu/glsl/GrGLSLCaps.h @@ -11,6 +11,7 @@ #include "GrCaps.h" #include "GrGLSL.h" +#include "GrSwizzle.h" class GrGLSLCaps : public GrShaderCaps { public: @@ -72,8 +73,6 @@ public: // Returns whether we can use the glsl funciton any() in our shader code. bool canUseAnyFunctionInShader() const { return fCanUseAnyFunctionInShader; } - bool forceHighPrecisionNDSTransform() const { return fForceHighPrecisionNDSTransform; } - bool canUseMinAndAbsTogether() const { return fCanUseMinAndAbsTogether; } bool mustForceNegatedAtanParamToFloat() const { return fMustForceNegatedAtanParamToFloat; } @@ -106,14 +105,19 @@ public: return fExternalTextureExtensionString; } - bool mustSwizzleInShader() const { return fMustSwizzleInShader; } - /** - * Returns a string which represents how to map from an internal GLFormat to a given - * GrPixelConfig. The function mustSwizzleInShader determines whether this swizzle is applied - * in the generated shader code or using sample state in the 3D API. + * Given a texture's config, this determines what swizzle must be appended to accesses to the + * texture in generated shader code. Swizzling may be implemented in texture parameters or a + * sampler rather than in the shader. In this case the returned swizzle will always be "rgba". */ - const char* getSwizzleMap(GrPixelConfig config) const { return fConfigSwizzle[config]; } + const GrSwizzle& configTextureSwizzle(GrPixelConfig config) const { + return fConfigTextureSwizzle[config]; + } + + /** Swizzle that should occur on the fragment shader outputs for a given config. */ + const GrSwizzle& configOutputSwizzle(GrPixelConfig config) const { + return fConfigOutputSwizzle[config]; + } GrGLSLGeneration generation() const { return fGLSLGeneration; } @@ -133,7 +137,6 @@ private: bool fBindlessTextureSupport : 1; bool fUsesPrecisionModifiers : 1; bool fCanUseAnyFunctionInShader : 1; - bool fForceHighPrecisionNDSTransform : 1; // Used for specific driver bug work arounds bool fCanUseMinAndAbsTogether : 1; @@ -151,13 +154,13 @@ private: AdvBlendEqInteraction fAdvBlendEqInteraction; - bool fMustSwizzleInShader; - const char* fConfigSwizzle[kGrPixelConfigCnt]; + GrSwizzle fConfigTextureSwizzle[kGrPixelConfigCnt]; + GrSwizzle fConfigOutputSwizzle[kGrPixelConfigCnt]; friend class GrGLCaps; // For initialization. + friend class GrVkCaps; typedef GrShaderCaps INHERITED; }; - #endif diff --git a/gfx/skia/skia/src/gpu/glsl/GrGLSLFragmentProcessor.cpp b/gfx/skia/skia/src/gpu/glsl/GrGLSLFragmentProcessor.cpp index a0019669f2..649bd99dea 100644 --- a/gfx/skia/skia/src/gpu/glsl/GrGLSLFragmentProcessor.cpp +++ b/gfx/skia/skia/src/gpu/glsl/GrGLSLFragmentProcessor.cpp @@ -9,7 +9,7 @@ #include "GrFragmentProcessor.h" #include "GrProcessor.h" #include "glsl/GrGLSLFragmentShaderBuilder.h" -#include "glsl/GrGLSLProgramBuilder.h" +#include "glsl/GrGLSLUniformHandler.h" void GrGLSLFragmentProcessor::setData(const GrGLSLProgramDataManager& pdman, const GrFragmentProcessor& processor) { @@ -93,8 +93,8 @@ void GrGLSLFragmentProcessor::internalEmitChild(int childIndex, const char* inpu fragBuilder->codeAppend("{\n"); fragBuilder->codeAppendf("// Child Index %d (mangle: %s): %s\n", childIndex, fragBuilder->getMangleString().c_str(), childProc.name()); - EmitArgs childArgs(args.fBuilder, - fragBuilder, + EmitArgs childArgs(fragBuilder, + args.fUniformHandler, args.fGLSLCaps, childProc, outputColor, diff --git a/gfx/skia/skia/src/gpu/glsl/GrGLSLFragmentProcessor.h b/gfx/skia/skia/src/gpu/glsl/GrGLSLFragmentProcessor.h index 0fd1ae3c32..ca72a9c415 100644 --- a/gfx/skia/skia/src/gpu/glsl/GrGLSLFragmentProcessor.h +++ b/gfx/skia/skia/src/gpu/glsl/GrGLSLFragmentProcessor.h @@ -14,9 +14,10 @@ class GrProcessor; class GrProcessorKeyBuilder; +class GrGLSLCaps; class GrGLSLFPBuilder; class GrGLSLFragmentBuilder; -class GrGLSLCaps; +class GrGLSLUniformHandler; class GrGLSLFragmentProcessor { public: @@ -51,24 +52,24 @@ public: */ struct EmitArgs { - EmitArgs(GrGLSLFPBuilder* builder, - GrGLSLFragmentBuilder* fragBuilder, + EmitArgs(GrGLSLFragmentBuilder* fragBuilder, + GrGLSLUniformHandler* uniformHandler, const GrGLSLCaps* caps, const GrFragmentProcessor& fp, const char* outputColor, const char* inputColor, const GrGLSLTransformedCoordsArray& coords, const TextureSamplerArray& samplers) - : fBuilder(builder) - , fFragBuilder(fragBuilder) + : fFragBuilder(fragBuilder) + , fUniformHandler(uniformHandler) , fGLSLCaps(caps) , fFp(fp) , fOutputColor(outputColor) , fInputColor(inputColor) , fCoords(coords) , fSamplers(samplers) {} - GrGLSLFPBuilder* fBuilder; GrGLSLFragmentBuilder* fFragBuilder; + GrGLSLUniformHandler* fUniformHandler; const GrGLSLCaps* fGLSLCaps; const GrFragmentProcessor& fFp; const char* fOutputColor; diff --git a/gfx/skia/skia/src/gpu/glsl/GrGLSLFragmentShaderBuilder.cpp b/gfx/skia/skia/src/gpu/glsl/GrGLSLFragmentShaderBuilder.cpp index 122db10ec9..54e0b7f956 100644 --- a/gfx/skia/skia/src/gpu/glsl/GrGLSLFragmentShaderBuilder.cpp +++ b/gfx/skia/skia/src/gpu/glsl/GrGLSLFragmentShaderBuilder.cpp @@ -10,6 +10,7 @@ #include "glsl/GrGLSL.h" #include "glsl/GrGLSLCaps.h" #include "glsl/GrGLSLProgramBuilder.h" +#include "glsl/GrGLSLUniformHandler.h" #include "glsl/GrGLSLVarying.h" const char* GrGLSLFragmentShaderBuilder::kDstTextureColorName = "_dstColor"; @@ -136,24 +137,19 @@ const char* GrGLSLFragmentShaderBuilder::fragmentPosition() { static const char* kTempName = "tmpXYFragCoord"; static const char* kCoordName = "fragCoordYDown"; if (!fSetupFragPosition) { - SkASSERT(!fProgramBuilder->fUniformHandles.fRTHeightUni.isValid()); const char* rtHeightName; - fProgramBuilder->fUniformHandles.fRTHeightUni = - fProgramBuilder->addFragPosUniform(GrGLSLProgramBuilder::kFragment_Visibility, - kFloat_GrSLType, - kDefault_GrSLPrecision, - "RTHeight", - &rtHeightName); + fProgramBuilder->addRTHeightUniform("RTHeight", &rtHeightName); // The Adreno compiler seems to be very touchy about access to "gl_FragCoord". // Accessing glFragCoord.zw can cause a program to fail to link. Additionally, // depending on the surrounding code, accessing .xy with a uniform involved can // do the same thing. Copying gl_FragCoord.xy into a temp vec2 beforehand // (and only accessing .xy) seems to "fix" things. - this->codePrependf("\tvec4 %s = vec4(%s.x, %s - %s.y, 1.0, 1.0);\n", - kCoordName, kTempName, rtHeightName, kTempName); - this->codePrependf("vec2 %s = gl_FragCoord.xy;", kTempName); + const char* precision = glslCaps->usesPrecisionModifiers() ? "highp " : ""; + this->codePrependf("\t%svec4 %s = vec4(%s.x, %s - %s.y, 1.0, 1.0);\n", + precision, kCoordName, kTempName, rtHeightName, kTempName); + this->codePrependf("%svec2 %s = gl_FragCoord.xy;", precision, kTempName); fSetupFragPosition = true; } SkASSERT(fProgramBuilder->fUniformHandles.fRTHeightUni.isValid()); diff --git a/gfx/skia/skia/src/gpu/glsl/GrGLSLFragmentShaderBuilder.h b/gfx/skia/skia/src/gpu/glsl/GrGLSLFragmentShaderBuilder.h index a55fdd2860..820cf17ae4 100644 --- a/gfx/skia/skia/src/gpu/glsl/GrGLSLFragmentShaderBuilder.h +++ b/gfx/skia/skia/src/gpu/glsl/GrGLSLFragmentShaderBuilder.h @@ -194,6 +194,7 @@ private: bool fHasReadDstColor; bool fHasReadFragmentPosition; + friend class GrGLSLProgramBuilder; friend class GrGLProgramBuilder; typedef GrGLSLXPFragmentBuilder INHERITED; diff --git a/gfx/skia/skia/src/gpu/glsl/GrGLSLGeometryProcessor.cpp b/gfx/skia/skia/src/gpu/glsl/GrGLSLGeometryProcessor.cpp index 8a1e81a1f8..a8bd8ac4f8 100644 --- a/gfx/skia/skia/src/gpu/glsl/GrGLSLGeometryProcessor.cpp +++ b/gfx/skia/skia/src/gpu/glsl/GrGLSLGeometryProcessor.cpp @@ -7,9 +7,10 @@ #include "GrGLSLGeometryProcessor.h" +#include "GrCoordTransform.h" #include "glsl/GrGLSLFragmentShaderBuilder.h" #include "glsl/GrGLSLProcessorTypes.h" -#include "glsl/GrGLSLProgramBuilder.h" +#include "glsl/GrGLSLUniformHandler.h" #include "glsl/GrGLSLVarying.h" #include "glsl/GrGLSLVertexShaderBuilder.h" @@ -20,9 +21,9 @@ void GrGLSLGeometryProcessor::emitCode(EmitArgs& args) { vBuilder->transformToNormalizedDeviceSpace(gpArgs.fPositionVar); } -void GrGLSLGeometryProcessor::emitTransforms(GrGLSLGPBuilder* pb, - GrGLSLVertexBuilder* vb, +void GrGLSLGeometryProcessor::emitTransforms(GrGLSLVertexBuilder* vb, GrGLSLVaryingHandler* varyingHandler, + GrGLSLUniformHandler* uniformHandler, const GrShaderVar& posVar, const char* localCoords, const SkMatrix& localMatrix, @@ -49,10 +50,10 @@ void GrGLSLGeometryProcessor::emitTransforms(GrGLSLGPBuilder* pb, const char* uniName; fInstalledTransforms[i][t].fHandle = - pb->addUniform(GrGLSLProgramBuilder::kVertex_Visibility, - kMat33f_GrSLType, precision, - strUniName.c_str(), - &uniName).toIndex(); + uniformHandler->addUniform(GrGLSLUniformHandler::kVertex_Visibility, + kMat33f_GrSLType, precision, + strUniName.c_str(), + &uniName).toIndex(); SkString strVaryingName("MatrixCoord"); strVaryingName.appendf("_%i_%i", i, t); @@ -94,8 +95,7 @@ void GrGLSLGeometryProcessor::emitTransforms(GrGLSLGPBuilder* pb, } } -void GrGLSLGeometryProcessor::emitTransforms(GrGLSLGPBuilder* pb, - GrGLSLVertexBuilder* vb, +void GrGLSLGeometryProcessor::emitTransforms(GrGLSLVertexBuilder* vb, GrGLSLVaryingHandler* varyingHandler, const char* localCoords, const TransformsIn& tin, @@ -124,16 +124,15 @@ void GrGLSLGeometryProcessor::emitTransforms(GrGLSLGPBuilder* pb, } } -void GrGLSLGeometryProcessor::setupPosition(GrGLSLGPBuilder* pb, - GrGLSLVertexBuilder* vertBuilder, +void GrGLSLGeometryProcessor::setupPosition(GrGLSLVertexBuilder* vertBuilder, GrGPArgs* gpArgs, const char* posName) { gpArgs->fPositionVar.set(kVec2f_GrSLType, "pos2"); vertBuilder->codeAppendf("vec2 %s = %s;", gpArgs->fPositionVar.c_str(), posName); } -void GrGLSLGeometryProcessor::setupPosition(GrGLSLGPBuilder* pb, - GrGLSLVertexBuilder* vertBuilder, +void GrGLSLGeometryProcessor::setupPosition(GrGLSLVertexBuilder* vertBuilder, + GrGLSLUniformHandler* uniformHandler, GrGPArgs* gpArgs, const char* posName, const SkMatrix& mat, @@ -143,10 +142,10 @@ void GrGLSLGeometryProcessor::setupPosition(GrGLSLGPBuilder* pb, vertBuilder->codeAppendf("vec2 %s = %s;", gpArgs->fPositionVar.c_str(), posName); } else { const char* viewMatrixName; - *viewMatrixUniform = pb->addUniform(GrGLSLProgramBuilder::kVertex_Visibility, - kMat33f_GrSLType, kHigh_GrSLPrecision, - "uViewM", - &viewMatrixName); + *viewMatrixUniform = uniformHandler->addUniform(GrGLSLUniformHandler::kVertex_Visibility, + kMat33f_GrSLType, kHigh_GrSLPrecision, + "uViewM", + &viewMatrixName); if (!mat.hasPerspective()) { gpArgs->fPositionVar.set(kVec2f_GrSLType, "pos2"); vertBuilder->codeAppendf("vec2 %s = vec2(%s * vec3(%s, 1));", diff --git a/gfx/skia/skia/src/gpu/glsl/GrGLSLGeometryProcessor.h b/gfx/skia/skia/src/gpu/glsl/GrGLSLGeometryProcessor.h index 4c586a7900..84a8f8b963 100644 --- a/gfx/skia/skia/src/gpu/glsl/GrGLSLGeometryProcessor.h +++ b/gfx/skia/skia/src/gpu/glsl/GrGLSLGeometryProcessor.h @@ -42,20 +42,21 @@ public: protected: // Emit a uniform matrix for each coord transform. - void emitTransforms(GrGLSLGPBuilder* gp, - GrGLSLVertexBuilder* vb, + void emitTransforms(GrGLSLVertexBuilder* vb, GrGLSLVaryingHandler* varyingHandler, + GrGLSLUniformHandler* uniformHandler, const GrShaderVar& posVar, const char* localCoords, const TransformsIn& tin, TransformsOut* tout) { - this->emitTransforms(gp, vb, varyingHandler, posVar, localCoords, SkMatrix::I(), tin, tout); + this->emitTransforms(vb, varyingHandler, uniformHandler, + posVar, localCoords, SkMatrix::I(), tin, tout); } // Emit pre-transformed coords as a vertex attribute per coord-transform. - void emitTransforms(GrGLSLGPBuilder*, - GrGLSLVertexBuilder*, + void emitTransforms(GrGLSLVertexBuilder*, GrGLSLVaryingHandler*, + GrGLSLUniformHandler*, const GrShaderVar& posVar, const char* localCoords, const SkMatrix& localMatrix, @@ -63,8 +64,7 @@ protected: TransformsOut*); // caller has emitted transforms via attributes - void emitTransforms(GrGLSLGPBuilder*, - GrGLSLVertexBuilder*, + void emitTransforms(GrGLSLVertexBuilder*, GrGLSLVaryingHandler*, const char* localCoords, const TransformsIn& tin, @@ -77,9 +77,9 @@ protected: }; // Create the correct type of position variable given the CTM - void setupPosition(GrGLSLGPBuilder*, GrGLSLVertexBuilder*, GrGPArgs*, const char* posName); - void setupPosition(GrGLSLGPBuilder*, - GrGLSLVertexBuilder*, + void setupPosition(GrGLSLVertexBuilder*, GrGPArgs*, const char* posName); + void setupPosition(GrGLSLVertexBuilder*, + GrGLSLUniformHandler* uniformHandler, GrGPArgs*, const char* posName, const SkMatrix& mat, diff --git a/gfx/skia/skia/src/gpu/glsl/GrGLSLPrimitiveProcessor.cpp b/gfx/skia/skia/src/gpu/glsl/GrGLSLPrimitiveProcessor.cpp index 70b58cda94..23bb249af7 100644 --- a/gfx/skia/skia/src/gpu/glsl/GrGLSLPrimitiveProcessor.cpp +++ b/gfx/skia/skia/src/gpu/glsl/GrGLSLPrimitiveProcessor.cpp @@ -7,8 +7,10 @@ #include "GrGLSLPrimitiveProcessor.h" -#include "glsl/GrGLSLProgramBuilder.h" +#include "GrCoordTransform.h" #include "glsl/GrGLSLFragmentShaderBuilder.h" +#include "glsl/GrGLSLUniformHandler.h" +#include "glsl/GrGLSLVertexShaderBuilder.h" SkMatrix GrGLSLPrimitiveProcessor::GetTransformMatrix(const SkMatrix& localMatrix, const GrCoordTransform& coordTransform) { @@ -32,16 +34,16 @@ SkMatrix GrGLSLPrimitiveProcessor::GetTransformMatrix(const SkMatrix& localMatri return combined; } -void GrGLSLPrimitiveProcessor::setupUniformColor(GrGLSLGPBuilder* pb, - GrGLSLFragmentBuilder* fragBuilder, +void GrGLSLPrimitiveProcessor::setupUniformColor(GrGLSLFragmentBuilder* fragBuilder, + GrGLSLUniformHandler* uniformHandler, const char* outputName, UniformHandle* colorUniform) { SkASSERT(colorUniform); const char* stagedLocalVarName; - *colorUniform = pb->addUniform(GrGLSLProgramBuilder::kFragment_Visibility, - kVec4f_GrSLType, - kDefault_GrSLPrecision, - "Color", - &stagedLocalVarName); + *colorUniform = uniformHandler->addUniform(GrGLSLUniformHandler::kFragment_Visibility, + kVec4f_GrSLType, + kDefault_GrSLPrecision, + "Color", + &stagedLocalVarName); fragBuilder->codeAppendf("%s = %s;", outputName, stagedLocalVarName); } diff --git a/gfx/skia/skia/src/gpu/glsl/GrGLSLPrimitiveProcessor.h b/gfx/skia/skia/src/gpu/glsl/GrGLSLPrimitiveProcessor.h index d164bbe53e..9fa5150e82 100644 --- a/gfx/skia/skia/src/gpu/glsl/GrGLSLPrimitiveProcessor.h +++ b/gfx/skia/skia/src/gpu/glsl/GrGLSLPrimitiveProcessor.h @@ -18,6 +18,7 @@ class GrPrimitiveProcessor; class GrGLSLCaps; class GrGLSLFragmentBuilder; class GrGLSLGPBuilder; +class GrGLSLUniformHandler; class GrGLSLVaryingHandler; class GrGLSLVertexBuilder; @@ -33,10 +34,10 @@ public: typedef SkSTArray<8, GrGLSLTransformedCoordsArray> TransformsOut; struct EmitArgs { - EmitArgs(GrGLSLGPBuilder* pb, - GrGLSLVertexBuilder* vertBuilder, + EmitArgs(GrGLSLVertexBuilder* vertBuilder, GrGLSLFragmentBuilder* fragBuilder, GrGLSLVaryingHandler* varyingHandler, + GrGLSLUniformHandler* uniformHandler, const GrGLSLCaps* caps, const GrPrimitiveProcessor& gp, const char* outputColor, @@ -44,10 +45,10 @@ public: const TextureSamplerArray& samplers, const TransformsIn& transformsIn, TransformsOut* transformsOut) - : fPB(pb) - , fVertBuilder(vertBuilder) + : fVertBuilder(vertBuilder) , fFragBuilder(fragBuilder) , fVaryingHandler(varyingHandler) + , fUniformHandler(uniformHandler) , fGLSLCaps(caps) , fGP(gp) , fOutputColor(outputColor) @@ -55,10 +56,10 @@ public: , fSamplers(samplers) , fTransformsIn(transformsIn) , fTransformsOut(transformsOut) {} - GrGLSLGPBuilder* fPB; GrGLSLVertexBuilder* fVertBuilder; GrGLSLFragmentBuilder* fFragBuilder; GrGLSLVaryingHandler* fVaryingHandler; + GrGLSLUniformHandler* fUniformHandler; const GrGLSLCaps* fGLSLCaps; const GrPrimitiveProcessor& fGP; const char* fOutputColor; @@ -91,8 +92,8 @@ public: const SkTArray& transforms) = 0; protected: - void setupUniformColor(GrGLSLGPBuilder* pb, - GrGLSLFragmentBuilder* fragBuilder, + void setupUniformColor(GrGLSLFragmentBuilder* fragBuilder, + GrGLSLUniformHandler* uniformHandler, const char* outputName, UniformHandle* colorUniform); diff --git a/gfx/skia/skia/src/gpu/glsl/GrGLSLProgramBuilder.cpp b/gfx/skia/skia/src/gpu/glsl/GrGLSLProgramBuilder.cpp index 54e82b30c0..6e0e95f16a 100644 --- a/gfx/skia/skia/src/gpu/glsl/GrGLSLProgramBuilder.cpp +++ b/gfx/skia/skia/src/gpu/glsl/GrGLSLProgramBuilder.cpp @@ -7,6 +7,11 @@ #include "glsl/GrGLSLProgramBuilder.h" +#include "GrPipeline.h" +#include "glsl/GrGLSLFragmentProcessor.h" +#include "glsl/GrGLSLGeometryProcessor.h" +#include "glsl/GrGLSLXferProcessor.h" + const int GrGLSLProgramBuilder::kVarsPerBlock = 8; GrGLSLProgramBuilder::GrGLSLProgramBuilder(const DrawArgs& args) @@ -14,7 +19,203 @@ GrGLSLProgramBuilder::GrGLSLProgramBuilder(const DrawArgs& args) , fGS(this) , fFS(this, args.fDesc->header().fFragPosKey) , fStageIndex(-1) - , fArgs(args) { + , fArgs(args) + , fGeometryProcessor(nullptr) + , fXferProcessor(nullptr) { +} + +bool GrGLSLProgramBuilder::emitAndInstallProcs(GrGLSLExpr4* inputColor, + GrGLSLExpr4* inputCoverage, + int maxTextures) { + // First we loop over all of the installed processors and collect coord transforms. These will + // be sent to the GrGLSLPrimitiveProcessor in its emitCode function + const GrPrimitiveProcessor& primProc = this->primitiveProcessor(); + int totalTextures = primProc.numTextures(); + + for (int i = 0; i < this->pipeline().numFragmentProcessors(); i++) { + const GrFragmentProcessor& processor = this->pipeline().getFragmentProcessor(i); + + if (!primProc.hasTransformedLocalCoords()) { + SkTArray& procCoords = fCoordTransforms.push_back(); + processor.gatherCoordTransforms(&procCoords); + } + + totalTextures += processor.numTextures(); + if (totalTextures >= maxTextures) { + GrCapsDebugf(this->caps(), "Program would use too many texture units\n"); + return false; + } + } + + this->emitAndInstallPrimProc(primProc, inputColor, inputCoverage); + + int numProcs = this->pipeline().numFragmentProcessors(); + this->emitAndInstallFragProcs(0, this->pipeline().numColorFragmentProcessors(), inputColor); + this->emitAndInstallFragProcs(this->pipeline().numColorFragmentProcessors(), numProcs, + inputCoverage); + this->emitAndInstallXferProc(this->pipeline().getXferProcessor(), *inputColor, *inputCoverage, + this->pipeline().ignoresCoverage()); + this->emitFSOutputSwizzle(this->pipeline().getXferProcessor().hasSecondaryOutput()); + return true; +} + +void GrGLSLProgramBuilder::emitAndInstallPrimProc(const GrPrimitiveProcessor& proc, + GrGLSLExpr4* outputColor, + GrGLSLExpr4* outputCoverage) { + // Program builders have a bit of state we need to clear with each effect + AutoStageAdvance adv(this); + this->nameExpression(outputColor, "outputColor"); + this->nameExpression(outputCoverage, "outputCoverage"); + + // Enclose custom code in a block to avoid namespace conflicts + SkString openBrace; + openBrace.printf("{ // Stage %d, %s\n", fStageIndex, proc.name()); + fFS.codeAppend(openBrace.c_str()); + fVS.codeAppendf("// Primitive Processor %s\n", proc.name()); + + SkASSERT(!fGeometryProcessor); + fGeometryProcessor = proc.createGLSLInstance(*this->glslCaps()); + + SkSTArray<4, GrGLSLTextureSampler> samplers(proc.numTextures()); + this->emitSamplers(proc, &samplers); + + GrGLSLGeometryProcessor::EmitArgs args(&fVS, + &fFS, + this->varyingHandler(), + this->uniformHandler(), + this->glslCaps(), + proc, + outputColor->c_str(), + outputCoverage->c_str(), + samplers, + fCoordTransforms, + &fOutCoords); + fGeometryProcessor->emitCode(args); + + // We have to check that effects and the code they emit are consistent, ie if an effect + // asks for dst color, then the emit code needs to follow suit + verify(proc); + + fFS.codeAppend("}"); +} + +void GrGLSLProgramBuilder::emitAndInstallFragProcs(int procOffset, + int numProcs, + GrGLSLExpr4* inOut) { + for (int i = procOffset; i < numProcs; ++i) { + GrGLSLExpr4 output; + const GrFragmentProcessor& fp = this->pipeline().getFragmentProcessor(i); + this->emitAndInstallFragProc(fp, i, *inOut, &output); + *inOut = output; + } +} + +// TODO Processors cannot output zeros because an empty string is all 1s +// the fix is to allow effects to take the GrGLSLExpr4 directly +void GrGLSLProgramBuilder::emitAndInstallFragProc(const GrFragmentProcessor& fp, + int index, + const GrGLSLExpr4& input, + GrGLSLExpr4* output) { + // Program builders have a bit of state we need to clear with each effect + AutoStageAdvance adv(this); + this->nameExpression(output, "output"); + + // Enclose custom code in a block to avoid namespace conflicts + SkString openBrace; + openBrace.printf("{ // Stage %d, %s\n", fStageIndex, fp.name()); + fFS.codeAppend(openBrace.c_str()); + + GrGLSLFragmentProcessor* fragProc = fp.createGLSLInstance(); + + SkSTArray<4, GrGLSLTextureSampler> samplers(fp.numTextures()); + this->emitSamplers(fp, &samplers); + + GrGLSLFragmentProcessor::EmitArgs args(&fFS, + this->uniformHandler(), + this->glslCaps(), + fp, + output->c_str(), + input.isOnes() ? nullptr : input.c_str(), + fOutCoords[index], + samplers); + fragProc->emitCode(args); + + // We have to check that effects and the code they emit are consistent, ie if an effect + // asks for dst color, then the emit code needs to follow suit + verify(fp); + fFragmentProcessors.push_back(fragProc); + + fFS.codeAppend("}"); +} + +void GrGLSLProgramBuilder::emitAndInstallXferProc(const GrXferProcessor& xp, + const GrGLSLExpr4& colorIn, + const GrGLSLExpr4& coverageIn, + bool ignoresCoverage) { + // Program builders have a bit of state we need to clear with each effect + AutoStageAdvance adv(this); + + SkASSERT(!fXferProcessor); + fXferProcessor = xp.createGLSLInstance(); + + // Enable dual source secondary output if we have one + if (xp.hasSecondaryOutput()) { + fFS.enableSecondaryOutput(); + } + + if (this->glslCaps()->mustDeclareFragmentShaderOutput()) { + fFS.enableCustomOutput(); + } + + SkString openBrace; + openBrace.printf("{ // Xfer Processor: %s\n", xp.name()); + fFS.codeAppend(openBrace.c_str()); + + SkSTArray<4, GrGLSLTextureSampler> samplers(xp.numTextures()); + this->emitSamplers(xp, &samplers); + + GrGLSLXferProcessor::EmitArgs args(&fFS, + this->uniformHandler(), + this->glslCaps(), + xp, colorIn.c_str(), + ignoresCoverage ? nullptr : coverageIn.c_str(), + fFS.getPrimaryColorOutputName(), + fFS.getSecondaryColorOutputName(), + samplers); + fXferProcessor->emitCode(args); + + // We have to check that effects and the code they emit are consistent, ie if an effect + // asks for dst color, then the emit code needs to follow suit + verify(xp); + fFS.codeAppend("}"); +} + +void GrGLSLProgramBuilder::emitFSOutputSwizzle(bool hasSecondaryOutput) { + // Swizzle the fragment shader outputs if necessary. + GrSwizzle swizzle; + swizzle.setFromKey(this->desc().header().fOutputSwizzle); + if (swizzle != GrSwizzle::RGBA()) { + fFS.codeAppendf("%s = %s.%s;", fFS.getPrimaryColorOutputName(), + fFS.getPrimaryColorOutputName(), + swizzle.c_str()); + if (hasSecondaryOutput) { + fFS.codeAppendf("%s = %s.%s;", fFS.getSecondaryColorOutputName(), + fFS.getSecondaryColorOutputName(), + swizzle.c_str()); + } + } +} + +void GrGLSLProgramBuilder::verify(const GrPrimitiveProcessor& gp) { + SkASSERT(fFS.hasReadFragmentPosition() == gp.willReadFragmentPosition()); +} + +void GrGLSLProgramBuilder::verify(const GrXferProcessor& xp) { + SkASSERT(fFS.hasReadDstColor() == xp.willReadDstColor()); +} + +void GrGLSLProgramBuilder::verify(const GrFragmentProcessor& fp) { + SkASSERT(fFS.hasReadFragmentPosition() == fp.willReadFragmentPosition()); } void GrGLSLProgramBuilder::nameVariable(SkString* out, char prefix, const char* name, bool mangle) { @@ -32,8 +233,49 @@ void GrGLSLProgramBuilder::nameVariable(SkString* out, char prefix, const char* } } -void GrGLSLProgramBuilder::appendUniformDecls(ShaderVisibility visibility, - SkString* out) const { - this->onAppendUniformDecls(visibility, out); +void GrGLSLProgramBuilder::nameExpression(GrGLSLExpr4* output, const char* baseName) { + // create var to hold stage result. If we already have a valid output name, just use that + // otherwise create a new mangled one. This name is only valid if we are reordering stages + // and have to tell stage exactly where to put its output. + SkString outName; + if (output->isValid()) { + outName = output->c_str(); + } else { + this->nameVariable(&outName, '\0', baseName); + } + fFS.codeAppendf("vec4 %s;", outName.c_str()); + *output = outName; +} + +void GrGLSLProgramBuilder::appendUniformDecls(ShaderVisibility visibility, + SkString* out) const { + this->uniformHandler()->appendUniformDecls(visibility, out); +} + +void GrGLSLProgramBuilder::addRTAdjustmentUniform(GrSLPrecision precision, + const char* name, + const char** outName) { + SkASSERT(!fUniformHandles.fRTAdjustmentUni.isValid()); + fUniformHandles.fRTAdjustmentUni = + this->uniformHandler()->addUniform(GrGLSLUniformHandler::kVertex_Visibility, + kVec4f_GrSLType, + precision, + name, + outName); +} + +void GrGLSLProgramBuilder::addRTHeightUniform(const char* name, const char** outName) { + SkASSERT(!fUniformHandles.fRTHeightUni.isValid()); + GrGLSLUniformHandler* uniformHandler = this->uniformHandler(); + fUniformHandles.fRTHeightUni = + uniformHandler->internalAddUniformArray(GrGLSLUniformHandler::kFragment_Visibility, + kFloat_GrSLType, kDefault_GrSLPrecision, + name, false, 0, outName); +} + +void GrGLSLProgramBuilder::cleanupFragmentProcessors() { + for (int i = 0; i < fFragmentProcessors.count(); ++i) { + delete fFragmentProcessors[i]; + } } diff --git a/gfx/skia/skia/src/gpu/glsl/GrGLSLProgramBuilder.h b/gfx/skia/skia/src/gpu/glsl/GrGLSLProgramBuilder.h index 81037084e9..964d320298 100644 --- a/gfx/skia/skia/src/gpu/glsl/GrGLSLProgramBuilder.h +++ b/gfx/skia/skia/src/gpu/glsl/GrGLSLProgramBuilder.h @@ -12,106 +12,37 @@ #include "GrGpu.h" #include "glsl/GrGLSLFragmentShaderBuilder.h" #include "glsl/GrGLSLGeometryShaderBuilder.h" +#include "glsl/GrGLSLPrimitiveProcessor.h" #include "glsl/GrGLSLProgramDataManager.h" +#include "glsl/GrGLSLUniformHandler.h" +#include "glsl/GrGLSLTextureSampler.h" #include "glsl/GrGLSLVertexShaderBuilder.h" +#include "glsl/GrGLSLXferProcessor.h" class GrGLSLCaps; class GrGLSLShaderVar; class GrGLSLVaryingHandler; -// Enough precision to represent 1 / 2048 accurately in printf -#define GR_SIGNIFICANT_POW2_DECIMAL_DIG 11 +typedef SkSTArray<8, GrGLSLFragmentProcessor*, true> GrGLSLFragProcs; -class GrGLSLUniformBuilder { -public: - enum ShaderVisibility { - kVertex_Visibility = 1 << kVertex_GrShaderType, - kGeometry_Visibility = 1 << kGeometry_GrShaderType, - kFragment_Visibility = 1 << kFragment_GrShaderType, - }; - - virtual ~GrGLSLUniformBuilder() {} - - typedef GrGLSLProgramDataManager::UniformHandle UniformHandle; - - /** Add a uniform variable to the current program, that has visibility in one or more shaders. - visibility is a bitfield of ShaderVisibility values indicating from which shaders the - uniform should be accessible. At least one bit must be set. Geometry shader uniforms are not - supported at this time. The actual uniform name will be mangled. If outName is not nullptr - then it will refer to the final uniform name after return. Use the addUniformArray variant - to add an array of uniforms. */ - UniformHandle addUniform(uint32_t visibility, - GrSLType type, - GrSLPrecision precision, - const char* name, - const char** outName = nullptr) { - return this->addUniformArray(visibility, type, precision, name, 0, outName); - } - - UniformHandle addUniformArray(uint32_t visibility, - GrSLType type, - GrSLPrecision precision, - const char* name, - int arrayCount, - const char** outName = nullptr) { - return this->internalAddUniformArray(visibility, type, precision, name, true, arrayCount, - outName); - } - - virtual const GrGLSLShaderVar& getUniformVariable(UniformHandle u) const = 0; - - /** - * Shortcut for getUniformVariable(u).c_str() - */ - virtual const char* getUniformCStr(UniformHandle u) const = 0; - - /* - * *NOTE* NO MEMBERS ALLOWED, MULTIPLE INHERITANCE - */ -protected: - virtual UniformHandle internalAddUniformArray( - uint32_t visibility, - GrSLType type, - GrSLPrecision precision, - const char* name, - bool mangleName, - int arrayCount, - const char** outName) = 0; -}; - -/* a specialization of the above for GPs. Lets the user add uniforms, varyings, and VS / FS code */ -class GrGLSLGPBuilder : public virtual GrGLSLUniformBuilder { -public: - /* - * *NOTE* NO MEMBERS ALLOWED, MULTIPLE INHERITANCE - */ -}; - - -/* a specializations for FPs. Lets the user add uniforms and FS code */ -class GrGLSLFPBuilder : public virtual GrGLSLUniformBuilder { -public: - /* - * *NOTE* NO MEMBERS ALLOWED, MULTIPLE INHERITANCE - */ -}; - -/* a specializations for XPs. Lets the user add uniforms and FS code */ -class GrGLSLXPBuilder : public virtual GrGLSLUniformBuilder { -public: - /* - * *NOTE* NO MEMBERS ALLOWED, MULTIPLE INHERITANCE - */ -}; - -class GrGLSLProgramBuilder : public GrGLSLGPBuilder, - public GrGLSLFPBuilder, - public GrGLSLXPBuilder { +class GrGLSLProgramBuilder { public: typedef GrGpu::DrawArgs DrawArgs; + typedef GrGLSLUniformHandler::ShaderVisibility ShaderVisibility; + typedef GrGLSLUniformHandler::UniformHandle UniformHandle; + virtual ~GrGLSLProgramBuilder() {} + + virtual const GrCaps* caps() const = 0; virtual const GrGLSLCaps* glslCaps() const = 0; + const GrPrimitiveProcessor& primitiveProcessor() const { return *fArgs.fPrimitiveProcessor; } + const GrPipeline& pipeline() const { return *fArgs.fPipeline; } + const GrProgramDesc& desc() const { return *fArgs.fDesc; } + const GrProgramDesc::KeyHeader& header() const { return fArgs.fDesc->header(); } + + void appendUniformDecls(ShaderVisibility, SkString*) const; + // Handles for program uniforms (other than per-effect uniforms) struct BuiltinUniformHandles { UniformHandle fRTAdjustmentUni; @@ -121,33 +52,21 @@ public: UniformHandle fRTHeightUni; }; -protected: - explicit GrGLSLProgramBuilder(const DrawArgs& args); - - const GrPrimitiveProcessor& primitiveProcessor() const { return *fArgs.fPrimitiveProcessor; } - const GrPipeline& pipeline() const { return *fArgs.fPipeline; } - const GrProgramDesc& desc() const { return *fArgs.fDesc; } - const GrProgramDesc::KeyHeader& header() const { return fArgs.fDesc->header(); } - - void appendUniformDecls(ShaderVisibility, SkString*) const; - - // Used to add a uniform for frag position without mangling the name of the uniform inside of a - // stage. - UniformHandle addFragPosUniform(uint32_t visibility, - GrSLType type, - GrSLPrecision precision, - const char* name, - const char** outName) { - return this->internalAddUniformArray(visibility, type, precision, name, false, 0, outName); - } - + // Used to add a uniform in the vertex shader for transforming into normalized device space. + void addRTAdjustmentUniform(GrSLPrecision precision, const char* name, const char** outName); const char* rtAdjustment() const { return "rtAdjustment"; } + + // Used to add a uniform for the RenderTarget height (used for frag position) without mangling + // the name of the uniform inside of a stage. + void addRTHeightUniform(const char* name, const char** outName); // Generates a name for a variable. The generated string will be name prefixed by the prefix // char (unless the prefix is '\0'). It also will mangle the name to be stage-specific unless // explicitly asked not to. void nameVariable(SkString* out, char prefix, const char* name, bool mangle = true); + virtual GrGLSLUniformHandler* uniformHandler() = 0; + virtual const GrGLSLUniformHandler* uniformHandler() const = 0; virtual GrGLSLVaryingHandler* varyingHandler() = 0; // number of each input/output type in a single allocation block, used by many builders @@ -159,18 +78,71 @@ protected: int fStageIndex; - BuiltinUniformHandles fUniformHandles; - const DrawArgs& fArgs; -private: - virtual void onAppendUniformDecls(ShaderVisibility visibility, SkString* out) const = 0; + BuiltinUniformHandles fUniformHandles; - friend class GrGLSLShaderBuilder; - friend class GrGLSLVertexBuilder; - friend class GrGLSLFragmentShaderBuilder; - friend class GrGLSLGeometryBuilder; - friend class GrGLSLVaryingHandler; + GrGLSLPrimitiveProcessor* fGeometryProcessor; + GrGLSLXferProcessor* fXferProcessor; + GrGLSLFragProcs fFragmentProcessors; + +protected: + explicit GrGLSLProgramBuilder(const DrawArgs& args); + + bool emitAndInstallProcs(GrGLSLExpr4* inputColor, GrGLSLExpr4* inputCoverage, int maxTextures); + + void cleanupFragmentProcessors(); + +private: + // reset is called by program creator between each processor's emit code. It increments the + // stage offset for variable name mangling, and also ensures verfication variables in the + // fragment shader are cleared. + void reset() { + this->addStage(); + fFS.reset(); + } + void addStage() { fStageIndex++; } + + class AutoStageAdvance { + public: + AutoStageAdvance(GrGLSLProgramBuilder* pb) + : fPB(pb) { + fPB->reset(); + // Each output to the fragment processor gets its own code section + fPB->fFS.nextStage(); + } + ~AutoStageAdvance() {} + private: + GrGLSLProgramBuilder* fPB; + }; + + // Generates a possibly mangled name for a stage variable and writes it to the fragment shader. + // If GrGLSLExpr4 has a valid name then it will use that instead + void nameExpression(GrGLSLExpr4*, const char* baseName); + + void emitAndInstallPrimProc(const GrPrimitiveProcessor&, + GrGLSLExpr4* outputColor, + GrGLSLExpr4* outputCoverage); + void emitAndInstallFragProcs(int procOffset, int numProcs, GrGLSLExpr4* inOut); + void emitAndInstallFragProc(const GrFragmentProcessor&, + int index, + const GrGLSLExpr4& input, + GrGLSLExpr4* output); + void emitAndInstallXferProc(const GrXferProcessor&, + const GrGLSLExpr4& colorIn, + const GrGLSLExpr4& coverageIn, + bool ignoresCoverage); + void emitFSOutputSwizzle(bool hasSecondaryOutput); + + void verify(const GrPrimitiveProcessor&); + void verify(const GrXferProcessor&); + void verify(const GrFragmentProcessor&); + + virtual void emitSamplers(const GrProcessor& processor, + GrGLSLTextureSampler::TextureSamplerArray* outSamplers) = 0; + + GrGLSLPrimitiveProcessor::TransformsIn fCoordTransforms; + GrGLSLPrimitiveProcessor::TransformsOut fOutCoords; }; #endif diff --git a/gfx/skia/skia/src/gpu/glsl/GrGLSLShaderBuilder.cpp b/gfx/skia/skia/src/gpu/glsl/GrGLSLShaderBuilder.cpp index 1fc15ecb3c..f1ede1decd 100644 --- a/gfx/skia/skia/src/gpu/glsl/GrGLSLShaderBuilder.cpp +++ b/gfx/skia/skia/src/gpu/glsl/GrGLSLShaderBuilder.cpp @@ -5,70 +5,13 @@ * found in the LICENSE file. */ +#include "GrSwizzle.h" #include "glsl/GrGLSLShaderBuilder.h" #include "glsl/GrGLSLCaps.h" #include "glsl/GrGLSLShaderVar.h" #include "glsl/GrGLSLTextureSampler.h" #include "glsl/GrGLSLProgramBuilder.h" -static void map_swizzle(const char* swizzleMap, const char* swizzle, char* mangledSwizzle) { - int i; - for (i = 0; '\0' != swizzle[i]; ++i) { - switch (swizzle[i]) { - case 'r': - mangledSwizzle[i] = swizzleMap[0]; - break; - case 'g': - mangledSwizzle[i] = swizzleMap[1]; - break; - case 'b': - mangledSwizzle[i] = swizzleMap[2]; - break; - case 'a': - mangledSwizzle[i] = swizzleMap[3]; - break; - default: - SkFAIL("Unsupported swizzle"); - } - } - mangledSwizzle[i] ='\0'; -} - -static void append_texture_lookup(SkString* out, - const GrGLSLCaps* glslCaps, - const char* samplerName, - const char* coordName, - GrPixelConfig config, - const char* swizzle, - GrSLType varyingType = kVec2f_GrSLType) { - SkASSERT(coordName); - - out->appendf("%s(%s, %s)", - GrGLSLTexture2DFunctionName(varyingType, glslCaps->generation()), - samplerName, - coordName); - - char mangledSwizzle[5]; - - // This refers to any swizzling we may need to get from some backend internal format to the - // format used in GrPixelConfig. Some backends will automatically do the sizzling for us. - if (glslCaps->mustSwizzleInShader()) { - const char* swizzleMap = glslCaps->getSwizzleMap(config); - // if the map is simply 'rgba' then we don't need to do any manual swizzling to get us to - // a GrPixelConfig format. - if (memcmp(swizzleMap, "rgba", 4)) { - // Manually 'swizzle' the swizzle using our mapping - map_swizzle(swizzleMap, swizzle, mangledSwizzle); - swizzle = mangledSwizzle; - } - } - - // For shader prettiness we omit the swizzle rather than appending ".rgba". - if (memcmp(swizzle, "rgba", 4)) { - out->appendf(".%s", swizzle); - } -} - GrGLSLShaderBuilder::GrGLSLShaderBuilder(GrGLSLProgramBuilder* program) : fProgramBuilder(program) , fInputs(GrGLSLProgramBuilder::kVarsPerBlock) @@ -117,13 +60,22 @@ void GrGLSLShaderBuilder::appendTextureLookup(SkString* out, const GrGLSLTextureSampler& sampler, const char* coordName, GrSLType varyingType) const { - append_texture_lookup(out, - fProgramBuilder->glslCaps(), - fProgramBuilder->getUniformCStr(sampler.fSamplerUniform), - coordName, - sampler.config(), - sampler.swizzle(), - varyingType); + const GrGLSLCaps* glslCaps = fProgramBuilder->glslCaps(); + GrGLSLUniformHandler* uniformHandler = fProgramBuilder->uniformHandler(); + GrSLType samplerType = uniformHandler->getUniformVariable(sampler.fSamplerUniform).getType(); + out->appendf("%s(%s, %s)", + GrGLSLTexture2DFunctionName(varyingType, samplerType, glslCaps->generation()), + uniformHandler->getUniformCStr(sampler.fSamplerUniform), + coordName); + + // This refers to any swizzling we may need to get from some backend internal format to the + // format used in GrPixelConfig. If this is implemented by the GrGpu object, then swizzle will + // be rgba. For shader prettiness we omit the swizzle rather than appending ".rgba". + const GrSwizzle& configSwizzle = glslCaps->configTextureSwizzle(sampler.config()); + + if (configSwizzle != GrSwizzle::RGBA()) { + out->appendf(".%s", configSwizzle.c_str()); + } } void GrGLSLShaderBuilder::appendTextureLookup(const GrGLSLTextureSampler& sampler, @@ -187,7 +139,7 @@ void GrGLSLShaderBuilder::finalize(uint32_t visibility) { this->versionDecl() = fProgramBuilder->glslCaps()->versionDeclString(); this->compileAndAppendLayoutQualifiers(); SkASSERT(visibility); - fProgramBuilder->appendUniformDecls((GrGLSLProgramBuilder::ShaderVisibility) visibility, + fProgramBuilder->appendUniformDecls((GrGLSLUniformHandler::ShaderVisibility) visibility, &this->uniforms()); this->appendDecls(fInputs, &this->inputs()); this->appendDecls(fOutputs, &this->outputs()); diff --git a/gfx/skia/skia/src/gpu/glsl/GrGLSLShaderBuilder.h b/gfx/skia/skia/src/gpu/glsl/GrGLSLShaderBuilder.h index 1a8255dbb4..16a0756306 100644 --- a/gfx/skia/skia/src/gpu/glsl/GrGLSLShaderBuilder.h +++ b/gfx/skia/skia/src/gpu/glsl/GrGLSLShaderBuilder.h @@ -191,7 +191,9 @@ protected: int fCodeIndex; bool fFinalized; + friend class GrGLSLProgramBuilder; friend class GrGLProgramBuilder; friend class GrGLPathProgramBuilder; // to access fInputs. + friend class GrVkProgramBuilder; }; #endif diff --git a/gfx/skia/skia/src/gpu/glsl/GrGLSLTextureSampler.h b/gfx/skia/skia/src/gpu/glsl/GrGLSLTextureSampler.h index a4fbf550f2..fd8bcb26e9 100644 --- a/gfx/skia/skia/src/gpu/glsl/GrGLSLTextureSampler.h +++ b/gfx/skia/skia/src/gpu/glsl/GrGLSLTextureSampler.h @@ -21,17 +21,13 @@ public: : fSamplerUniform(uniform) , fConfig(access.getTexture()->config()) { SkASSERT(kUnknown_GrPixelConfig != fConfig); - memcpy(fSwizzle, access.getSwizzle(), 5); } GrPixelConfig config() const { return fConfig; } - // this is .abcd - const char* swizzle() const { return fSwizzle; } private: UniformHandle fSamplerUniform; GrPixelConfig fConfig; - char fSwizzle[5]; friend class GrGLSLShaderBuilder; }; diff --git a/gfx/skia/skia/src/gpu/glsl/GrGLSLUniformHandler.h b/gfx/skia/skia/src/gpu/glsl/GrGLSLUniformHandler.h new file mode 100644 index 0000000000..e72716d04e --- /dev/null +++ b/gfx/skia/skia/src/gpu/glsl/GrGLSLUniformHandler.h @@ -0,0 +1,79 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef GrGLSLUniformHandler_DEFINED +#define GrGLSLUniformHandler_DEFINED + +#include "GrGLSLProgramDataManager.h" +#include "GrGLSLShaderVar.h" + +class GrGLSLProgramBuilder; + +class GrGLSLUniformHandler { +public: + enum ShaderVisibility { + kVertex_Visibility = 1 << kVertex_GrShaderType, + kGeometry_Visibility = 1 << kGeometry_GrShaderType, + kFragment_Visibility = 1 << kFragment_GrShaderType, + }; + + virtual ~GrGLSLUniformHandler() {} + + typedef GrGLSLProgramDataManager::UniformHandle UniformHandle; + + /** Add a uniform variable to the current program, that has visibility in one or more shaders. + visibility is a bitfield of ShaderVisibility values indicating from which shaders the + uniform should be accessible. At least one bit must be set. Geometry shader uniforms are not + supported at this time. The actual uniform name will be mangled. If outName is not nullptr + then it will refer to the final uniform name after return. Use the addUniformArray variant + to add an array of uniforms. */ + UniformHandle addUniform(uint32_t visibility, + GrSLType type, + GrSLPrecision precision, + const char* name, + const char** outName = nullptr) { + return this->addUniformArray(visibility, type, precision, name, 0, outName); + } + + UniformHandle addUniformArray(uint32_t visibility, + GrSLType type, + GrSLPrecision precision, + const char* name, + int arrayCount, + const char** outName = nullptr) { + return this->internalAddUniformArray(visibility, type, precision, name, true, arrayCount, + outName); + } + + virtual const GrGLSLShaderVar& getUniformVariable(UniformHandle u) const = 0; + + /** + * Shortcut for getUniformVariable(u).c_str() + */ + virtual const char* getUniformCStr(UniformHandle u) const = 0; +protected: + explicit GrGLSLUniformHandler(GrGLSLProgramBuilder* program) : fProgramBuilder(program) {} + + // This is not owned by the class + GrGLSLProgramBuilder* fProgramBuilder; + +private: + virtual UniformHandle internalAddUniformArray(uint32_t visibility, + GrSLType type, + GrSLPrecision precision, + const char* name, + bool mangleName, + int arrayCount, + const char** outName) = 0; + + virtual void appendUniformDecls(ShaderVisibility, SkString*) const = 0; + + friend class GrGLSLProgramBuilder; +}; + +#endif + diff --git a/gfx/skia/skia/src/gpu/glsl/GrGLSLVertexShaderBuilder.cpp b/gfx/skia/skia/src/gpu/glsl/GrGLSLVertexShaderBuilder.cpp index 73c0fcd81e..005b2725ff 100644 --- a/gfx/skia/skia/src/gpu/glsl/GrGLSLVertexShaderBuilder.cpp +++ b/gfx/skia/skia/src/gpu/glsl/GrGLSLVertexShaderBuilder.cpp @@ -7,6 +7,7 @@ #include "GrGLSLVertexShaderBuilder.h" #include "glsl/GrGLSLProgramBuilder.h" +#include "glsl/GrGLSLUniformHandler.h" #include "glsl/GrGLSLVarying.h" GrGLSLVertexBuilder::GrGLSLVertexBuilder(GrGLSLProgramBuilder* program) @@ -17,17 +18,10 @@ GrGLSLVertexBuilder::GrGLSLVertexBuilder(GrGLSLProgramBuilder* program) void GrGLSLVertexBuilder::transformToNormalizedDeviceSpace(const GrShaderVar& posVar) { SkASSERT(!fRtAdjustName); - GrSLPrecision precision = kDefault_GrSLPrecision; - if (fProgramBuilder->glslCaps()->forceHighPrecisionNDSTransform()) { - precision = kHigh_GrSLPrecision; - } - // setup RT Uniform - fProgramBuilder->fUniformHandles.fRTAdjustmentUni = - fProgramBuilder->addUniform(GrGLSLProgramBuilder::kVertex_Visibility, - kVec4f_GrSLType, precision, - fProgramBuilder->rtAdjustment(), - &fRtAdjustName); + fProgramBuilder->addRTAdjustmentUniform(kHigh_GrSLPrecision, + fProgramBuilder->rtAdjustment(), + &fRtAdjustName); if (this->getProgramBuilder()->desc().header().fSnapVerticesToPixelCenters) { if (kVec3f_GrSLType == posVar.getType()) { const char* p = posVar.c_str(); diff --git a/gfx/skia/skia/src/gpu/glsl/GrGLSLVertexShaderBuilder.h b/gfx/skia/skia/src/gpu/glsl/GrGLSLVertexShaderBuilder.h index b76721fd00..7c624cee65 100644 --- a/gfx/skia/skia/src/gpu/glsl/GrGLSLVertexShaderBuilder.h +++ b/gfx/skia/skia/src/gpu/glsl/GrGLSLVertexShaderBuilder.h @@ -13,6 +13,9 @@ class GrGLSLVarying; +// Enough precision to represent 1 / 2048 accurately in printf +#define GR_SIGNIFICANT_POW2_DECIMAL_DIG 11 + class GrGLSLVertexBuilder : public GrGLSLShaderBuilder { public: GrGLSLVertexBuilder(GrGLSLProgramBuilder* program); diff --git a/gfx/skia/skia/src/gpu/glsl/GrGLSLXferProcessor.cpp b/gfx/skia/skia/src/gpu/glsl/GrGLSLXferProcessor.cpp index b6af968651..81bd015718 100644 --- a/gfx/skia/skia/src/gpu/glsl/GrGLSLXferProcessor.cpp +++ b/gfx/skia/skia/src/gpu/glsl/GrGLSLXferProcessor.cpp @@ -9,8 +9,8 @@ #include "GrXferProcessor.h" #include "glsl/GrGLSLFragmentShaderBuilder.h" -#include "glsl/GrGLSLProgramBuilder.h" #include "glsl/GrGLSLProgramDataManager.h" +#include "glsl/GrGLSLUniformHandler.h" void GrGLSLXferProcessor::emitCode(const EmitArgs& args) { if (!args.fXP.willReadDstColor()) { @@ -19,6 +19,7 @@ void GrGLSLXferProcessor::emitCode(const EmitArgs& args) { } GrGLSLXPFragmentBuilder* fragBuilder = args.fXPFragBuilder; + GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; const char* dstColor = fragBuilder->dstColor(); if (args.fXP.getDstTexture()) { @@ -35,16 +36,16 @@ void GrGLSLXferProcessor::emitCode(const EmitArgs& args) { const char* dstTopLeftName; const char* dstCoordScaleName; - fDstTopLeftUni = args.fPB->addUniform(GrGLSLProgramBuilder::kFragment_Visibility, - kVec2f_GrSLType, - kDefault_GrSLPrecision, - "DstTextureUpperLeft", - &dstTopLeftName); - fDstScaleUni = args.fPB->addUniform(GrGLSLProgramBuilder::kFragment_Visibility, - kVec2f_GrSLType, - kDefault_GrSLPrecision, - "DstTextureCoordScale", - &dstCoordScaleName); + fDstTopLeftUni = uniformHandler->addUniform(GrGLSLUniformHandler::kFragment_Visibility, + kVec2f_GrSLType, + kDefault_GrSLPrecision, + "DstTextureUpperLeft", + &dstTopLeftName); + fDstScaleUni = uniformHandler->addUniform(GrGLSLUniformHandler::kFragment_Visibility, + kVec2f_GrSLType, + kDefault_GrSLPrecision, + "DstTextureCoordScale", + &dstCoordScaleName); const char* fragPos = fragBuilder->fragmentPosition(); fragBuilder->codeAppend("// Read color from copy of the destination.\n"); @@ -60,8 +61,8 @@ void GrGLSLXferProcessor::emitCode(const EmitArgs& args) { fragBuilder->codeAppend(";"); } - this->emitBlendCodeForDstRead(args.fPB, - fragBuilder, + this->emitBlendCodeForDstRead(fragBuilder, + uniformHandler, args.fInputColor, args.fInputCoverage, dstColor, diff --git a/gfx/skia/skia/src/gpu/glsl/GrGLSLXferProcessor.h b/gfx/skia/skia/src/gpu/glsl/GrGLSLXferProcessor.h index 3707d93459..ed8099b6f3 100644 --- a/gfx/skia/skia/src/gpu/glsl/GrGLSLXferProcessor.h +++ b/gfx/skia/skia/src/gpu/glsl/GrGLSLXferProcessor.h @@ -12,9 +12,10 @@ #include "glsl/GrGLSLTextureSampler.h" class GrXferProcessor; +class GrGLSLCaps; +class GrGLSLUniformHandler; class GrGLSLXPBuilder; class GrGLSLXPFragmentBuilder; -class GrGLSLCaps; class GrGLSLXferProcessor { public: @@ -23,8 +24,8 @@ public: typedef GrGLSLTextureSampler::TextureSamplerArray TextureSamplerArray; struct EmitArgs { - EmitArgs(GrGLSLXPBuilder* pb, - GrGLSLXPFragmentBuilder* fragBuilder, + EmitArgs(GrGLSLXPFragmentBuilder* fragBuilder, + GrGLSLUniformHandler* uniformHandler, const GrGLSLCaps* caps, const GrXferProcessor& xp, const char* inputColor, @@ -32,8 +33,8 @@ public: const char* outputPrimary, const char* outputSecondary, const TextureSamplerArray& samplers) - : fPB(pb) - , fXPFragBuilder(fragBuilder) + : fXPFragBuilder(fragBuilder) + , fUniformHandler(uniformHandler) , fGLSLCaps(caps) , fXP(xp) , fInputColor(inputColor) @@ -42,8 +43,8 @@ public: , fOutputSecondary(outputSecondary) , fSamplers(samplers) {} - GrGLSLXPBuilder* fPB; GrGLSLXPFragmentBuilder* fXPFragBuilder; + GrGLSLUniformHandler* fUniformHandler; const GrGLSLCaps* fGLSLCaps; const GrXferProcessor& fXP; const char* fInputColor; @@ -82,8 +83,8 @@ private: * the blending logic. The base class applies coverage. A subclass only needs to implement this * method if it can construct a GrXferProcessor that reads the dst color. */ - virtual void emitBlendCodeForDstRead(GrGLSLXPBuilder*, - GrGLSLXPFragmentBuilder*, + virtual void emitBlendCodeForDstRead(GrGLSLXPFragmentBuilder*, + GrGLSLUniformHandler*, const char* srcColor, const char* srcCoverage, const char* dstColor, diff --git a/gfx/skia/skia/src/gpu/text/GrAtlasTextBlob.cpp b/gfx/skia/skia/src/gpu/text/GrAtlasTextBlob.cpp new file mode 100644 index 0000000000..f79f82598f --- /dev/null +++ b/gfx/skia/skia/src/gpu/text/GrAtlasTextBlob.cpp @@ -0,0 +1,516 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "GrAtlasTextBlob.h" + +#include "GrBlurUtils.h" +#include "GrContext.h" +#include "GrDrawContext.h" +#include "GrTextUtils.h" +#include "SkColorFilter.h" +#include "SkDrawFilter.h" +#include "SkGlyphCache.h" +#include "SkTextBlobRunIterator.h" +#include "batches/GrAtlasTextBatch.h" + +SkGlyphCache* GrAtlasTextBlob::setupCache(int runIndex, + const SkSurfaceProps& props, + const SkPaint& skPaint, + const SkMatrix* viewMatrix, + bool noGamma) { + GrAtlasTextBlob::Run* run = &fRuns[runIndex]; + + // if we have an override descriptor for the run, then we should use that + SkAutoDescriptor* desc = run->fOverrideDescriptor.get() ? run->fOverrideDescriptor.get() : + &run->fDescriptor; + skPaint.getScalerContextDescriptor(desc, props, viewMatrix, noGamma); + run->fTypeface.reset(SkSafeRef(skPaint.getTypeface())); + return SkGlyphCache::DetachCache(run->fTypeface, desc->getDesc()); +} + +void GrAtlasTextBlob::appendGlyph(int runIndex, + const SkRect& positions, + GrColor color, + GrBatchTextStrike* strike, + GrGlyph* glyph, + GrFontScaler* scaler, const SkGlyph& skGlyph, + SkScalar x, SkScalar y, SkScalar scale, bool applyVM) { + + // If the glyph is too large we fall back to paths + if (glyph->fTooLargeForAtlas) { + this->appendLargeGlyph(glyph, scaler, skGlyph, x, y, scale, applyVM); + return; + } + + Run& run = fRuns[runIndex]; + GrMaskFormat format = glyph->fMaskFormat; + + Run::SubRunInfo* subRun = &run.fSubRunInfo.back(); + if (run.fInitialized && subRun->maskFormat() != format) { + subRun = &run.push_back(); + subRun->setStrike(strike); + } else if (!run.fInitialized) { + subRun->setStrike(strike); + } + + run.fInitialized = true; + + size_t vertexStride = GetVertexStride(format); + + subRun->setMaskFormat(format); + + run.fVertexBounds.joinNonEmptyArg(positions); + subRun->setColor(color); + + intptr_t vertex = reinterpret_cast(this->fVertices + subRun->vertexEndIndex()); + + if (kARGB_GrMaskFormat != glyph->fMaskFormat) { + // V0 + SkPoint* position = reinterpret_cast(vertex); + position->set(positions.fLeft, positions.fTop); + SkColor* colorPtr = reinterpret_cast(vertex + sizeof(SkPoint)); + *colorPtr = color; + vertex += vertexStride; + + // V1 + position = reinterpret_cast(vertex); + position->set(positions.fLeft, positions.fBottom); + colorPtr = reinterpret_cast(vertex + sizeof(SkPoint)); + *colorPtr = color; + vertex += vertexStride; + + // V2 + position = reinterpret_cast(vertex); + position->set(positions.fRight, positions.fBottom); + colorPtr = reinterpret_cast(vertex + sizeof(SkPoint)); + *colorPtr = color; + vertex += vertexStride; + + // V3 + position = reinterpret_cast(vertex); + position->set(positions.fRight, positions.fTop); + colorPtr = reinterpret_cast(vertex + sizeof(SkPoint)); + *colorPtr = color; + } else { + // V0 + SkPoint* position = reinterpret_cast(vertex); + position->set(positions.fLeft, positions.fTop); + vertex += vertexStride; + + // V1 + position = reinterpret_cast(vertex); + position->set(positions.fLeft, positions.fBottom); + vertex += vertexStride; + + // V2 + position = reinterpret_cast(vertex); + position->set(positions.fRight, positions.fBottom); + vertex += vertexStride; + + // V3 + position = reinterpret_cast(vertex); + position->set(positions.fRight, positions.fTop); + } + subRun->appendVertices(vertexStride); + fGlyphs[subRun->glyphEndIndex()] = glyph; + subRun->glyphAppended(); +} + +void GrAtlasTextBlob::appendLargeGlyph(GrGlyph* glyph, GrFontScaler* scaler, const SkGlyph& skGlyph, + SkScalar x, SkScalar y, SkScalar scale, bool applyVM) { + if (nullptr == glyph->fPath) { + const SkPath* glyphPath = scaler->getGlyphPath(skGlyph); + if (!glyphPath) { + return; + } + + glyph->fPath = new SkPath(*glyphPath); + } + fBigGlyphs.push_back(GrAtlasTextBlob::BigGlyph(*glyph->fPath, x, y, scale, applyVM)); +} + +bool GrAtlasTextBlob::mustRegenerate(SkScalar* outTransX, SkScalar* outTransY, + const SkPaint& paint, + GrColor color, const SkMaskFilter::BlurRec& blurRec, + const SkMatrix& viewMatrix, SkScalar x, SkScalar y) { + // If we have LCD text then our canonical color will be set to transparent, in this case we have + // to regenerate the blob on any color change + // We use the grPaint to get any color filter effects + if (fKey.fCanonicalColor == SK_ColorTRANSPARENT && + fPaintColor != color) { + return true; + } + + if (fViewMatrix.hasPerspective() != viewMatrix.hasPerspective()) { + return true; + } + + if (fViewMatrix.hasPerspective() && !fViewMatrix.cheapEqualTo(viewMatrix)) { + return true; + } + + // We only cache one masked version + if (fKey.fHasBlur && + (fBlurRec.fSigma != blurRec.fSigma || + fBlurRec.fStyle != blurRec.fStyle || + fBlurRec.fQuality != blurRec.fQuality)) { + return true; + } + + // Similarly, we only cache one version for each style + if (fKey.fStyle != SkPaint::kFill_Style && + (fStrokeInfo.fFrameWidth != paint.getStrokeWidth() || + fStrokeInfo.fMiterLimit != paint.getStrokeMiter() || + fStrokeInfo.fJoin != paint.getStrokeJoin())) { + return true; + } + + // Mixed blobs must be regenerated. We could probably figure out a way to do integer scrolls + // for mixed blobs if this becomes an issue. + if (this->hasBitmap() && this->hasDistanceField()) { + // Identical viewmatrices and we can reuse in all cases + if (fViewMatrix.cheapEqualTo(viewMatrix) && x == fX && y == fY) { + return false; + } + return true; + } + + if (this->hasBitmap()) { + if (fViewMatrix.getScaleX() != viewMatrix.getScaleX() || + fViewMatrix.getScaleY() != viewMatrix.getScaleY() || + fViewMatrix.getSkewX() != viewMatrix.getSkewX() || + fViewMatrix.getSkewY() != viewMatrix.getSkewY()) { + return true; + } + + // We can update the positions in the cachedtextblobs without regenerating the whole blob, + // but only for integer translations. + // This cool bit of math will determine the necessary translation to apply to the already + // generated vertex coordinates to move them to the correct position + SkScalar transX = viewMatrix.getTranslateX() + + viewMatrix.getScaleX() * (x - fX) + + viewMatrix.getSkewX() * (y - fY) - + fViewMatrix.getTranslateX(); + SkScalar transY = viewMatrix.getTranslateY() + + viewMatrix.getSkewY() * (x - fX) + + viewMatrix.getScaleY() * (y - fY) - + fViewMatrix.getTranslateY(); + if (!SkScalarIsInt(transX) || !SkScalarIsInt(transY) ) { + return true; + } + + (*outTransX) = transX; + (*outTransY) = transY; + } else if (this->hasDistanceField()) { + // A scale outside of [blob.fMaxMinScale, blob.fMinMaxScale] would result in a different + // distance field being generated, so we have to regenerate in those cases + SkScalar newMaxScale = viewMatrix.getMaxScale(); + SkScalar oldMaxScale = fViewMatrix.getMaxScale(); + SkScalar scaleAdjust = newMaxScale / oldMaxScale; + if (scaleAdjust < fMaxMinScale || scaleAdjust > fMinMaxScale) { + return true; + } + + (*outTransX) = x - fX; + (*outTransY) = y - fY; + } + + + // If we can reuse the blob, then make sure we update the blob's viewmatrix, and x/y + // offsets. Note, we offset the vertex bounds right before flushing + fViewMatrix = viewMatrix; + fX = x; + fY = y; + + // It is possible that a blob has neither distanceField nor bitmaptext. This is in the case + // when all of the runs inside the blob are drawn as paths. In this case, we always regenerate + // the blob anyways at flush time, so no need to regenerate explicitly + return false; +} + +GrDrawBatch* GrAtlasTextBlob::createBatch(const Run::SubRunInfo& info, + int glyphCount, int run, int subRun, + GrColor color, SkScalar transX, SkScalar transY, + const SkPaint& skPaint, const SkSurfaceProps& props, + const GrDistanceFieldAdjustTable* distanceAdjustTable, + GrBatchFontCache* cache) { + GrMaskFormat format = info.maskFormat(); + GrColor subRunColor; + if (kARGB_GrMaskFormat == format) { + uint8_t paintAlpha = skPaint.getAlpha(); + subRunColor = SkColorSetARGB(paintAlpha, paintAlpha, paintAlpha, paintAlpha); + } else { + subRunColor = color; + } + + GrAtlasTextBatch* batch; + if (info.drawAsDistanceFields()) { + SkColor filteredColor; + SkColorFilter* colorFilter = skPaint.getColorFilter(); + if (colorFilter) { + filteredColor = colorFilter->filterColor(skPaint.getColor()); + } else { + filteredColor = skPaint.getColor(); + } + bool useBGR = SkPixelGeometryIsBGR(props.pixelGeometry()); + batch = GrAtlasTextBatch::CreateDistanceField(glyphCount, cache, + distanceAdjustTable, filteredColor, + info.hasUseLCDText(), useBGR); + } else { + batch = GrAtlasTextBatch::CreateBitmap(format, glyphCount, cache); + } + GrAtlasTextBatch::Geometry& geometry = batch->geometry(); + geometry.fBlob = SkRef(this); + geometry.fRun = run; + geometry.fSubRun = subRun; + geometry.fColor = subRunColor; + geometry.fTransX = transX; + geometry.fTransY = transY; + batch->init(); + + return batch; +} + +inline +void GrAtlasTextBlob::flushRun(GrDrawContext* dc, GrPipelineBuilder* pipelineBuilder, + int run, GrColor color, + SkScalar transX, SkScalar transY, + const SkPaint& skPaint, const SkSurfaceProps& props, + const GrDistanceFieldAdjustTable* distanceAdjustTable, + GrBatchFontCache* cache) { + for (int subRun = 0; subRun < fRuns[run].fSubRunInfo.count(); subRun++) { + const Run::SubRunInfo& info = fRuns[run].fSubRunInfo[subRun]; + int glyphCount = info.glyphCount(); + if (0 == glyphCount) { + continue; + } + + SkAutoTUnref batch(this->createBatch(info, glyphCount, run, + subRun, color, transX, transY, + skPaint, props, + distanceAdjustTable, cache)); + dc->drawBatch(pipelineBuilder, batch); + } +} + +void GrAtlasTextBlob::flushBigGlyphs(GrContext* context, GrDrawContext* dc, + const GrClip& clip, const SkPaint& skPaint, + SkScalar transX, SkScalar transY, + const SkIRect& clipBounds) { + for (int i = 0; i < fBigGlyphs.count(); i++) { + GrAtlasTextBlob::BigGlyph& bigGlyph = fBigGlyphs[i]; + bigGlyph.fVx += transX; + bigGlyph.fVy += transY; + SkMatrix ctm; + ctm.setScale(bigGlyph.fScale, bigGlyph.fScale); + ctm.postTranslate(bigGlyph.fVx, bigGlyph.fVy); + if (bigGlyph.fApplyVM) { + ctm.postConcat(fViewMatrix); + } + + GrBlurUtils::drawPathWithMaskFilter(context, dc, clip, bigGlyph.fPath, + skPaint, ctm, nullptr, clipBounds, false); + } +} + +void GrAtlasTextBlob::flushRunAsPaths(GrContext* context, GrDrawContext* dc, + const SkSurfaceProps& props, + const SkTextBlobRunIterator& it, + const GrClip& clip, const SkPaint& skPaint, + SkDrawFilter* drawFilter, const SkMatrix& viewMatrix, + const SkIRect& clipBounds, SkScalar x, SkScalar y) { + SkPaint runPaint = skPaint; + + size_t textLen = it.glyphCount() * sizeof(uint16_t); + const SkPoint& offset = it.offset(); + + it.applyFontToPaint(&runPaint); + + if (drawFilter && !drawFilter->filter(&runPaint, SkDrawFilter::kText_Type)) { + return; + } + + runPaint.setFlags(GrTextContext::FilterTextFlags(props, runPaint)); + + switch (it.positioning()) { + case SkTextBlob::kDefault_Positioning: + GrTextUtils::DrawTextAsPath(context, dc, clip, runPaint, viewMatrix, + (const char *)it.glyphs(), + textLen, x + offset.x(), y + offset.y(), clipBounds); + break; + case SkTextBlob::kHorizontal_Positioning: + GrTextUtils::DrawPosTextAsPath(context, dc, props, clip, runPaint, viewMatrix, + (const char*)it.glyphs(), + textLen, it.pos(), 1, SkPoint::Make(x, y + offset.y()), + clipBounds); + break; + case SkTextBlob::kFull_Positioning: + GrTextUtils::DrawPosTextAsPath(context, dc, props, clip, runPaint, viewMatrix, + (const char*)it.glyphs(), + textLen, it.pos(), 2, SkPoint::Make(x, y), clipBounds); + break; + } +} + +void GrAtlasTextBlob::flushCached(GrContext* context, + GrDrawContext* dc, + const SkTextBlob* blob, + const SkSurfaceProps& props, + const GrDistanceFieldAdjustTable* distanceAdjustTable, + const SkPaint& skPaint, + const GrPaint& grPaint, + SkDrawFilter* drawFilter, + const GrClip& clip, + const SkMatrix& viewMatrix, + const SkIRect& clipBounds, + SkScalar x, SkScalar y, + SkScalar transX, SkScalar transY) { + // We loop through the runs of the blob, flushing each. If any run is too large, then we flush + // it as paths + GrPipelineBuilder pipelineBuilder(grPaint, dc->accessRenderTarget(), clip); + + GrColor color = grPaint.getColor(); + + SkTextBlobRunIterator it(blob); + for (int run = 0; !it.done(); it.next(), run++) { + if (fRuns[run].fDrawAsPaths) { + this->flushRunAsPaths(context, dc, props, it, clip, skPaint, + drawFilter, viewMatrix, clipBounds, x, y); + continue; + } + fRuns[run].fVertexBounds.offset(transX, transY); + this->flushRun(dc, &pipelineBuilder, run, color, + transX, transY, skPaint, props, + distanceAdjustTable, context->getBatchFontCache()); + } + + // Now flush big glyphs + this->flushBigGlyphs(context, dc, clip, skPaint, transX, transY, clipBounds); +} + +void GrAtlasTextBlob::flushThrowaway(GrContext* context, + GrDrawContext* dc, + const SkSurfaceProps& props, + const GrDistanceFieldAdjustTable* distanceAdjustTable, + const SkPaint& skPaint, + const GrPaint& grPaint, + const GrClip& clip, + const SkIRect& clipBounds) { + GrPipelineBuilder pipelineBuilder(grPaint, dc->accessRenderTarget(), clip); + + GrColor color = grPaint.getColor(); + for (int run = 0; run < fRunCount; run++) { + this->flushRun(dc, &pipelineBuilder, run, color, 0, 0, skPaint, props, + distanceAdjustTable, context->getBatchFontCache()); + } + + // Now flush big glyphs + this->flushBigGlyphs(context, dc, clip, skPaint, 0, 0, clipBounds); +} + + +// TODO get this code building again +#ifdef CACHE_SANITY_CHECK +void GrAtlasTextBlob::AssertEqual(const GrAtlasTextBlob& l, const GrAtlasTextBlob& r) { + SkASSERT(l.fSize == r.fSize); + SkASSERT(l.fPool == r.fPool); + + SkASSERT(l.fBlurRec.fSigma == r.fBlurRec.fSigma); + SkASSERT(l.fBlurRec.fStyle == r.fBlurRec.fStyle); + SkASSERT(l.fBlurRec.fQuality == r.fBlurRec.fQuality); + + SkASSERT(l.fStrokeInfo.fFrameWidth == r.fStrokeInfo.fFrameWidth); + SkASSERT(l.fStrokeInfo.fMiterLimit == r.fStrokeInfo.fMiterLimit); + SkASSERT(l.fStrokeInfo.fJoin == r.fStrokeInfo.fJoin); + + SkASSERT(l.fBigGlyphs.count() == r.fBigGlyphs.count()); + for (int i = 0; i < l.fBigGlyphs.count(); i++) { + const BigGlyph& lBigGlyph = l.fBigGlyphs[i]; + const BigGlyph& rBigGlyph = r.fBigGlyphs[i]; + + SkASSERT(lBigGlyph.fPath == rBigGlyph.fPath); + // We can't assert that these have the same translations + } + + SkASSERT(l.fKey == r.fKey); + SkASSERT(l.fViewMatrix.cheapEqualTo(r.fViewMatrix)); + SkASSERT(l.fPaintColor == r.fPaintColor); + SkASSERT(l.fMaxMinScale == r.fMaxMinScale); + SkASSERT(l.fMinMaxScale == r.fMinMaxScale); + SkASSERT(l.fTextType == r.fTextType); + + SkASSERT(l.fRunCount == r.fRunCount); + for (int i = 0; i < l.fRunCount; i++) { + const Run& lRun = l.fRuns[i]; + const Run& rRun = r.fRuns[i]; + + if (lRun.fStrike.get()) { + SkASSERT(rRun.fStrike.get()); + SkASSERT(GrBatchTextStrike::GetKey(*lRun.fStrike) == + GrBatchTextStrike::GetKey(*rRun.fStrike)); + + } else { + SkASSERT(!rRun.fStrike.get()); + } + + if (lRun.fTypeface.get()) { + SkASSERT(rRun.fTypeface.get()); + SkASSERT(SkTypeface::Equal(lRun.fTypeface, rRun.fTypeface)); + } else { + SkASSERT(!rRun.fTypeface.get()); + } + + // We offset bounds right before flush time so they will not be correct here + //SkASSERT(lRun.fVertexBounds == rRun.fVertexBounds); + + SkASSERT(lRun.fDescriptor.getDesc()); + SkASSERT(rRun.fDescriptor.getDesc()); + SkASSERT(lRun.fDescriptor.getDesc()->equals(*rRun.fDescriptor.getDesc())); + + if (lRun.fOverrideDescriptor.get()) { + SkASSERT(lRun.fOverrideDescriptor->getDesc()); + SkASSERT(rRun.fOverrideDescriptor.get() && rRun.fOverrideDescriptor->getDesc());; + SkASSERT(lRun.fOverrideDescriptor->getDesc()->equals( + *rRun.fOverrideDescriptor->getDesc())); + } else { + SkASSERT(!rRun.fOverrideDescriptor.get()); + } + + // color can be changed + //SkASSERT(lRun.fColor == rRun.fColor); + SkASSERT(lRun.fInitialized == rRun.fInitialized); + SkASSERT(lRun.fDrawAsPaths == rRun.fDrawAsPaths); + + SkASSERT(lRun.fSubRunInfo.count() == rRun.fSubRunInfo.count()); + for(int j = 0; j < lRun.fSubRunInfo.count(); j++) { + const Run::SubRunInfo& lSubRun = lRun.fSubRunInfo[j]; + const Run::SubRunInfo& rSubRun = rRun.fSubRunInfo[j]; + + SkASSERT(lSubRun.fVertexStartIndex == rSubRun.fVertexStartIndex); + SkASSERT(lSubRun.fVertexEndIndex == rSubRun.fVertexEndIndex); + SkASSERT(lSubRun.fGlyphStartIndex == rSubRun.fGlyphStartIndex); + SkASSERT(lSubRun.fGlyphEndIndex == rSubRun.fGlyphEndIndex); + SkASSERT(lSubRun.fTextRatio == rSubRun.fTextRatio); + SkASSERT(lSubRun.fMaskFormat == rSubRun.fMaskFormat); + SkASSERT(lSubRun.fDrawAsDistanceFields == rSubRun.fDrawAsDistanceFields); + SkASSERT(lSubRun.fUseLCDText == rSubRun.fUseLCDText); + + //We can't compare the bulk use tokens with this method + /* + SkASSERT(lSubRun.fBulkUseToken.fPlotsToUpdate.count() == + rSubRun.fBulkUseToken.fPlotsToUpdate.count()); + SkASSERT(lSubRun.fBulkUseToken.fPlotAlreadyUpdated == + rSubRun.fBulkUseToken.fPlotAlreadyUpdated); + for (int k = 0; k < lSubRun.fBulkUseToken.fPlotsToUpdate.count(); k++) { + SkASSERT(lSubRun.fBulkUseToken.fPlotsToUpdate[k] == + rSubRun.fBulkUseToken.fPlotsToUpdate[k]); + }*/ + } + } +} + +#endif diff --git a/gfx/skia/skia/src/gpu/GrAtlasTextBlob.h b/gfx/skia/skia/src/gpu/text/GrAtlasTextBlob.h similarity index 52% rename from gfx/skia/skia/src/gpu/GrAtlasTextBlob.h rename to gfx/skia/skia/src/gpu/text/GrAtlasTextBlob.h index 6ea8662700..c24dabd010 100644 --- a/gfx/skia/skia/src/gpu/GrAtlasTextBlob.h +++ b/gfx/skia/skia/src/gpu/text/GrAtlasTextBlob.h @@ -11,12 +11,18 @@ #include "GrBatchAtlas.h" #include "GrBatchFontCache.h" #include "GrColor.h" +#include "GrMemoryPool.h" #include "SkDescriptor.h" #include "SkMaskFilter.h" -#include "GrMemoryPool.h" #include "SkSurfaceProps.h" #include "SkTInternalLList.h" +struct GrDistanceFieldAdjustTable; +class GrTextContext; +class SkDrawFilter; +class SkTextBlob; +class SkTextBlobRunIterator; + // With this flag enabled, the GrAtlasTextContext will, as a sanity check, regenerate every blob // that comes in to verify the integrity of its cache //#define CACHE_SANITY_CHECK // VERY SLOW @@ -35,7 +41,8 @@ * * *WARNING* If you add new fields to this struct, then you may need to to update AssertEqual */ -struct GrAtlasTextBlob : public SkRefCnt { +class GrAtlasTextBlob : public SkNVRefCnt { +public: SK_DECLARE_INTERNAL_LLIST_INTERFACE(GrAtlasTextBlob); /* @@ -63,8 +70,7 @@ struct GrAtlasTextBlob : public SkRefCnt { */ struct Run { Run() - : fColor(GrColor_ILLEGAL) - , fInitialized(false) + : fInitialized(false) , fDrawAsPaths(false) { fVertexBounds.setLargestInverted(); // To ensure we always have one subrun, we push back a fresh run here @@ -77,7 +83,7 @@ struct GrAtlasTextBlob : public SkRefCnt { , fVertexEndIndex(0) , fGlyphStartIndex(0) , fGlyphEndIndex(0) - , fTextRatio(1.0f) + , fColor(GrColor_ILLEGAL) , fMaskFormat(kA8_GrMaskFormat) , fDrawAsDistanceFields(false) , fUseLCDText(false) {} @@ -89,18 +95,52 @@ struct GrAtlasTextBlob : public SkRefCnt { , fVertexEndIndex(that.fVertexEndIndex) , fGlyphStartIndex(that.fGlyphStartIndex) , fGlyphEndIndex(that.fGlyphEndIndex) - , fTextRatio(that.fTextRatio) + , fColor(that.fColor) , fMaskFormat(that.fMaskFormat) , fDrawAsDistanceFields(that.fDrawAsDistanceFields) , fUseLCDText(that.fUseLCDText) { } - // Distance field text cannot draw coloremoji, and so has to fall back. However, - // though the distance field text and the coloremoji may share the same run, they - // will have different descriptors. If fOverrideDescriptor is non-nullptr, then it - // will be used in place of the run's descriptor to regen texture coords - // TODO we could have a descriptor cache, it would reduce the size of these blobs - // significantly, and then the subrun could just have a refed pointer to the - // correct descriptor. + + // TODO when this object is more internal, drop the privacy + void resetBulkUseToken() { fBulkUseToken.reset(); } + GrBatchAtlas::BulkUseTokenUpdater* bulkUseToken() { return &fBulkUseToken; } + void setStrike(GrBatchTextStrike* strike) { fStrike.reset(SkRef(strike)); } + GrBatchTextStrike* strike() const { return fStrike.get(); } + + void setAtlasGeneration(uint64_t atlasGeneration) { fAtlasGeneration = atlasGeneration;} + uint64_t atlasGeneration() const { return fAtlasGeneration; } + + size_t byteCount() const { return fVertexEndIndex - fVertexStartIndex; } + size_t vertexStartIndex() const { return fVertexStartIndex; } + size_t vertexEndIndex() const { return fVertexEndIndex; } + void appendVertices(size_t vertexStride) { + fVertexEndIndex += vertexStride * kVerticesPerGlyph; + } + + uint32_t glyphCount() const { return fGlyphEndIndex - fGlyphStartIndex; } + uint32_t glyphStartIndex() const { return fGlyphStartIndex; } + uint32_t glyphEndIndex() const { return fGlyphEndIndex; } + void glyphAppended() { fGlyphEndIndex++; } + void setColor(GrColor color) { fColor = color; } + GrColor color() const { return fColor; } + void setMaskFormat(GrMaskFormat format) { fMaskFormat = format; } + GrMaskFormat maskFormat() const { return fMaskFormat; } + + void setAsSuccessor(const SubRunInfo& prev) { + fGlyphStartIndex = prev.glyphEndIndex(); + fGlyphEndIndex = prev.glyphEndIndex(); + + fVertexStartIndex = prev.vertexEndIndex(); + fVertexEndIndex = prev.vertexEndIndex(); + } + + // df properties + void setUseLCDText(bool useLCDText) { fUseLCDText = useLCDText; } + bool hasUseLCDText() const { return fUseLCDText; } + void setDrawAsDistanceFields() { fDrawAsDistanceFields = true; } + bool drawAsDistanceFields() const { return fDrawAsDistanceFields; } + + private: GrBatchAtlas::BulkUseTokenUpdater fBulkUseToken; SkAutoTUnref fStrike; uint64_t fAtlasGeneration; @@ -108,7 +148,7 @@ struct GrAtlasTextBlob : public SkRefCnt { size_t fVertexEndIndex; uint32_t fGlyphStartIndex; uint32_t fGlyphEndIndex; - SkScalar fTextRatio; // df property + GrColor fColor; GrMaskFormat fMaskFormat; bool fDrawAsDistanceFields; // df property bool fUseLCDText; // df property @@ -117,13 +157,9 @@ struct GrAtlasTextBlob : public SkRefCnt { SubRunInfo& push_back() { // Forward glyph / vertex information to seed the new sub run SubRunInfo& newSubRun = fSubRunInfo.push_back(); - SubRunInfo& prevSubRun = fSubRunInfo.fromBack(1); + const SubRunInfo& prevSubRun = fSubRunInfo.fromBack(1); - newSubRun.fGlyphStartIndex = prevSubRun.fGlyphEndIndex; - newSubRun.fGlyphEndIndex = prevSubRun.fGlyphEndIndex; - - newSubRun.fVertexStartIndex = prevSubRun.fVertexEndIndex; - newSubRun.fVertexEndIndex = prevSubRun.fVertexEndIndex; + newSubRun.setAsSuccessor(prevSubRun); return newSubRun; } static const int kMinSubRuns = 1; @@ -131,8 +167,12 @@ struct GrAtlasTextBlob : public SkRefCnt { SkRect fVertexBounds; SkSTArray fSubRunInfo; SkAutoDescriptor fDescriptor; + + // Distance field text cannot draw coloremoji, and so has to fall back. However, + // though the distance field text and the coloremoji may share the same run, they + // will have different descriptors. If fOverrideDescriptor is non-nullptr, then it + // will be used in place of the run's descriptor to regen texture coords SkAutoTDelete fOverrideDescriptor; // df properties - GrColor fColor; bool fInitialized; bool fDrawAsPaths; }; @@ -208,7 +248,7 @@ struct GrAtlasTextBlob : public SkRefCnt { , fMinMaxScale(SK_ScalarMax) , fTextType(0) {} - ~GrAtlasTextBlob() override { + ~GrAtlasTextBlob() { for (int i = 0; i < fRunCount; i++) { fRuns[i].~Run(); } @@ -241,10 +281,114 @@ struct GrAtlasTextBlob : public SkRefCnt { void setHasDistanceField() { fTextType |= kHasDistanceField_TextType; } void setHasBitmap() { fTextType |= kHasBitmap_TextType; } + void push_back_run(int currRun) { + SkASSERT(currRun < fRunCount); + if (currRun > 0) { + Run::SubRunInfo& newRun = fRuns[currRun].fSubRunInfo.back(); + Run::SubRunInfo& lastRun = fRuns[currRun - 1].fSubRunInfo.back(); + newRun.setAsSuccessor(lastRun); + } + } + + SkGlyphCache* setupCache(int runIndex, + const SkSurfaceProps& props, + const SkPaint& skPaint, + const SkMatrix* viewMatrix, + bool noGamma); + + // Appends a glyph to the blob. If the glyph is too large, the glyph will be appended + // as a path. + void appendGlyph(int runIndex, + const SkRect& positions, + GrColor color, + GrBatchTextStrike* strike, + GrGlyph* glyph, + GrFontScaler* scaler, const SkGlyph& skGlyph, + SkScalar x, SkScalar y, SkScalar scale, bool applyVM); + + static size_t GetVertexStride(GrMaskFormat maskFormat) { + switch (maskFormat) { + case kA8_GrMaskFormat: + return kGrayTextVASize; + case kARGB_GrMaskFormat: + return kColorTextVASize; + default: + return kLCDTextVASize; + } + } + + bool mustRegenerate(SkScalar* outTransX, SkScalar* outTransY, const SkPaint& paint, + GrColor color, const SkMaskFilter::BlurRec& blurRec, + const SkMatrix& viewMatrix, SkScalar x, SkScalar y); + + // flush a GrAtlasTextBlob associated with a SkTextBlob + void flushCached(GrContext* context, + GrDrawContext* dc, + const SkTextBlob* blob, + const SkSurfaceProps& props, + const GrDistanceFieldAdjustTable* distanceAdjustTable, + const SkPaint& skPaint, + const GrPaint& grPaint, + SkDrawFilter* drawFilter, + const GrClip& clip, + const SkMatrix& viewMatrix, + const SkIRect& clipBounds, + SkScalar x, SkScalar y, + SkScalar transX, SkScalar transY); + + // flush a throwaway GrAtlasTextBlob *not* associated with an SkTextBlob + void flushThrowaway(GrContext* context, + GrDrawContext* dc, + const SkSurfaceProps& props, + const GrDistanceFieldAdjustTable* distanceAdjustTable, + const SkPaint& skPaint, + const GrPaint& grPaint, + const GrClip& clip, + const SkIRect& clipBounds); + + // position + local coord + static const size_t kColorTextVASize = sizeof(SkPoint) + sizeof(SkIPoint16); + static const size_t kGrayTextVASize = sizeof(SkPoint) + sizeof(GrColor) + sizeof(SkIPoint16); + static const size_t kLCDTextVASize = kGrayTextVASize; + static const int kVerticesPerGlyph = 4; + #ifdef CACHE_SANITY_CHECK static void AssertEqual(const GrAtlasTextBlob&, const GrAtlasTextBlob&); size_t fSize; #endif + + // We'd like to inline this and make it private, but there is some test code which calls it. + // TODO refactor this + GrDrawBatch* createBatch(const Run::SubRunInfo& info, + int glyphCount, int run, int subRun, + GrColor color, SkScalar transX, SkScalar transY, + const SkPaint& skPaint, const SkSurfaceProps& props, + const GrDistanceFieldAdjustTable* distanceAdjustTable, + GrBatchFontCache* cache); + +private: + void appendLargeGlyph(GrGlyph* glyph, GrFontScaler* scaler, const SkGlyph& skGlyph, + SkScalar x, SkScalar y, SkScalar scale, bool applyVM); + + inline void flushRun(GrDrawContext* dc, GrPipelineBuilder* pipelineBuilder, + int run, GrColor color, + SkScalar transX, SkScalar transY, + const SkPaint& skPaint, const SkSurfaceProps& props, + const GrDistanceFieldAdjustTable* distanceAdjustTable, + GrBatchFontCache* cache); + + void flushBigGlyphs(GrContext* context, GrDrawContext* dc, + const GrClip& clip, const SkPaint& skPaint, + SkScalar transX, SkScalar transY, + const SkIRect& clipBounds); + + void flushRunAsPaths(GrContext* context, + GrDrawContext* dc, + const SkSurfaceProps& props, + const SkTextBlobRunIterator& it, + const GrClip& clip, const SkPaint& skPaint, + SkDrawFilter* drawFilter, const SkMatrix& viewMatrix, + const SkIRect& clipBounds, SkScalar x, SkScalar y); }; #endif diff --git a/gfx/skia/skia/src/gpu/text/GrAtlasTextContext.cpp b/gfx/skia/skia/src/gpu/text/GrAtlasTextContext.cpp new file mode 100644 index 0000000000..37cd53d080 --- /dev/null +++ b/gfx/skia/skia/src/gpu/text/GrAtlasTextContext.cpp @@ -0,0 +1,774 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#include "GrAtlasTextContext.h" + +#include "GrDrawContext.h" +#include "GrDrawTarget.h" +#include "GrFontScaler.h" +#include "GrStrokeInfo.h" +#include "GrTextBlobCache.h" +#include "GrTexturePriv.h" +#include "GrTextUtils.h" +#include "GrVertexBuffer.h" + +#include "SkAutoKern.h" +#include "SkColorPriv.h" +#include "SkColorFilter.h" +#include "SkDistanceFieldGen.h" +#include "SkDraw.h" +#include "SkDrawFilter.h" +#include "SkDrawProcs.h" +#include "SkFindAndPlaceGlyph.h" +#include "SkGlyphCache.h" +#include "SkGpuDevice.h" +#include "SkGrPriv.h" +#include "SkPath.h" +#include "SkRTConf.h" +#include "SkStrokeRec.h" +#include "SkTextBlob.h" +#include "SkTextMapStateProc.h" + +#include "batches/GrAtlasTextBatch.h" + +namespace { +static const int kMinDFFontSize = 18; +static const int kSmallDFFontSize = 32; +static const int kSmallDFFontLimit = 32; +static const int kMediumDFFontSize = 72; +static const int kMediumDFFontLimit = 72; +static const int kLargeDFFontSize = 162; +#ifdef SK_BUILD_FOR_ANDROID +static const int kLargeDFFontLimit = 384; +#else +static const int kLargeDFFontLimit = 2 * kLargeDFFontSize; +#endif +}; + +GrAtlasTextContext::GrAtlasTextContext(GrContext* context, const SkSurfaceProps& surfaceProps) + : INHERITED(context, surfaceProps) + , fDistanceAdjustTable(new GrDistanceFieldAdjustTable) { + // We overallocate vertices in our textblobs based on the assumption that A8 has the greatest + // vertexStride + static_assert(GrAtlasTextBlob::kGrayTextVASize >= GrAtlasTextBlob::kColorTextVASize && + GrAtlasTextBlob::kGrayTextVASize >= GrAtlasTextBlob::kLCDTextVASize, + "vertex_attribute_changed"); + fCurrStrike = nullptr; + fCache = context->getTextBlobCache(); +} + + +GrAtlasTextContext* GrAtlasTextContext::Create(GrContext* context, + const SkSurfaceProps& surfaceProps) { + return new GrAtlasTextContext(context, surfaceProps); +} + +bool GrAtlasTextContext::canDraw(const SkPaint& skPaint, const SkMatrix& viewMatrix) { + return this->canDrawAsDistanceFields(skPaint, viewMatrix) || + !SkDraw::ShouldDrawTextAsPaths(skPaint, viewMatrix); +} + +GrColor GrAtlasTextContext::ComputeCanonicalColor(const SkPaint& paint, bool lcd) { + GrColor canonicalColor = paint.computeLuminanceColor(); + if (lcd) { + // This is the correct computation, but there are tons of cases where LCD can be overridden. + // For now we just regenerate if any run in a textblob has LCD. + // TODO figure out where all of these overrides are and see if we can incorporate that logic + // at a higher level *OR* use sRGB + SkASSERT(false); + //canonicalColor = SkMaskGamma::CanonicalColor(canonicalColor); + } else { + // A8, though can have mixed BMP text but it shouldn't matter because BMP text won't have + // gamma corrected masks anyways, nor color + U8CPU lum = SkComputeLuminance(SkColorGetR(canonicalColor), + SkColorGetG(canonicalColor), + SkColorGetB(canonicalColor)); + // reduce to our finite number of bits + canonicalColor = SkMaskGamma::CanonicalColor(SkColorSetRGB(lum, lum, lum)); + } + return canonicalColor; +} + +// TODO if this function ever shows up in profiling, then we can compute this value when the +// textblob is being built and cache it. However, for the time being textblobs mostly only have 1 +// run so this is not a big deal to compute here. +bool GrAtlasTextContext::HasLCD(const SkTextBlob* blob) { + SkTextBlobRunIterator it(blob); + for (; !it.done(); it.next()) { + if (it.isLCD()) { + return true; + } + } + return false; +} + +void GrAtlasTextContext::drawTextBlob(GrDrawContext* dc, + const GrClip& clip, const SkPaint& skPaint, + const SkMatrix& viewMatrix, const SkTextBlob* blob, + SkScalar x, SkScalar y, + SkDrawFilter* drawFilter, const SkIRect& clipBounds) { + // If we have been abandoned, then don't draw + if (fContext->abandoned()) { + return; + } + + SkAutoTUnref cacheBlob; + SkMaskFilter::BlurRec blurRec; + GrAtlasTextBlob::Key key; + // It might be worth caching these things, but its not clear at this time + // TODO for animated mask filters, this will fill up our cache. We need a safeguard here + const SkMaskFilter* mf = skPaint.getMaskFilter(); + bool canCache = !(skPaint.getPathEffect() || + (mf && !mf->asABlur(&blurRec)) || + drawFilter); + + if (canCache) { + bool hasLCD = HasLCD(blob); + + // We canonicalize all non-lcd draws to use kUnknown_SkPixelGeometry + SkPixelGeometry pixelGeometry = hasLCD ? fSurfaceProps.pixelGeometry() : + kUnknown_SkPixelGeometry; + + // TODO we want to figure out a way to be able to use the canonical color on LCD text, + // see the note on ComputeCanonicalColor above. We pick a dummy value for LCD text to + // ensure we always match the same key + GrColor canonicalColor = hasLCD ? SK_ColorTRANSPARENT : + ComputeCanonicalColor(skPaint, hasLCD); + + key.fPixelGeometry = pixelGeometry; + key.fUniqueID = blob->uniqueID(); + key.fStyle = skPaint.getStyle(); + key.fHasBlur = SkToBool(mf); + key.fCanonicalColor = canonicalColor; + cacheBlob.reset(SkSafeRef(fCache->find(key))); + } + + SkScalar transX = 0.f; + SkScalar transY = 0.f; + + // Though for the time being runs in the textblob can override the paint, they only touch font + // info. + GrPaint grPaint; + if (!SkPaintToGrPaint(fContext, skPaint, viewMatrix, &grPaint)) { + return; + } + + if (cacheBlob) { + if (cacheBlob->mustRegenerate(&transX, &transY, skPaint, grPaint.getColor(), blurRec, + viewMatrix, x, y)) { + // We have to remake the blob because changes may invalidate our masks. + // TODO we could probably get away reuse most of the time if the pointer is unique, + // but we'd have to clear the subrun information + fCache->remove(cacheBlob); + cacheBlob.reset(SkRef(fCache->createCachedBlob(blob, key, blurRec, skPaint, + GrAtlasTextBlob::kGrayTextVASize))); + this->regenerateTextBlob(cacheBlob, skPaint, grPaint.getColor(), viewMatrix, + blob, x, y, drawFilter, clip); + } else { + fCache->makeMRU(cacheBlob); +#ifdef CACHE_SANITY_CHECK + { + int glyphCount = 0; + int runCount = 0; + GrTextBlobCache::BlobGlyphCount(&glyphCount, &runCount, blob); + SkAutoTUnref sanityBlob(fCache->createBlob(glyphCount, runCount, + kGrayTextVASize)); + GrTextBlobCache::SetupCacheBlobKey(sanityBlob, key, blurRec, skPaint); + this->regenerateTextBlob(sanityBlob, skPaint, grPaint.getColor(), viewMatrix, + blob, x, y, drawFilter, clip); + GrAtlasTextBlob::AssertEqual(*sanityBlob, *cacheBlob); + } + +#endif + } + } else { + if (canCache) { + cacheBlob.reset(SkRef(fCache->createCachedBlob(blob, key, blurRec, skPaint, + GrAtlasTextBlob::kGrayTextVASize))); + } else { + cacheBlob.reset(fCache->createBlob(blob, GrAtlasTextBlob::kGrayTextVASize)); + } + this->regenerateTextBlob(cacheBlob, skPaint, grPaint.getColor(), viewMatrix, + blob, x, y, drawFilter, clip); + } + + cacheBlob->flushCached(fContext, dc, blob, fSurfaceProps, fDistanceAdjustTable, skPaint, + grPaint, drawFilter, clip, viewMatrix, clipBounds, x, y, transX, transY); +} + +inline bool GrAtlasTextContext::canDrawAsDistanceFields(const SkPaint& skPaint, + const SkMatrix& viewMatrix) { + // TODO: support perspective (need getMaxScale replacement) + if (viewMatrix.hasPerspective()) { + return false; + } + + SkScalar maxScale = viewMatrix.getMaxScale(); + SkScalar scaledTextSize = maxScale*skPaint.getTextSize(); + // Hinted text looks far better at small resolutions + // Scaling up beyond 2x yields undesireable artifacts + if (scaledTextSize < kMinDFFontSize || scaledTextSize > kLargeDFFontLimit) { + return false; + } + + bool useDFT = fSurfaceProps.isUseDeviceIndependentFonts(); +#if SK_FORCE_DISTANCE_FIELD_TEXT + useDFT = true; +#endif + + if (!useDFT && scaledTextSize < kLargeDFFontSize) { + return false; + } + + // rasterizers and mask filters modify alpha, which doesn't + // translate well to distance + if (skPaint.getRasterizer() || skPaint.getMaskFilter() || + !fContext->caps()->shaderCaps()->shaderDerivativeSupport()) { + return false; + } + + // TODO: add some stroking support + if (skPaint.getStyle() != SkPaint::kFill_Style) { + return false; + } + + return true; +} + +void GrAtlasTextContext::regenerateTextBlob(GrAtlasTextBlob* cacheBlob, + const SkPaint& skPaint, GrColor color, + const SkMatrix& viewMatrix, + const SkTextBlob* blob, SkScalar x, SkScalar y, + SkDrawFilter* drawFilter, + const GrClip& clip) { + // The color here is the GrPaint color, and it is used to determine whether we + // have to regenerate LCD text blobs. + // We use this color vs the SkPaint color because it has the colorfilter applied. + cacheBlob->fPaintColor = color; + cacheBlob->fViewMatrix = viewMatrix; + cacheBlob->fX = x; + cacheBlob->fY = y; + + // Regenerate textblob + SkPaint runPaint = skPaint; + SkTextBlobRunIterator it(blob); + for (int run = 0; !it.done(); it.next(), run++) { + int glyphCount = it.glyphCount(); + size_t textLen = glyphCount * sizeof(uint16_t); + const SkPoint& offset = it.offset(); + // applyFontToPaint() always overwrites the exact same attributes, + // so it is safe to not re-seed the paint for this reason. + it.applyFontToPaint(&runPaint); + + if (drawFilter && !drawFilter->filter(&runPaint, SkDrawFilter::kText_Type)) { + // A false return from filter() means we should abort the current draw. + runPaint = skPaint; + continue; + } + + runPaint.setFlags(FilterTextFlags(fSurfaceProps, runPaint)); + + cacheBlob->push_back_run(run); + + if (this->canDrawAsDistanceFields(runPaint, viewMatrix)) { + cacheBlob->setHasDistanceField(); + SkPaint dfPaint = runPaint; + SkScalar textRatio; + this->initDistanceFieldPaint(cacheBlob, &dfPaint, &textRatio, viewMatrix); + Run& runIdx = cacheBlob->fRuns[run]; + PerSubRunInfo& subRun = runIdx.fSubRunInfo.back(); + subRun.setUseLCDText(runPaint.isLCDRenderText()); + subRun.setDrawAsDistanceFields(); + + switch (it.positioning()) { + case SkTextBlob::kDefault_Positioning: { + this->internalDrawDFText(cacheBlob, run, dfPaint, color, viewMatrix, + (const char *)it.glyphs(), textLen, + x + offset.x(), y + offset.y(), textRatio, runPaint); + break; + } + case SkTextBlob::kHorizontal_Positioning: { + SkPoint dfOffset = SkPoint::Make(x, y + offset.y()); + this->internalDrawDFPosText(cacheBlob, run, dfPaint, color, viewMatrix, + (const char*)it.glyphs(), textLen, it.pos(), + 1, dfOffset, textRatio, + runPaint); + break; + } + case SkTextBlob::kFull_Positioning: { + SkPoint dfOffset = SkPoint::Make(x, y); + this->internalDrawDFPosText(cacheBlob, run, dfPaint, color, viewMatrix, + (const char*)it.glyphs(), textLen, it.pos(), + 2, dfOffset, textRatio, runPaint); + break; + } + } + } else if (SkDraw::ShouldDrawTextAsPaths(runPaint, viewMatrix)) { + cacheBlob->fRuns[run].fDrawAsPaths = true; + } else { + cacheBlob->setHasBitmap(); + switch (it.positioning()) { + case SkTextBlob::kDefault_Positioning: + GrTextUtils::DrawBmpText(cacheBlob, run, fContext->getBatchFontCache(), + fSurfaceProps, runPaint, color, viewMatrix, + (const char *)it.glyphs(), textLen, + x + offset.x(), y + offset.y()); + break; + case SkTextBlob::kHorizontal_Positioning: + GrTextUtils::DrawBmpPosText(cacheBlob, run, fContext->getBatchFontCache(), + fSurfaceProps, runPaint, color, viewMatrix, + (const char*)it.glyphs(), textLen, it.pos(), 1, + SkPoint::Make(x, y + offset.y())); + break; + case SkTextBlob::kFull_Positioning: + GrTextUtils::DrawBmpPosText(cacheBlob, run, fContext->getBatchFontCache(), + fSurfaceProps, runPaint, color, viewMatrix, + (const char*)it.glyphs(), textLen, it.pos(), 2, + SkPoint::Make(x, y)); + break; + } + } + + if (drawFilter) { + // A draw filter may change the paint arbitrarily, so we must re-seed in this case. + runPaint = skPaint; + } + } +} + +inline void GrAtlasTextContext::initDistanceFieldPaint(GrAtlasTextBlob* blob, + SkPaint* skPaint, + SkScalar* textRatio, + const SkMatrix& viewMatrix) { + // getMaxScale doesn't support perspective, so neither do we at the moment + SkASSERT(!viewMatrix.hasPerspective()); + SkScalar maxScale = viewMatrix.getMaxScale(); + SkScalar textSize = skPaint->getTextSize(); + SkScalar scaledTextSize = textSize; + // if we have non-unity scale, we need to choose our base text size + // based on the SkPaint's text size multiplied by the max scale factor + // TODO: do we need to do this if we're scaling down (i.e. maxScale < 1)? + if (maxScale > 0 && !SkScalarNearlyEqual(maxScale, SK_Scalar1)) { + scaledTextSize *= maxScale; + } + + // We have three sizes of distance field text, and within each size 'bucket' there is a floor + // and ceiling. A scale outside of this range would require regenerating the distance fields + SkScalar dfMaskScaleFloor; + SkScalar dfMaskScaleCeil; + if (scaledTextSize <= kSmallDFFontLimit) { + dfMaskScaleFloor = kMinDFFontSize; + dfMaskScaleCeil = kSmallDFFontLimit; + *textRatio = textSize / kSmallDFFontSize; + skPaint->setTextSize(SkIntToScalar(kSmallDFFontSize)); + } else if (scaledTextSize <= kMediumDFFontLimit) { + dfMaskScaleFloor = kSmallDFFontLimit; + dfMaskScaleCeil = kMediumDFFontLimit; + *textRatio = textSize / kMediumDFFontSize; + skPaint->setTextSize(SkIntToScalar(kMediumDFFontSize)); + } else { + dfMaskScaleFloor = kMediumDFFontLimit; + dfMaskScaleCeil = kLargeDFFontLimit; + *textRatio = textSize / kLargeDFFontSize; + skPaint->setTextSize(SkIntToScalar(kLargeDFFontSize)); + } + + // Because there can be multiple runs in the blob, we want the overall maxMinScale, and + // minMaxScale to make regeneration decisions. Specifically, we want the maximum minimum scale + // we can tolerate before we'd drop to a lower mip size, and the minimum maximum scale we can + // tolerate before we'd have to move to a large mip size. When we actually test these values + // we look at the delta in scale between the new viewmatrix and the old viewmatrix, and test + // against these values to decide if we can reuse or not(ie, will a given scale change our mip + // level) + SkASSERT(dfMaskScaleFloor <= scaledTextSize && scaledTextSize <= dfMaskScaleCeil); + blob->fMaxMinScale = SkMaxScalar(dfMaskScaleFloor / scaledTextSize, blob->fMaxMinScale); + blob->fMinMaxScale = SkMinScalar(dfMaskScaleCeil / scaledTextSize, blob->fMinMaxScale); + + skPaint->setLCDRenderText(false); + skPaint->setAutohinted(false); + skPaint->setHinting(SkPaint::kNormal_Hinting); + skPaint->setSubpixelText(true); +} + +inline void GrAtlasTextContext::fallbackDrawPosText(GrAtlasTextBlob* blob, + int runIndex, + GrColor color, + const SkPaint& skPaint, + const SkMatrix& viewMatrix, + const SkTDArray& fallbackTxt, + const SkTDArray& fallbackPos, + int scalarsPerPosition, + const SkPoint& offset) { + SkASSERT(fallbackTxt.count()); + blob->setHasBitmap(); + Run& run = blob->fRuns[runIndex]; + // Push back a new subrun to fill and set the override descriptor + run.push_back(); + run.fOverrideDescriptor.reset(new SkAutoDescriptor); + GrTextUtils::DrawBmpPosText(blob, runIndex, fContext->getBatchFontCache(), fSurfaceProps, + skPaint, color, viewMatrix, fallbackTxt.begin(), fallbackTxt.count(), + fallbackPos.begin(), scalarsPerPosition, offset); +} + +inline GrAtlasTextBlob* +GrAtlasTextContext::setupDFBlob(int glyphCount, const SkPaint& origPaint, + const SkMatrix& viewMatrix, SkPaint* dfPaint, + SkScalar* textRatio) { + GrAtlasTextBlob* blob = fCache->createBlob(glyphCount, 1, GrAtlasTextBlob::kGrayTextVASize); + + *dfPaint = origPaint; + this->initDistanceFieldPaint(blob, dfPaint, textRatio, viewMatrix); + blob->fViewMatrix = viewMatrix; + Run& run = blob->fRuns[0]; + PerSubRunInfo& subRun = run.fSubRunInfo.back(); + subRun.setUseLCDText(origPaint.isLCDRenderText()); + subRun.setDrawAsDistanceFields(); + + return blob; +} + +inline GrAtlasTextBlob* +GrAtlasTextContext::createDrawTextBlob(const GrPaint& paint, const SkPaint& skPaint, + const SkMatrix& viewMatrix, + const char text[], size_t byteLength, + SkScalar x, SkScalar y) { + int glyphCount = skPaint.countText(text, byteLength); + + GrAtlasTextBlob* blob; + if (this->canDrawAsDistanceFields(skPaint, viewMatrix)) { + SkPaint dfPaint; + SkScalar textRatio; + blob = this->setupDFBlob(glyphCount, skPaint, viewMatrix, &dfPaint, &textRatio); + + this->internalDrawDFText(blob, 0, dfPaint, paint.getColor(), viewMatrix, text, + byteLength, x, y, textRatio, skPaint); + } else { + blob = fCache->createBlob(glyphCount, 1, GrAtlasTextBlob::kGrayTextVASize); + blob->fViewMatrix = viewMatrix; + + GrTextUtils::DrawBmpText(blob, 0, fContext->getBatchFontCache(), fSurfaceProps, skPaint, + paint.getColor(), viewMatrix, text, byteLength, x, y); + } + return blob; +} + +inline GrAtlasTextBlob* +GrAtlasTextContext::createDrawPosTextBlob(const GrPaint& paint, const SkPaint& skPaint, + const SkMatrix& viewMatrix, + const char text[], size_t byteLength, + const SkScalar pos[], int scalarsPerPosition, + const SkPoint& offset) { + int glyphCount = skPaint.countText(text, byteLength); + + GrAtlasTextBlob* blob; + if (this->canDrawAsDistanceFields(skPaint, viewMatrix)) { + SkPaint dfPaint; + SkScalar textRatio; + blob = this->setupDFBlob(glyphCount, skPaint, viewMatrix, &dfPaint, &textRatio); + + this->internalDrawDFPosText(blob, 0, dfPaint, paint.getColor(), viewMatrix, text, + byteLength, pos, scalarsPerPosition, offset, textRatio, + skPaint); + } else { + blob = fCache->createBlob(glyphCount, 1, GrAtlasTextBlob::kGrayTextVASize); + blob->fViewMatrix = viewMatrix; + GrTextUtils::DrawBmpPosText(blob, 0, fContext->getBatchFontCache(), fSurfaceProps, skPaint, + paint.getColor(), viewMatrix, text, + byteLength, pos, scalarsPerPosition, offset); + } + return blob; +} + +void GrAtlasTextContext::onDrawText(GrDrawContext* dc, + const GrClip& clip, + const GrPaint& paint, const SkPaint& skPaint, + const SkMatrix& viewMatrix, + const char text[], size_t byteLength, + SkScalar x, SkScalar y, const SkIRect& regionClipBounds) { + SkAutoTUnref blob( + this->createDrawTextBlob(paint, skPaint, viewMatrix, text, byteLength, x, y)); + blob->flushThrowaway(fContext, dc, fSurfaceProps, fDistanceAdjustTable, skPaint, paint, + clip, regionClipBounds); +} + +void GrAtlasTextContext::onDrawPosText(GrDrawContext* dc, + const GrClip& clip, + const GrPaint& paint, const SkPaint& skPaint, + const SkMatrix& viewMatrix, + const char text[], size_t byteLength, + const SkScalar pos[], int scalarsPerPosition, + const SkPoint& offset, const SkIRect& regionClipBounds) { + SkAutoTUnref blob( + this->createDrawPosTextBlob(paint, skPaint, viewMatrix, + text, byteLength, + pos, scalarsPerPosition, + offset)); + + blob->flushThrowaway(fContext, dc, fSurfaceProps, fDistanceAdjustTable, skPaint, paint, clip, + regionClipBounds); +} + +void GrAtlasTextContext::internalDrawDFText(GrAtlasTextBlob* blob, int runIndex, + const SkPaint& skPaint, GrColor color, + const SkMatrix& viewMatrix, + const char text[], size_t byteLength, + SkScalar x, SkScalar y, + SkScalar textRatio, + const SkPaint& origPaint) { + SkASSERT(byteLength == 0 || text != nullptr); + + // nothing to draw + if (text == nullptr || byteLength == 0) { + return; + } + + SkDrawCacheProc glyphCacheProc = origPaint.getDrawCacheProc(); + SkAutoDescriptor desc; + origPaint.getScalerContextDescriptor(&desc, fSurfaceProps, nullptr, true); + SkGlyphCache* origPaintCache = SkGlyphCache::DetachCache(origPaint.getTypeface(), + desc.getDesc()); + + SkTArray positions; + + const char* textPtr = text; + SkFixed stopX = 0; + SkFixed stopY = 0; + SkFixed origin = 0; + switch (origPaint.getTextAlign()) { + case SkPaint::kRight_Align: origin = SK_Fixed1; break; + case SkPaint::kCenter_Align: origin = SK_FixedHalf; break; + case SkPaint::kLeft_Align: origin = 0; break; + } + + SkAutoKern autokern; + const char* stop = text + byteLength; + while (textPtr < stop) { + // don't need x, y here, since all subpixel variants will have the + // same advance + const SkGlyph& glyph = glyphCacheProc(origPaintCache, &textPtr, 0, 0); + + SkFixed width = glyph.fAdvanceX + autokern.adjust(glyph); + positions.push_back(SkFixedToScalar(stopX + SkFixedMul(origin, width))); + + SkFixed height = glyph.fAdvanceY; + positions.push_back(SkFixedToScalar(stopY + SkFixedMul(origin, height))); + + stopX += width; + stopY += height; + } + SkASSERT(textPtr == stop); + + SkGlyphCache::AttachCache(origPaintCache); + + // now adjust starting point depending on alignment + SkScalar alignX = SkFixedToScalar(stopX); + SkScalar alignY = SkFixedToScalar(stopY); + if (origPaint.getTextAlign() == SkPaint::kCenter_Align) { + alignX = SkScalarHalf(alignX); + alignY = SkScalarHalf(alignY); + } else if (origPaint.getTextAlign() == SkPaint::kLeft_Align) { + alignX = 0; + alignY = 0; + } + x -= alignX; + y -= alignY; + SkPoint offset = SkPoint::Make(x, y); + + this->internalDrawDFPosText(blob, runIndex, skPaint, color, viewMatrix, text, byteLength, + positions.begin(), 2, offset, textRatio, origPaint); +} + +void GrAtlasTextContext::internalDrawDFPosText(GrAtlasTextBlob* blob, int runIndex, + const SkPaint& skPaint, + GrColor color, + const SkMatrix& viewMatrix, + const char text[], size_t byteLength, + const SkScalar pos[], int scalarsPerPosition, + const SkPoint& offset, + SkScalar textRatio, + const SkPaint& origPaint) { + + SkASSERT(byteLength == 0 || text != nullptr); + SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition); + + // nothing to draw + if (text == nullptr || byteLength == 0) { + return; + } + + SkTDArray fallbackTxt; + SkTDArray fallbackPos; + + fCurrStrike = nullptr; + + SkGlyphCache* cache = blob->setupCache(runIndex, fSurfaceProps, skPaint, nullptr, true); + SkDrawCacheProc glyphCacheProc = skPaint.getDrawCacheProc(); + GrFontScaler* fontScaler = GetGrFontScaler(cache); + + const char* stop = text + byteLength; + + if (SkPaint::kLeft_Align == skPaint.getTextAlign()) { + while (text < stop) { + const char* lastText = text; + // the last 2 parameters are ignored + const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0); + + if (glyph.fWidth) { + SkScalar x = offset.x() + pos[0]; + SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0); + + if (!this->dfAppendGlyph(blob, + runIndex, + glyph, + x, y, color, fontScaler, + textRatio, viewMatrix)) { + // couldn't append, send to fallback + fallbackTxt.append(SkToInt(text-lastText), lastText); + *fallbackPos.append() = pos[0]; + if (2 == scalarsPerPosition) { + *fallbackPos.append() = pos[1]; + } + } + } + pos += scalarsPerPosition; + } + } else { + SkScalar alignMul = SkPaint::kCenter_Align == skPaint.getTextAlign() ? SK_ScalarHalf + : SK_Scalar1; + while (text < stop) { + const char* lastText = text; + // the last 2 parameters are ignored + const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0); + + if (glyph.fWidth) { + SkScalar x = offset.x() + pos[0]; + SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0); + + SkScalar advanceX = SkFixedToScalar(glyph.fAdvanceX) * alignMul * textRatio; + SkScalar advanceY = SkFixedToScalar(glyph.fAdvanceY) * alignMul * textRatio; + + if (!this->dfAppendGlyph(blob, + runIndex, + glyph, + x - advanceX, y - advanceY, color, + fontScaler, + textRatio, + viewMatrix)) { + // couldn't append, send to fallback + fallbackTxt.append(SkToInt(text-lastText), lastText); + *fallbackPos.append() = pos[0]; + if (2 == scalarsPerPosition) { + *fallbackPos.append() = pos[1]; + } + } + } + pos += scalarsPerPosition; + } + } + + SkGlyphCache::AttachCache(cache); + if (fallbackTxt.count()) { + this->fallbackDrawPosText(blob, runIndex, origPaint.getColor(), origPaint, viewMatrix, + fallbackTxt, fallbackPos, scalarsPerPosition, offset); + } +} + +bool GrAtlasTextContext::dfAppendGlyph(GrAtlasTextBlob* blob, int runIndex, + const SkGlyph& skGlyph, + SkScalar sx, SkScalar sy, GrColor color, + GrFontScaler* scaler, + SkScalar textRatio, const SkMatrix& viewMatrix) { + if (!fCurrStrike) { + fCurrStrike = fContext->getBatchFontCache()->getStrike(scaler); + } + + GrGlyph::PackedID id = GrGlyph::Pack(skGlyph.getGlyphID(), + skGlyph.getSubXFixed(), + skGlyph.getSubYFixed(), + GrGlyph::kDistance_MaskStyle); + GrGlyph* glyph = fCurrStrike->getGlyph(skGlyph, id, scaler); + if (!glyph) { + return true; + } + + // fallback to color glyph support + if (kA8_GrMaskFormat != glyph->fMaskFormat) { + return false; + } + + SkScalar dx = SkIntToScalar(glyph->fBounds.fLeft + SK_DistanceFieldInset); + SkScalar dy = SkIntToScalar(glyph->fBounds.fTop + SK_DistanceFieldInset); + SkScalar width = SkIntToScalar(glyph->fBounds.width() - 2 * SK_DistanceFieldInset); + SkScalar height = SkIntToScalar(glyph->fBounds.height() - 2 * SK_DistanceFieldInset); + + SkScalar scale = textRatio; + dx *= scale; + dy *= scale; + width *= scale; + height *= scale; + sx += dx; + sy += dy; + SkRect glyphRect = SkRect::MakeXYWH(sx, sy, width, height); + + blob->appendGlyph(runIndex, glyphRect, color, fCurrStrike, glyph, scaler, skGlyph, + sx - dx, sy - dy, scale, true); + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifdef GR_TEST_UTILS + +DRAW_BATCH_TEST_DEFINE(TextBlobBatch) { + static uint32_t gContextID = SK_InvalidGenID; + static GrAtlasTextContext* gTextContext = nullptr; + static SkSurfaceProps gSurfaceProps(SkSurfaceProps::kLegacyFontHost_InitType); + + if (context->uniqueID() != gContextID) { + gContextID = context->uniqueID(); + delete gTextContext; + + // We don't yet test the fall back to paths in the GrTextContext base class. This is mostly + // because we don't really want to have a gpu device here. + // We enable distance fields by twiddling a knob on the paint + gTextContext = GrAtlasTextContext::Create(context, gSurfaceProps); + } + + // Setup dummy SkPaint / GrPaint + GrColor color = GrRandomColor(random); + SkMatrix viewMatrix = GrTest::TestMatrixInvertible(random); + SkPaint skPaint; + skPaint.setColor(color); + skPaint.setLCDRenderText(random->nextBool()); + skPaint.setAntiAlias(skPaint.isLCDRenderText() ? true : random->nextBool()); + skPaint.setSubpixelText(random->nextBool()); + + GrPaint grPaint; + if (!SkPaintToGrPaint(context, skPaint, viewMatrix, &grPaint)) { + SkFAIL("couldn't convert paint\n"); + } + + const char* text = "The quick brown fox jumps over the lazy dog."; + int textLen = (int)strlen(text); + + // Setup clip + GrClip clip; + + // right now we don't handle textblobs, nor do we handle drawPosText. Since we only + // intend to test the batch with this unit test, that is okay. + SkAutoTUnref blob( + gTextContext->createDrawTextBlob(grPaint, skPaint, viewMatrix, text, + static_cast(textLen), 0, 0)); + + SkScalar transX = static_cast(random->nextU()); + SkScalar transY = static_cast(random->nextU()); + const GrAtlasTextBlob::Run::SubRunInfo& info = blob->fRuns[0].fSubRunInfo[0]; + return blob->createBatch(info, textLen, 0, 0, color, transX, transY, skPaint, + gSurfaceProps, gTextContext->dfAdjustTable(), + context->getBatchFontCache()); +} + +#endif diff --git a/gfx/skia/skia/src/gpu/text/GrAtlasTextContext.h b/gfx/skia/skia/src/gpu/text/GrAtlasTextContext.h new file mode 100644 index 0000000000..3971352e96 --- /dev/null +++ b/gfx/skia/skia/src/gpu/text/GrAtlasTextContext.h @@ -0,0 +1,126 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef GrAtlasTextContext_DEFINED +#define GrAtlasTextContext_DEFINED + +#include "GrTextContext.h" + +#include "GrAtlasTextBlob.h" +#include "GrDistanceFieldAdjustTable.h" +#include "GrGeometryProcessor.h" +#include "SkTextBlobRunIterator.h" + +#ifdef GR_TEST_UTILS +#include "GrBatchTest.h" +#endif + +class GrDrawBatch; +class GrDrawContext; +class GrDrawTarget; +class GrPipelineBuilder; +class GrTextBlobCache; +class SkGlyph; + +/* + * This class implements GrTextContext using standard bitmap fonts, and can also process textblobs. + */ +class GrAtlasTextContext : public GrTextContext { +public: + static GrAtlasTextContext* Create(GrContext*, const SkSurfaceProps&); + +private: + GrAtlasTextContext(GrContext*, const SkSurfaceProps&); + ~GrAtlasTextContext() override {} + + bool canDraw(const SkPaint&, const SkMatrix& viewMatrix) override; + + void onDrawText(GrDrawContext*, const GrClip&, const GrPaint&, const SkPaint&, + const SkMatrix& viewMatrix, const char text[], size_t byteLength, + SkScalar x, SkScalar y, const SkIRect& regionClipBounds) override; + void onDrawPosText(GrDrawContext*, const GrClip&, const GrPaint&, + const SkPaint&, const SkMatrix& viewMatrix, + const char text[], size_t byteLength, + const SkScalar pos[], int scalarsPerPosition, + const SkPoint& offset, const SkIRect& regionClipBounds) override; + void drawTextBlob(GrDrawContext*, const GrClip&, const SkPaint&, + const SkMatrix& viewMatrix, const SkTextBlob*, SkScalar x, SkScalar y, + SkDrawFilter*, const SkIRect& clipBounds) override; + + typedef GrAtlasTextBlob::Run Run; + typedef Run::SubRunInfo PerSubRunInfo; + + inline bool canDrawAsDistanceFields(const SkPaint&, const SkMatrix& viewMatrix); + GrAtlasTextBlob* setupDFBlob(int glyphCount, const SkPaint& origPaint, + const SkMatrix& viewMatrix, SkPaint* dfPaint, + SkScalar* textRatio); + void bmpAppendGlyph(GrAtlasTextBlob*, int runIndex, const SkGlyph&, int left, int top, + GrColor color, GrFontScaler*); + bool dfAppendGlyph(GrAtlasTextBlob*, int runIndex, const SkGlyph&, SkScalar sx, SkScalar sy, + GrColor color, GrFontScaler*, SkScalar textRatio, + const SkMatrix& viewMatrix); + + // A helper for drawing BitmapText in a run of distance fields + inline void fallbackDrawPosText(GrAtlasTextBlob*, int runIndex, + GrColor color, + const SkPaint&, const SkMatrix& viewMatrix, + const SkTDArray& fallbackTxt, + const SkTDArray& fallbackPos, + int scalarsPerPosition, + const SkPoint& offset); + + void internalDrawDFText(GrAtlasTextBlob*, int runIndex, + const SkPaint&, + GrColor color, const SkMatrix& viewMatrix, + const char text[], size_t byteLength, + SkScalar x, SkScalar y, + SkScalar textRatio, const SkPaint& origPaint); + void internalDrawDFPosText(GrAtlasTextBlob*, int runIndex, + const SkPaint&, + GrColor color, const SkMatrix& viewMatrix, + const char text[], size_t byteLength, + const SkScalar pos[], int scalarsPerPosition, + const SkPoint& offset, + SkScalar textRatio, + const SkPaint& origPaint); + + // sets up the descriptor on the blob and returns a detached cache. Client must attach + inline static GrColor ComputeCanonicalColor(const SkPaint&, bool lcd); + void regenerateTextBlob(GrAtlasTextBlob* bmp, const SkPaint& skPaint, GrColor, + const SkMatrix& viewMatrix, + const SkTextBlob* blob, SkScalar x, SkScalar y, + SkDrawFilter* drawFilter, + const GrClip&); + inline static bool HasLCD(const SkTextBlob*); + inline void initDistanceFieldPaint(GrAtlasTextBlob*, SkPaint*, SkScalar* textRatio, + const SkMatrix&); + + // Test methods + // TODO this is really ugly. It'd be much nicer if positioning could be moved to batch + inline GrAtlasTextBlob* createDrawTextBlob(const GrPaint&, + const SkPaint&, const SkMatrix& viewMatrix, + const char text[], size_t byteLength, + SkScalar x, SkScalar y); + inline GrAtlasTextBlob* createDrawPosTextBlob(const GrPaint&, + const SkPaint&, const SkMatrix& viewMatrix, + const char text[], size_t byteLength, + const SkScalar pos[], int scalarsPerPosition, + const SkPoint& offset); + const GrDistanceFieldAdjustTable* dfAdjustTable() const { return fDistanceAdjustTable; } + + GrBatchTextStrike* fCurrStrike; + GrTextBlobCache* fCache; + SkAutoTUnref fDistanceAdjustTable; + +#ifdef GR_TEST_UTILS + DRAW_BATCH_TEST_FRIEND(TextBlobBatch); +#endif + + typedef GrTextContext INHERITED; +}; + +#endif diff --git a/gfx/skia/skia/src/gpu/GrBatchFontCache.cpp b/gfx/skia/skia/src/gpu/text/GrBatchFontCache.cpp similarity index 100% rename from gfx/skia/skia/src/gpu/GrBatchFontCache.cpp rename to gfx/skia/skia/src/gpu/text/GrBatchFontCache.cpp diff --git a/gfx/skia/skia/src/gpu/GrBatchFontCache.h b/gfx/skia/skia/src/gpu/text/GrBatchFontCache.h similarity index 100% rename from gfx/skia/skia/src/gpu/GrBatchFontCache.h rename to gfx/skia/skia/src/gpu/text/GrBatchFontCache.h diff --git a/gfx/skia/skia/src/gpu/text/GrDistanceFieldAdjustTable.cpp b/gfx/skia/skia/src/gpu/text/GrDistanceFieldAdjustTable.cpp new file mode 100644 index 0000000000..1c5aeceb80 --- /dev/null +++ b/gfx/skia/skia/src/gpu/text/GrDistanceFieldAdjustTable.cpp @@ -0,0 +1,93 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "GrDistanceFieldAdjustTable.h" + +#include "SkScalerContext.h" + +SkDEBUGCODE(static const int kExpectedDistanceAdjustTableSize = 8;) + +void GrDistanceFieldAdjustTable::buildDistanceAdjustTable() { + // This is used for an approximation of the mask gamma hack, used by raster and bitmap + // text. The mask gamma hack is based off of guessing what the blend color is going to + // be, and adjusting the mask so that when run through the linear blend will + // produce the value closest to the desired result. However, in practice this means + // that the 'adjusted' mask is just increasing or decreasing the coverage of + // the mask depending on what it is thought it will blit against. For black (on + // assumed white) this means that coverages are decreased (on a curve). For white (on + // assumed black) this means that coverages are increased (on a a curve). At + // middle (perceptual) gray (which could be blit against anything) the coverages + // remain the same. + // + // The idea here is that instead of determining the initial (real) coverage and + // then adjusting that coverage, we determine an adjusted coverage directly by + // essentially manipulating the geometry (in this case, the distance to the glyph + // edge). So for black (on assumed white) this thins a bit; for white (on + // assumed black) this fake bolds the geometry a bit. + // + // The distance adjustment is calculated by determining the actual coverage value which + // when fed into in the mask gamma table gives us an 'adjusted coverage' value of 0.5. This + // actual coverage value (assuming it's between 0 and 1) corresponds to a distance from the + // actual edge. So by subtracting this distance adjustment and computing without the + // the coverage adjustment we should get 0.5 coverage at the same point. + // + // This has several implications: + // For non-gray lcd smoothed text, each subpixel essentially is using a + // slightly different geometry. + // + // For black (on assumed white) this may not cover some pixels which were + // previously covered; however those pixels would have been only slightly + // covered and that slight coverage would have been decreased anyway. Also, some pixels + // which were previously fully covered may no longer be fully covered. + // + // For white (on assumed black) this may cover some pixels which weren't + // previously covered at all. + + int width, height; + size_t size; + +#ifdef SK_GAMMA_CONTRAST + SkScalar contrast = SK_GAMMA_CONTRAST; +#else + SkScalar contrast = 0.5f; +#endif + SkScalar paintGamma = SK_GAMMA_EXPONENT; + SkScalar deviceGamma = SK_GAMMA_EXPONENT; + + size = SkScalerContext::GetGammaLUTSize(contrast, paintGamma, deviceGamma, + &width, &height); + + SkASSERT(kExpectedDistanceAdjustTableSize == height); + fTable = new SkScalar[height]; + + SkAutoTArray data((int)size); + SkScalerContext::GetGammaLUTData(contrast, paintGamma, deviceGamma, data.get()); + + // find the inverse points where we cross 0.5 + // binsearch might be better, but we only need to do this once on creation + for (int row = 0; row < height; ++row) { + uint8_t* rowPtr = data.get() + row*width; + for (int col = 0; col < width - 1; ++col) { + if (rowPtr[col] <= 127 && rowPtr[col + 1] >= 128) { + // compute point where a mask value will give us a result of 0.5 + float interp = (127.5f - rowPtr[col]) / (rowPtr[col + 1] - rowPtr[col]); + float borderAlpha = (col + interp) / 255.f; + + // compute t value for that alpha + // this is an approximate inverse for smoothstep() + float t = borderAlpha*(borderAlpha*(4.0f*borderAlpha - 6.0f) + 5.0f) / 3.0f; + + // compute distance which gives us that t value + const float kDistanceFieldAAFactor = 0.65f; // should match SK_DistanceFieldAAFactor + float d = 2.0f*kDistanceFieldAAFactor*t - kDistanceFieldAAFactor; + + fTable[row] = d; + break; + } + } + } +} diff --git a/gfx/skia/skia/src/gpu/text/GrDistanceFieldAdjustTable.h b/gfx/skia/skia/src/gpu/text/GrDistanceFieldAdjustTable.h new file mode 100644 index 0000000000..f7d8bee089 --- /dev/null +++ b/gfx/skia/skia/src/gpu/text/GrDistanceFieldAdjustTable.h @@ -0,0 +1,31 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef GrDistanceFieldAdjustTable_DEFINED +#define GrDistanceFieldAdjustTable_DEFINED + +#include "SkRefCnt.h" +#include "SkScalar.h" + +// Distance field text needs this table to compute a value for use in the fragment shader. +// Because the GrAtlasTextContext can go out of scope before the final flush, this needs to be +// refcnted and malloced +struct GrDistanceFieldAdjustTable : public SkNVRefCnt { + GrDistanceFieldAdjustTable() { this->buildDistanceAdjustTable(); } + ~GrDistanceFieldAdjustTable() { delete[] fTable; } + + const SkScalar& operator[] (int i) const { + return fTable[i]; + } + +private: + void buildDistanceAdjustTable(); + + SkScalar* fTable; +}; + +#endif diff --git a/gfx/skia/skia/src/gpu/GrFontScaler.cpp b/gfx/skia/skia/src/gpu/text/GrFontScaler.cpp similarity index 100% rename from gfx/skia/skia/src/gpu/GrFontScaler.cpp rename to gfx/skia/skia/src/gpu/text/GrFontScaler.cpp diff --git a/gfx/skia/skia/src/gpu/GrFontScaler.h b/gfx/skia/skia/src/gpu/text/GrFontScaler.h similarity index 100% rename from gfx/skia/skia/src/gpu/GrFontScaler.h rename to gfx/skia/skia/src/gpu/text/GrFontScaler.h diff --git a/gfx/skia/skia/src/gpu/GrStencilAndCoverTextContext.cpp b/gfx/skia/skia/src/gpu/text/GrStencilAndCoverTextContext.cpp similarity index 90% rename from gfx/skia/skia/src/gpu/GrStencilAndCoverTextContext.cpp rename to gfx/skia/skia/src/gpu/text/GrStencilAndCoverTextContext.cpp index bab0b32ce6..d28f1a803a 100644 --- a/gfx/skia/skia/src/gpu/GrStencilAndCoverTextContext.cpp +++ b/gfx/skia/skia/src/gpu/text/GrStencilAndCoverTextContext.cpp @@ -71,7 +71,7 @@ bool GrStencilAndCoverTextContext::internalCanDraw(const SkPaint& skPaint) { return SkPaint::kStroke_Style != skPaint.getStyle() || 0 != skPaint.getStrokeWidth(); } -void GrStencilAndCoverTextContext::onDrawText(GrDrawContext* dc, GrRenderTarget* rt, +void GrStencilAndCoverTextContext::onDrawText(GrDrawContext* dc, const GrClip& clip, const GrPaint& paint, const SkPaint& skPaint, @@ -81,13 +81,13 @@ void GrStencilAndCoverTextContext::onDrawText(GrDrawContext* dc, GrRenderTarget* SkScalar x, SkScalar y, const SkIRect& clipBounds) { TextRun run(skPaint); - GrPipelineBuilder pipelineBuilder(paint, rt, clip); + GrPipelineBuilder pipelineBuilder(paint, dc->accessRenderTarget(), clip); run.setText(text, byteLength, x, y); run.draw(fContext, dc, &pipelineBuilder, paint.getColor(), viewMatrix, 0, 0, clipBounds, fFallbackTextContext, skPaint); } -void GrStencilAndCoverTextContext::onDrawPosText(GrDrawContext* dc, GrRenderTarget* rt, +void GrStencilAndCoverTextContext::onDrawPosText(GrDrawContext* dc, const GrClip& clip, const GrPaint& paint, const SkPaint& skPaint, @@ -99,27 +99,27 @@ void GrStencilAndCoverTextContext::onDrawPosText(GrDrawContext* dc, GrRenderTarg const SkPoint& offset, const SkIRect& clipBounds) { TextRun run(skPaint); - GrPipelineBuilder pipelineBuilder(paint, rt, clip); + GrPipelineBuilder pipelineBuilder(paint, dc->accessRenderTarget(), clip); run.setPosText(text, byteLength, pos, scalarsPerPosition, offset); run.draw(fContext, dc, &pipelineBuilder, paint.getColor(), viewMatrix, 0, 0, clipBounds, fFallbackTextContext, skPaint); } -void GrStencilAndCoverTextContext::drawTextBlob(GrDrawContext* dc, GrRenderTarget* rt, +void GrStencilAndCoverTextContext::drawTextBlob(GrDrawContext* dc, const GrClip& clip, const SkPaint& skPaint, const SkMatrix& viewMatrix, const SkTextBlob* skBlob, SkScalar x, SkScalar y, SkDrawFilter* drawFilter, const SkIRect& clipBounds) { if (!this->internalCanDraw(skPaint)) { - fFallbackTextContext->drawTextBlob(dc, rt, clip, skPaint, viewMatrix, skBlob, x, y, + fFallbackTextContext->drawTextBlob(dc, clip, skPaint, viewMatrix, skBlob, x, y, drawFilter, clipBounds); return; } if (drawFilter || skPaint.getPathEffect()) { // This draw can't be cached. - INHERITED::drawTextBlob(dc, rt, clip, skPaint, viewMatrix, skBlob, x, y, drawFilter, + INHERITED::drawTextBlob(dc, clip, skPaint, viewMatrix, skBlob, x, y, drawFilter, clipBounds); return; } @@ -134,7 +134,7 @@ void GrStencilAndCoverTextContext::drawTextBlob(GrDrawContext* dc, GrRenderTarge } const TextBlob& blob = this->findOrCreateTextBlob(skBlob, skPaint); - GrPipelineBuilder pipelineBuilder(paint, rt, clip); + GrPipelineBuilder pipelineBuilder(paint, dc->accessRenderTarget(), clip); TextBlob::Iter iter(blob); for (TextRun* run = iter.get(); run; run = iter.next()) { @@ -233,7 +233,7 @@ void GrStencilAndCoverTextContext::TextBlob::init(const SkTextBlob* skBlob, class GrStencilAndCoverTextContext::FallbackBlobBuilder { public: - FallbackBlobBuilder() : fBuffIdx(0) {} + FallbackBlobBuilder() : fBuffIdx(0), fCount(0) {} bool isInitialized() const { return SkToBool(fBuilder); } @@ -241,7 +241,7 @@ public: void appendGlyph(uint16_t glyphId, const SkPoint& pos); - const SkTextBlob* buildIfInitialized(); + const SkTextBlob* buildIfNeeded(int* count); private: enum { kWriteBufferSize = 1024 }; @@ -251,6 +251,7 @@ private: SkAutoTDelete fBuilder; SkPaint fFont; int fBuffIdx; + int fCount; uint16_t fGlyphIds[kWriteBufferSize]; SkPoint fPositions[kWriteBufferSize]; }; @@ -261,6 +262,7 @@ GrStencilAndCoverTextContext::TextRun::TextRun(const SkPaint& fontAndStroke) : fStroke(fontAndStroke), fFont(fontAndStroke), fTotalGlyphCount(0), + fFallbackGlyphCount(0), fDetachedGlyphCache(nullptr), fLastDrawnGlyphsID(SK_InvalidUniqueID) { SkASSERT(!fStroke.isHairlineStyle()); // Hairlines are not supported. @@ -339,9 +341,6 @@ GrStencilAndCoverTextContext::TextRun::TextRun(const SkPaint& fontAndStroke) memcpy(&builder[2 + strokeDataCount], desc, desc->getLength()); } } - - // When drawing from canonically sized paths, the actual local coords are fTextRatio * coords. - fLocalMatrixTemplate.setScale(fTextRatio, fTextRatio); } GrStencilAndCoverTextContext::TextRun::~TextRun() { @@ -355,8 +354,9 @@ void GrStencilAndCoverTextContext::TextRun::setText(const char text[], size_t by SkGlyphCache* glyphCache = this->getGlyphCache(); SkDrawCacheProc glyphCacheProc = fFont.getDrawCacheProc(); - fDraw.reset(GrPathRangeDraw::Create(GrPathRendering::kTranslate_PathTransformType, - fTotalGlyphCount = fFont.countText(text, byteLength))); + fTotalGlyphCount = fFont.countText(text, byteLength); + fInstanceData.reset(InstanceData::Alloc(GrPathRendering::kTranslate_PathTransformType, + fTotalGlyphCount)); const char* stop = text + byteLength; @@ -407,7 +407,7 @@ void GrStencilAndCoverTextContext::TextRun::setText(const char text[], size_t by fy += SkFixedMul(glyph.fAdvanceY, fixedSizeRatio); } - fFallbackTextBlob.reset(fallback.buildIfInitialized()); + fFallbackTextBlob.reset(fallback.buildIfNeeded(&fFallbackGlyphCount)); } void GrStencilAndCoverTextContext::TextRun::setPosText(const char text[], size_t byteLength, @@ -419,8 +419,9 @@ void GrStencilAndCoverTextContext::TextRun::setPosText(const char text[], size_t SkGlyphCache* glyphCache = this->getGlyphCache(); SkDrawCacheProc glyphCacheProc = fFont.getDrawCacheProc(); - fDraw.reset(GrPathRangeDraw::Create(GrPathRendering::kTranslate_PathTransformType, - fTotalGlyphCount = fFont.countText(text, byteLength))); + fTotalGlyphCount = fFont.countText(text, byteLength); + fInstanceData.reset(InstanceData::Alloc(GrPathRendering::kTranslate_PathTransformType, + fTotalGlyphCount)); const char* stop = text + byteLength; @@ -440,7 +441,7 @@ void GrStencilAndCoverTextContext::TextRun::setPosText(const char text[], size_t pos += scalarsPerPosition; } - fFallbackTextBlob.reset(fallback.buildIfInitialized()); + fFallbackTextBlob.reset(fallback.buildIfNeeded(&fFallbackGlyphCount)); } GrPathRange* GrStencilAndCoverTextContext::TextRun::createGlyphs(GrContext* ctx) const { @@ -470,8 +471,8 @@ inline void GrStencilAndCoverTextContext::TextRun::appendGlyph(const SkGlyph& gl } fallback->appendGlyph(glyph.getGlyphID(), pos); } else { - float translate[] = { fTextInverseRatio * pos.x(), fTextInverseRatio * pos.y() }; - fDraw->append(glyph.getGlyphID(), translate); + fInstanceData->append(glyph.getGlyphID(), fTextInverseRatio * pos.x(), + fTextInverseRatio * pos.y()); } } @@ -484,11 +485,10 @@ void GrStencilAndCoverTextContext::TextRun::draw(GrContext* ctx, const SkIRect& clipBounds, GrTextContext* fallbackTextContext, const SkPaint& originalSkPaint) const { - SkASSERT(fDraw); - SkASSERT(pipelineBuilder->getRenderTarget()->isStencilBufferMultisampled() || - !fFont.isAntiAlias()); + SkASSERT(fInstanceData); + SkASSERT(dc->accessRenderTarget()->isStencilBufferMultisampled() || !fFont.isAntiAlias()); - if (fDraw->count()) { + if (fInstanceData->count()) { pipelineBuilder->setState(GrPipelineBuilder::kHWAntialias_Flag, fFont.isAntiAlias()); GR_STATIC_CONST_SAME_STENCIL(kStencilPass, @@ -504,18 +504,10 @@ void GrStencilAndCoverTextContext::TextRun::draw(GrContext* ctx, SkAutoTUnref glyphs(this->createGlyphs(ctx)); if (fLastDrawnGlyphsID != glyphs->getUniqueID()) { // Either this is the first draw or the glyphs object was purged since last draw. - glyphs->loadPathsIfNeeded(fDraw->indices(), fDraw->count()); + glyphs->loadPathsIfNeeded(fInstanceData->indices(), fInstanceData->count()); fLastDrawnGlyphsID = glyphs->getUniqueID(); } - SkMatrix drawMatrix(viewMatrix); - drawMatrix.preTranslate(x, y); - drawMatrix.preScale(fTextRatio, fTextRatio); - - SkMatrix& localMatrix = fLocalMatrixTemplate; - localMatrix.setTranslateX(x); - localMatrix.setTranslateY(y); - // Don't compute a bounding box. For dst copy texture, we'll opt instead for it to just copy // the entire dst. Realistically this is a moot point, because any context that supports // NV_path_rendering will also support NV_blend_equation_advanced. @@ -524,8 +516,13 @@ void GrStencilAndCoverTextContext::TextRun::draw(GrContext* ctx, SkRect bounds = SkRect::MakeIWH(pipelineBuilder->getRenderTarget()->width(), pipelineBuilder->getRenderTarget()->height()); - dc->drawPathsFromRange(pipelineBuilder, drawMatrix, localMatrix, color, glyphs, fDraw, - GrPathRendering::kWinding_FillType, bounds); + SkAutoTUnref batch( + GrDrawPathRangeBatch::Create(viewMatrix, fTextRatio, fTextInverseRatio * x, + fTextInverseRatio * y, color, + GrPathRendering::kWinding_FillType, glyphs, fInstanceData, + bounds)); + + dc->drawPathBatch(*pipelineBuilder, batch); } if (fFallbackTextBlob) { @@ -535,8 +532,7 @@ void GrStencilAndCoverTextContext::TextRun::draw(GrContext* ctx, fallbackSkPaint.setStrokeWidth(fStroke.getWidth() * fTextRatio); } - fallbackTextContext->drawTextBlob(dc, pipelineBuilder->getRenderTarget(), - pipelineBuilder->clip(), fallbackSkPaint, viewMatrix, + fallbackTextContext->drawTextBlob(dc, pipelineBuilder->clip(), fallbackSkPaint, viewMatrix, fFallbackTextBlob, x, y, nullptr, clipBounds); } } @@ -557,11 +553,11 @@ void GrStencilAndCoverTextContext::TextRun::releaseGlyphCache() const { } size_t GrStencilAndCoverTextContext::TextRun::computeSizeInCache() const { - size_t size = sizeof(TextRun) + - fGlyphPathsKey.size() + - fTotalGlyphCount * (sizeof(uint16_t) + 2 * sizeof(float)); - if (fDraw) { - size += sizeof(GrPathRangeDraw); + size_t size = sizeof(TextRun) + fGlyphPathsKey.size(); + // The instance data always reserves enough space for every glyph. + size += (fTotalGlyphCount + fFallbackGlyphCount) * (sizeof(uint16_t) + 2 * sizeof(float)); + if (fInstanceData) { + size += sizeof(InstanceData); } if (fFallbackTextBlob) { size += sizeof(SkTextBlob); @@ -594,6 +590,7 @@ void GrStencilAndCoverTextContext::FallbackBlobBuilder::appendGlyph(uint16_t gly fGlyphIds[fBuffIdx] = glyphId; fPositions[fBuffIdx] = pos; fBuffIdx++; + fCount++; } void GrStencilAndCoverTextContext::FallbackBlobBuilder::flush() { @@ -609,10 +606,11 @@ void GrStencilAndCoverTextContext::FallbackBlobBuilder::flush() { fBuffIdx = 0; } -const SkTextBlob* GrStencilAndCoverTextContext::FallbackBlobBuilder::buildIfInitialized() { - if (!this->isInitialized()) { - return nullptr; +const SkTextBlob* GrStencilAndCoverTextContext::FallbackBlobBuilder::buildIfNeeded(int *count) { + *count = fCount; + if (fCount) { + this->flush(); + return fBuilder->build(); } - this->flush(); - return fBuilder->build(); + return nullptr; } diff --git a/gfx/skia/skia/src/gpu/GrStencilAndCoverTextContext.h b/gfx/skia/skia/src/gpu/text/GrStencilAndCoverTextContext.h similarity index 92% rename from gfx/skia/skia/src/gpu/GrStencilAndCoverTextContext.h rename to gfx/skia/skia/src/gpu/text/GrStencilAndCoverTextContext.h index af1403825a..dab71e0578 100644 --- a/gfx/skia/skia/src/gpu/GrStencilAndCoverTextContext.h +++ b/gfx/skia/skia/src/gpu/text/GrStencilAndCoverTextContext.h @@ -14,6 +14,7 @@ #include "SkTHash.h" #include "SkTInternalLList.h" #include "SkTLList.h" +#include "batches/GrDrawPathBatch.h" class GrTextStrike; class GrPath; @@ -39,17 +40,17 @@ private: bool internalCanDraw(const SkPaint&); - void onDrawText(GrDrawContext*, GrRenderTarget*, const GrClip&, const GrPaint&, const SkPaint&, + void onDrawText(GrDrawContext*, const GrClip&, const GrPaint&, const SkPaint&, const SkMatrix& viewMatrix, const char text[], size_t byteLength, SkScalar x, SkScalar y, const SkIRect& clipBounds) override; - void onDrawPosText(GrDrawContext*, GrRenderTarget*, + void onDrawPosText(GrDrawContext*, const GrClip&, const GrPaint&, const SkPaint&, const SkMatrix& viewMatrix, const char text[], size_t byteLength, const SkScalar pos[], int scalarsPerPosition, const SkPoint& offset, const SkIRect& clipBounds) override; - void drawTextBlob(GrDrawContext*, GrRenderTarget*, const GrClip&, const SkPaint&, + void drawTextBlob(GrDrawContext*, const GrClip&, const SkPaint&, const SkMatrix& viewMatrix, const SkTextBlob*, SkScalar x, SkScalar y, SkDrawFilter*, const SkIRect& clipBounds) override; @@ -74,6 +75,8 @@ private: size_t computeSizeInCache() const; private: + typedef GrDrawPathRangeBatch::InstanceData InstanceData; + SkGlyphCache* getGlyphCache() const; GrPathRange* createGlyphs(GrContext*) const; void appendGlyph(const SkGlyph&, const SkPoint&, FallbackBlobBuilder*); @@ -85,11 +88,11 @@ private: bool fUsingRawGlyphPaths; GrUniqueKey fGlyphPathsKey; int fTotalGlyphCount; - SkAutoTUnref fDraw; + SkAutoTUnref fInstanceData; + int fFallbackGlyphCount; SkAutoTUnref fFallbackTextBlob; mutable SkGlyphCache* fDetachedGlyphCache; mutable uint32_t fLastDrawnGlyphsID; - mutable SkMatrix fLocalMatrixTemplate; }; // Text blobs/caches. diff --git a/gfx/skia/skia/src/gpu/GrTextBlobCache.cpp b/gfx/skia/skia/src/gpu/text/GrTextBlobCache.cpp similarity index 94% rename from gfx/skia/skia/src/gpu/GrTextBlobCache.cpp rename to gfx/skia/skia/src/gpu/text/GrTextBlobCache.cpp index f11b7c60cb..685284bee5 100644 --- a/gfx/skia/skia/src/gpu/GrTextBlobCache.cpp +++ b/gfx/skia/skia/src/gpu/text/GrTextBlobCache.cpp @@ -55,4 +55,7 @@ void GrTextBlobCache::freeAll() { ++iter; } fCache.rewind(); + + // There should be no allocations in the memory pool at this point + SkASSERT(fPool.isEmpty()); } diff --git a/gfx/skia/skia/src/gpu/GrTextBlobCache.h b/gfx/skia/skia/src/gpu/text/GrTextBlobCache.h similarity index 100% rename from gfx/skia/skia/src/gpu/GrTextBlobCache.h rename to gfx/skia/skia/src/gpu/text/GrTextBlobCache.h diff --git a/gfx/skia/skia/src/gpu/GrTextContext.cpp b/gfx/skia/skia/src/gpu/text/GrTextContext.cpp similarity index 51% rename from gfx/skia/skia/src/gpu/GrTextContext.cpp rename to gfx/skia/skia/src/gpu/text/GrTextContext.cpp index b1bae56ed5..8ff2523a73 100644 --- a/gfx/skia/skia/src/gpu/GrTextContext.cpp +++ b/gfx/skia/skia/src/gpu/text/GrTextContext.cpp @@ -6,20 +6,14 @@ */ #include "GrTextContext.h" -#include "GrBlurUtils.h" #include "GrContext.h" -#include "GrDrawContext.h" #include "GrFontScaler.h" +#include "GrTextUtils.h" -#include "SkAutoKern.h" #include "SkDrawFilter.h" -#include "SkDrawProcs.h" #include "SkGlyphCache.h" -#include "SkGpuDevice.h" #include "SkGrPriv.h" #include "SkTextBlobRunIterator.h" -#include "SkTextMapStateProc.h" -#include "SkTextToPathIter.h" GrTextContext::GrTextContext(GrContext* context, const SkSurfaceProps& surfaceProps) : fFallbackTextContext(nullptr) @@ -31,7 +25,7 @@ GrTextContext::~GrTextContext() { delete fFallbackTextContext; } -void GrTextContext::drawText(GrDrawContext* dc, GrRenderTarget* rt, +void GrTextContext::drawText(GrDrawContext* dc, const GrClip& clip, const GrPaint& paint, const SkPaint& skPaint, const SkMatrix& viewMatrix, const char text[], size_t byteLength, @@ -43,7 +37,7 @@ void GrTextContext::drawText(GrDrawContext* dc, GrRenderTarget* rt, GrTextContext* textContext = this; do { if (textContext->canDraw(skPaint, viewMatrix)) { - textContext->onDrawText(dc, rt, clip, paint, skPaint, viewMatrix, + textContext->onDrawText(dc, clip, paint, skPaint, viewMatrix, text, byteLength, x, y, clipBounds); return; } @@ -51,10 +45,11 @@ void GrTextContext::drawText(GrDrawContext* dc, GrRenderTarget* rt, } while (textContext); // fall back to drawing as a path - this->drawTextAsPath(dc, clip, skPaint, viewMatrix, text, byteLength, x, y, clipBounds); + GrTextUtils::DrawTextAsPath(fContext, dc, clip, skPaint, viewMatrix, text, byteLength, x, y, + clipBounds); } -void GrTextContext::drawPosText(GrDrawContext* dc, GrRenderTarget* rt, +void GrTextContext::drawPosText(GrDrawContext* dc, const GrClip& clip, const GrPaint& paint, const SkPaint& skPaint, const SkMatrix& viewMatrix, const char text[], size_t byteLength, @@ -67,7 +62,7 @@ void GrTextContext::drawPosText(GrDrawContext* dc, GrRenderTarget* rt, GrTextContext* textContext = this; do { if (textContext->canDraw(skPaint, viewMatrix)) { - textContext->onDrawPosText(dc, rt, clip, paint, skPaint, viewMatrix, + textContext->onDrawPosText(dc, clip, paint, skPaint, viewMatrix, text, byteLength, pos, scalarsPerPosition, offset, clipBounds); return; @@ -76,8 +71,8 @@ void GrTextContext::drawPosText(GrDrawContext* dc, GrRenderTarget* rt, } while (textContext); // fall back to drawing as a path - this->drawPosTextAsPath(dc, clip, skPaint, viewMatrix, text, byteLength, pos, - scalarsPerPosition, offset, clipBounds); + GrTextUtils::DrawPosTextAsPath(fContext, dc, fSurfaceProps, clip, skPaint, viewMatrix, text, + byteLength, pos, scalarsPerPosition, offset, clipBounds); } bool GrTextContext::ShouldDisableLCD(const SkPaint& paint) { @@ -108,7 +103,7 @@ uint32_t GrTextContext::FilterTextFlags(const SkSurfaceProps& surfaceProps, cons return flags; } -void GrTextContext::drawTextBlob(GrDrawContext* dc, GrRenderTarget* rt, +void GrTextContext::drawTextBlob(GrDrawContext* dc, const GrClip& clip, const SkPaint& skPaint, const SkMatrix& viewMatrix, const SkTextBlob* blob, SkScalar x, SkScalar y, @@ -138,15 +133,15 @@ void GrTextContext::drawTextBlob(GrDrawContext* dc, GrRenderTarget* rt, switch (it.positioning()) { case SkTextBlob::kDefault_Positioning: - this->drawText(dc, rt, clip, grPaint, runPaint, viewMatrix, (const char *)it.glyphs(), + this->drawText(dc, clip, grPaint, runPaint, viewMatrix, (const char *)it.glyphs(), textLen, x + offset.x(), y + offset.y(), clipBounds); break; case SkTextBlob::kHorizontal_Positioning: - this->drawPosText(dc, rt, clip, grPaint, runPaint, viewMatrix, (const char*)it.glyphs(), + this->drawPosText(dc, clip, grPaint, runPaint, viewMatrix, (const char*)it.glyphs(), textLen, it.pos(), 1, SkPoint::Make(x, y + offset.y()), clipBounds); break; case SkTextBlob::kFull_Positioning: - this->drawPosText(dc, rt, clip, grPaint, runPaint, viewMatrix, (const char*)it.glyphs(), + this->drawPosText(dc, clip, grPaint, runPaint, viewMatrix, (const char*)it.glyphs(), textLen, it.pos(), 2, SkPoint::Make(x, y), clipBounds); break; default: @@ -160,105 +155,6 @@ void GrTextContext::drawTextBlob(GrDrawContext* dc, GrRenderTarget* rt, } } -void GrTextContext::drawTextAsPath(GrDrawContext* dc, - const GrClip& clip, - const SkPaint& skPaint, const SkMatrix& viewMatrix, - const char text[], size_t byteLength, SkScalar x, SkScalar y, - const SkIRect& clipBounds) { - SkTextToPathIter iter(text, byteLength, skPaint, true); - - SkMatrix matrix; - matrix.setScale(iter.getPathScale(), iter.getPathScale()); - matrix.postTranslate(x, y); - - const SkPath* iterPath; - SkScalar xpos, prevXPos = 0; - - while (iter.next(&iterPath, &xpos)) { - matrix.postTranslate(xpos - prevXPos, 0); - if (iterPath) { - const SkPaint& pnt = iter.getPaint(); - GrBlurUtils::drawPathWithMaskFilter(fContext, dc, clip, *iterPath, - pnt, viewMatrix, &matrix, clipBounds, false); - } - prevXPos = xpos; - } -} - -void GrTextContext::drawPosTextAsPath(GrDrawContext* dc, - const GrClip& clip, - const SkPaint& origPaint, const SkMatrix& viewMatrix, - const char text[], size_t byteLength, - const SkScalar pos[], int scalarsPerPosition, - const SkPoint& offset, const SkIRect& clipBounds) { - // setup our std paint, in hopes of getting hits in the cache - SkPaint paint(origPaint); - SkScalar matrixScale = paint.setupForAsPaths(); - - SkMatrix matrix; - matrix.setScale(matrixScale, matrixScale); - - // Temporarily jam in kFill, so we only ever ask for the raw outline from the cache. - paint.setStyle(SkPaint::kFill_Style); - paint.setPathEffect(nullptr); - - SkDrawCacheProc glyphCacheProc = paint.getDrawCacheProc(); - SkAutoGlyphCache autoCache(paint, &fSurfaceProps, nullptr); - SkGlyphCache* cache = autoCache.getCache(); - - const char* stop = text + byteLength; - SkTextAlignProc alignProc(paint.getTextAlign()); - SkTextMapStateProc tmsProc(SkMatrix::I(), offset, scalarsPerPosition); - - // Now restore the original settings, so we "draw" with whatever style/stroking. - paint.setStyle(origPaint.getStyle()); - paint.setPathEffect(origPaint.getPathEffect()); - - while (text < stop) { - const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0); - if (glyph.fWidth) { - const SkPath* path = cache->findPath(glyph); - if (path) { - SkPoint tmsLoc; - tmsProc(pos, &tmsLoc); - SkPoint loc; - alignProc(tmsLoc, glyph, &loc); - - matrix[SkMatrix::kMTransX] = loc.fX; - matrix[SkMatrix::kMTransY] = loc.fY; - GrBlurUtils::drawPathWithMaskFilter(fContext, dc, clip, *path, paint, - viewMatrix, &matrix, clipBounds, false); - } - } - pos += scalarsPerPosition; - } -} - -// *** change to output positions? -int GrTextContext::MeasureText(SkGlyphCache* cache, SkDrawCacheProc glyphCacheProc, - const char text[], size_t byteLength, SkVector* stopVector) { - SkFixed x = 0, y = 0; - const char* stop = text + byteLength; - - SkAutoKern autokern; - - int numGlyphs = 0; - while (text < stop) { - // don't need x, y here, since all subpixel variants will have the - // same advance - const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0); - - x += autokern.adjust(glyph) + glyph.fAdvanceX; - y += glyph.fAdvanceY; - ++numGlyphs; - } - stopVector->set(SkFixedToScalar(x), SkFixedToScalar(y)); - - SkASSERT(text == stop); - - return numGlyphs; -} - static void GlyphCacheAuxProc(void* data) { GrFontScaler* scaler = (GrFontScaler*)data; SkSafeUnref(scaler); diff --git a/gfx/skia/skia/src/gpu/GrTextContext.h b/gfx/skia/skia/src/gpu/text/GrTextContext.h similarity index 66% rename from gfx/skia/skia/src/gpu/GrTextContext.h rename to gfx/skia/skia/src/gpu/text/GrTextContext.h index f50bf8586a..206b34a403 100644 --- a/gfx/skia/skia/src/gpu/GrTextContext.h +++ b/gfx/skia/skia/src/gpu/text/GrTextContext.h @@ -28,17 +28,17 @@ class GrTextContext { public: virtual ~GrTextContext(); - void drawText(GrDrawContext* dc, GrRenderTarget* rt, + void drawText(GrDrawContext* dc, const GrClip&, const GrPaint&, const SkPaint&, const SkMatrix& viewMatrix, const char text[], size_t byteLength, SkScalar x, SkScalar y, const SkIRect& clipBounds); - void drawPosText(GrDrawContext* dc, GrRenderTarget* rt, + void drawPosText(GrDrawContext* dc, const GrClip&, const GrPaint&, const SkPaint&, const SkMatrix& viewMatrix, const char text[], size_t byteLength, const SkScalar pos[], int scalarsPerPosition, const SkPoint& offset, const SkIRect& clipBounds); - virtual void drawTextBlob(GrDrawContext* dc, GrRenderTarget*, const GrClip&, const SkPaint&, + virtual void drawTextBlob(GrDrawContext* dc, const GrClip&, const SkPaint&, const SkMatrix& viewMatrix, const SkTextBlob*, SkScalar x, SkScalar y, SkDrawFilter*, const SkIRect& clipBounds); @@ -54,34 +54,23 @@ protected: virtual bool canDraw(const SkPaint&, const SkMatrix& viewMatrix) = 0; - virtual void onDrawText(GrDrawContext*, GrRenderTarget*, const GrClip&, + virtual void onDrawText(GrDrawContext*, const GrClip&, const GrPaint&, const SkPaint&, const SkMatrix& viewMatrix, const char text[], size_t byteLength, SkScalar x, SkScalar y, const SkIRect& clipBounds) = 0; - virtual void onDrawPosText(GrDrawContext*, GrRenderTarget*, const GrClip&, + virtual void onDrawPosText(GrDrawContext*, const GrClip&, const GrPaint&, const SkPaint&, const SkMatrix& viewMatrix, const char text[], size_t byteLength, const SkScalar pos[], int scalarsPerPosition, const SkPoint& offset, const SkIRect& clipBounds) = 0; - void drawTextAsPath(GrDrawContext*, const GrClip& clip, - const SkPaint& origPaint, const SkMatrix& viewMatrix, - const char text[], size_t byteLength, SkScalar x, SkScalar y, - const SkIRect& clipBounds); - void drawPosTextAsPath(GrDrawContext*, const GrClip& clip, - const SkPaint& origPaint, const SkMatrix& viewMatrix, - const char text[], size_t byteLength, - const SkScalar pos[], int scalarsPerPosition, - const SkPoint& offset, const SkIRect& clipBounds); - static GrFontScaler* GetGrFontScaler(SkGlyphCache* cache); - // sets extent in stopVector and returns glyph count - static int MeasureText(SkGlyphCache* cache, SkDrawCacheProc glyphCacheProc, - const char text[], size_t byteLength, SkVector* stopVector); static uint32_t FilterTextFlags(const SkSurfaceProps& surfaceProps, const SkPaint& paint); friend class GrAtlasTextBatch; + friend class GrAtlasTextBlob; // for FilterTextFlags + friend class GrTextUtils; // for some static functions }; #endif diff --git a/gfx/skia/skia/src/gpu/text/GrTextUtils.cpp b/gfx/skia/skia/src/gpu/text/GrTextUtils.cpp new file mode 100644 index 0000000000..9d6bb2c857 --- /dev/null +++ b/gfx/skia/skia/src/gpu/text/GrTextUtils.cpp @@ -0,0 +1,206 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "GrTextUtils.h" + +#include "GrAtlasTextBlob.h" +#include "GrBatchFontCache.h" +#include "GrBlurUtils.h" +#include "GrContext.h" +#include "GrDrawContext.h" +#include "GrTextContext.h" +#include "SkDrawProcs.h" +#include "SkFindAndPlaceGlyph.h" +#include "SkGlyphCache.h" +#include "SkPaint.h" +#include "SkRect.h" +#include "SkTextMapStateProc.h" +#include "SkTextToPathIter.h" + +void GrTextUtils::DrawBmpText(GrAtlasTextBlob* blob, int runIndex, + GrBatchFontCache* fontCache, + const SkSurfaceProps& props, const SkPaint& skPaint, + GrColor color, + const SkMatrix& viewMatrix, + const char text[], size_t byteLength, + SkScalar x, SkScalar y) { + SkASSERT(byteLength == 0 || text != nullptr); + + // nothing to draw + if (text == nullptr || byteLength == 0) { + return; + } + + GrBatchTextStrike* currStrike = nullptr; + + // Get GrFontScaler from cache + SkGlyphCache* cache = blob->setupCache(runIndex, props, skPaint, &viewMatrix, false); + GrFontScaler* fontScaler = GrTextContext::GetGrFontScaler(cache); + + SkFindAndPlaceGlyph::ProcessText( + skPaint.getTextEncoding(), text, byteLength, + {x, y}, viewMatrix, skPaint.getTextAlign(), + cache, + [&](const SkGlyph& glyph, SkPoint position, SkPoint rounding) { + position += rounding; + BmpAppendGlyph( + blob, runIndex, fontCache, &currStrike, glyph, + SkScalarFloorToInt(position.fX), SkScalarFloorToInt(position.fY), + color, fontScaler); + } + ); + + SkGlyphCache::AttachCache(cache); +} + +void GrTextUtils::DrawBmpPosText(GrAtlasTextBlob* blob, int runIndex, + GrBatchFontCache* fontCache, + const SkSurfaceProps& props, const SkPaint& skPaint, + GrColor color, + const SkMatrix& viewMatrix, + const char text[], size_t byteLength, + const SkScalar pos[], int scalarsPerPosition, + const SkPoint& offset) { + SkASSERT(byteLength == 0 || text != nullptr); + SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition); + + // nothing to draw + if (text == nullptr || byteLength == 0) { + return; + } + + GrBatchTextStrike* currStrike = nullptr; + + // Get GrFontScaler from cache + SkGlyphCache* cache = blob->setupCache(runIndex, props, skPaint, &viewMatrix, false); + GrFontScaler* fontScaler = GrTextContext::GetGrFontScaler(cache); + + SkFindAndPlaceGlyph::ProcessPosText( + skPaint.getTextEncoding(), text, byteLength, + offset, viewMatrix, pos, scalarsPerPosition, + skPaint.getTextAlign(), cache, + [&](const SkGlyph& glyph, SkPoint position, SkPoint rounding) { + position += rounding; + BmpAppendGlyph( + blob, runIndex, fontCache, &currStrike, glyph, + SkScalarFloorToInt(position.fX), SkScalarFloorToInt(position.fY), + color, fontScaler); + } + ); + + SkGlyphCache::AttachCache(cache); +} + +void GrTextUtils::BmpAppendGlyph(GrAtlasTextBlob* blob, int runIndex, + GrBatchFontCache* fontCache, + GrBatchTextStrike** strike, const SkGlyph& skGlyph, + int vx, int vy, GrColor color, GrFontScaler* scaler) { + if (!*strike) { + *strike = fontCache->getStrike(scaler); + } + + GrGlyph::PackedID id = GrGlyph::Pack(skGlyph.getGlyphID(), + skGlyph.getSubXFixed(), + skGlyph.getSubYFixed(), + GrGlyph::kCoverage_MaskStyle); + GrGlyph* glyph = (*strike)->getGlyph(skGlyph, id, scaler); + if (!glyph) { + return; + } + + int x = vx + glyph->fBounds.fLeft; + int y = vy + glyph->fBounds.fTop; + + // keep them as ints until we've done the clip-test + int width = glyph->fBounds.width(); + int height = glyph->fBounds.height(); + + SkRect r; + r.fLeft = SkIntToScalar(x); + r.fTop = SkIntToScalar(y); + r.fRight = r.fLeft + SkIntToScalar(width); + r.fBottom = r.fTop + SkIntToScalar(height); + + blob->appendGlyph(runIndex, r, color, *strike, glyph, scaler, skGlyph, + SkIntToScalar(vx), SkIntToScalar(vy), 1.0f, false); +} + +void GrTextUtils::DrawTextAsPath(GrContext* context, GrDrawContext* dc, + const GrClip& clip, + const SkPaint& skPaint, const SkMatrix& viewMatrix, + const char text[], size_t byteLength, SkScalar x, SkScalar y, + const SkIRect& clipBounds) { + SkTextToPathIter iter(text, byteLength, skPaint, true); + + SkMatrix matrix; + matrix.setScale(iter.getPathScale(), iter.getPathScale()); + matrix.postTranslate(x, y); + + const SkPath* iterPath; + SkScalar xpos, prevXPos = 0; + + while (iter.next(&iterPath, &xpos)) { + matrix.postTranslate(xpos - prevXPos, 0); + if (iterPath) { + const SkPaint& pnt = iter.getPaint(); + GrBlurUtils::drawPathWithMaskFilter(context, dc, clip, *iterPath, + pnt, viewMatrix, &matrix, clipBounds, false); + } + prevXPos = xpos; + } +} + +void GrTextUtils::DrawPosTextAsPath(GrContext* context, + GrDrawContext* dc, + const SkSurfaceProps& props, + const GrClip& clip, + const SkPaint& origPaint, const SkMatrix& viewMatrix, + const char text[], size_t byteLength, + const SkScalar pos[], int scalarsPerPosition, + const SkPoint& offset, const SkIRect& clipBounds) { + // setup our std paint, in hopes of getting hits in the cache + SkPaint paint(origPaint); + SkScalar matrixScale = paint.setupForAsPaths(); + + SkMatrix matrix; + matrix.setScale(matrixScale, matrixScale); + + // Temporarily jam in kFill, so we only ever ask for the raw outline from the cache. + paint.setStyle(SkPaint::kFill_Style); + paint.setPathEffect(nullptr); + + SkDrawCacheProc glyphCacheProc = paint.getDrawCacheProc(); + SkAutoGlyphCache autoCache(paint, &props, nullptr); + SkGlyphCache* cache = autoCache.getCache(); + + const char* stop = text + byteLength; + SkTextAlignProc alignProc(paint.getTextAlign()); + SkTextMapStateProc tmsProc(SkMatrix::I(), offset, scalarsPerPosition); + + // Now restore the original settings, so we "draw" with whatever style/stroking. + paint.setStyle(origPaint.getStyle()); + paint.setPathEffect(origPaint.getPathEffect()); + + while (text < stop) { + const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0); + if (glyph.fWidth) { + const SkPath* path = cache->findPath(glyph); + if (path) { + SkPoint tmsLoc; + tmsProc(pos, &tmsLoc); + SkPoint loc; + alignProc(tmsLoc, glyph, &loc); + + matrix[SkMatrix::kMTransX] = loc.fX; + matrix[SkMatrix::kMTransY] = loc.fY; + GrBlurUtils::drawPathWithMaskFilter(context, dc, clip, *path, paint, + viewMatrix, &matrix, clipBounds, false); + } + } + pos += scalarsPerPosition; + } +} diff --git a/gfx/skia/skia/src/gpu/text/GrTextUtils.h b/gfx/skia/skia/src/gpu/text/GrTextUtils.h new file mode 100644 index 0000000000..0996f655a3 --- /dev/null +++ b/gfx/skia/skia/src/gpu/text/GrTextUtils.h @@ -0,0 +1,71 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef GrTextUtils_DEFINED +#define GrTextUtils_DEFINED + +#include "GrColor.h" +#include "SkScalar.h" + +class GrAtlasTextBlob; +class GrBatchFontCache; +class GrBatchTextStrike; +class GrClip; +class GrContext; +class GrDrawContext; +class GrFontScaler; +class SkGlyph; +class SkMatrix; +struct SkIRect; +class SkPaint; +struct SkPoint; +class SkGlyphCache; +class SkSurfaceProps; + +/* + * A class to house a bunch of common text utilities. This class should *ONLY* have static + * functions. It is not a namespace only because we wish to friend SkPaint + * + */ +class GrTextUtils { +public: + // Functions for appending BMP text to GrAtlasTextBlob + static void DrawBmpText(GrAtlasTextBlob*, int runIndex, + GrBatchFontCache*, const SkSurfaceProps&, + const SkPaint&, + GrColor, const SkMatrix& viewMatrix, + const char text[], size_t byteLength, + SkScalar x, SkScalar y); + + static void DrawBmpPosText(GrAtlasTextBlob*, int runIndex, + GrBatchFontCache*, const SkSurfaceProps&, const SkPaint&, + GrColor, const SkMatrix& viewMatrix, + const char text[], size_t byteLength, + const SkScalar pos[], int scalarsPerPosition, + const SkPoint& offset); + + // Functions for drawing text as paths + static void DrawTextAsPath(GrContext*, GrDrawContext*, const GrClip& clip, + const SkPaint& origPaint, const SkMatrix& viewMatrix, + const char text[], size_t byteLength, SkScalar x, SkScalar y, + const SkIRect& clipBounds); + + static void DrawPosTextAsPath(GrContext* context, + GrDrawContext* dc, + const SkSurfaceProps& props, + const GrClip& clip, + const SkPaint& origPaint, const SkMatrix& viewMatrix, + const char text[], size_t byteLength, + const SkScalar pos[], int scalarsPerPosition, + const SkPoint& offset, const SkIRect& clipBounds); +private: + static void BmpAppendGlyph(GrAtlasTextBlob*, int runIndex, GrBatchFontCache*, + GrBatchTextStrike**, const SkGlyph&, int left, int top, + GrColor color, GrFontScaler*); +}; + +#endif diff --git a/gfx/skia/skia/src/image/SkImage.cpp b/gfx/skia/skia/src/image/SkImage.cpp index be837f28a8..25370bcff1 100644 --- a/gfx/skia/skia/src/image/SkImage.cpp +++ b/gfx/skia/skia/src/image/SkImage.cpp @@ -9,6 +9,7 @@ #include "SkBitmapCache.h" #include "SkCanvas.h" #include "SkData.h" +#include "SkImageEncoder.h" #include "SkImageGenerator.h" #include "SkImagePriv.h" #include "SkImageShader.h" @@ -88,80 +89,6 @@ void SkImage::preroll(GrContext* ctx) const { } } -SkImage* SkImage::applyFilter(SkImageFilter* filter, SkIPoint* offset, - bool forceResultToOriginalSize) const { - if (!filter) { - return nullptr; - } - - SkIPoint offsetStorage; - if (!offset) { - offset = &offsetStorage; - } - return as_IB(this)->onApplyFilter(filter, offset, forceResultToOriginalSize); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// - -#include "SkImageFilter.h" -#include "SkBitmapDevice.h" - -static SkIRect compute_fast_ibounds(SkImageFilter* filter, const SkIRect& srcBounds) { - SkRect fastBounds; - fastBounds.set(srcBounds); - filter->computeFastBounds(fastBounds, &fastBounds); - return fastBounds.roundOut(); -} - -class SkRasterImageFilterProxy : public SkImageFilter::Proxy { -public: - SkBaseDevice* createDevice(int width, int height) override { - return SkBitmapDevice::Create(SkImageInfo::MakeN32Premul(width, height)); - } - - bool filterImage(const SkImageFilter*, const SkBitmap&, const SkImageFilter::Context&, - SkBitmap*, SkIPoint*) override { - return false; - } -}; - -SkImage* SkImage_Base::onApplyFilter(SkImageFilter* filter, SkIPoint* offsetResult, - bool forceResultToOriginalSize) const { - SkBitmap src; - if (!this->getROPixels(&src)) { - return nullptr; - } - - const SkIRect srcBounds = SkIRect::MakeWH(this->width(), this->height()); - - if (forceResultToOriginalSize) { - const SkIRect clipBounds = srcBounds; - SkRasterImageFilterProxy proxy; - SkImageFilter::Context ctx(SkMatrix::I(), clipBounds, SkImageFilter::Cache::Get(), - SkImageFilter::kExact_SizeConstraint); - - SkBitmap dst; - if (filter->filterImage(&proxy, src, ctx, &dst, offsetResult)) { - dst.setImmutable(); - return SkImage::NewFromBitmap(dst); - } - } else { - const SkIRect dstR = compute_fast_ibounds(filter, srcBounds); - - SkImageInfo info = SkImageInfo::MakeN32Premul(dstR.width(), dstR.height()); - SkAutoTUnref surface(this->onNewSurface(info)); - - SkPaint paint; - paint.setImageFilter(filter); - surface->getCanvas()->drawImage(this, SkIntToScalar(-dstR.x()), SkIntToScalar(-dstR.y()), - &paint); - - offsetResult->set(dstR.x(), dstR.y()); - return surface->newImageSnapshot(); - } - return nullptr; -} - /////////////////////////////////////////////////////////////////////////////////////////////////// SkShader* SkImage::newShader(SkShader::TileMode tileX, @@ -178,25 +105,14 @@ SkData* SkImage::encode(SkImageEncoder::Type type, int quality) const { return nullptr; } -namespace { - -class DefaultSerializer : public SkPixelSerializer { -protected: - bool onUseEncodedData(const void *data, size_t len) override { - return true; - } - - SkData* onEncodePixels(const SkImageInfo& info, const void* pixels, size_t rowBytes) override { - return SkImageEncoder::EncodeData(info, pixels, rowBytes, SkImageEncoder::kPNG_Type, 100); - } -}; - -} // anonymous namespace - SkData* SkImage::encode(SkPixelSerializer* serializer) const { - DefaultSerializer defaultSerializer; - SkPixelSerializer* effectiveSerializer = serializer ? serializer : &defaultSerializer; - + SkAutoTUnref defaultSerializer; + SkPixelSerializer* effectiveSerializer = serializer; + if (!effectiveSerializer) { + defaultSerializer.reset(SkImageEncoder::CreatePixelSerializer()); + SkASSERT(defaultSerializer.get()); + effectiveSerializer = defaultSerializer.get(); + } SkAutoTUnref encoded(this->refEncoded()); if (encoded && effectiveSerializer->useEncodedData(encoded->data(), encoded->size())) { return encoded.detach(); @@ -205,15 +121,15 @@ SkData* SkImage::encode(SkPixelSerializer* serializer) const { SkBitmap bm; SkAutoPixmapUnlock apu; if (as_IB(this)->getROPixels(&bm) && bm.requestLock(&apu)) { - const SkPixmap& pmap = apu.pixmap(); - return effectiveSerializer->encodePixels(pmap.info(), pmap.addr(), pmap.rowBytes()); + return effectiveSerializer->encode(apu.pixmap()); } return nullptr; } SkData* SkImage::refEncoded() const { - return as_IB(this)->onRefEncoded(); + GrContext* ctx = nullptr; // should we allow the caller to pass in a ctx? + return as_IB(this)->onRefEncoded(ctx); } SkImage* SkImage::NewFromEncoded(SkData* encoded, const SkIRect* subset) { diff --git a/gfx/skia/skia/src/image/SkImagePriv.h b/gfx/skia/skia/src/image/SkImagePriv.h index b8f177905d..7518bbe808 100644 --- a/gfx/skia/skia/src/image/SkImagePriv.h +++ b/gfx/skia/skia/src/image/SkImagePriv.h @@ -40,14 +40,6 @@ enum ForceCopyMode { }; extern SkImage* SkNewImageFromRasterBitmap(const SkBitmap&, ForceCopyMode = kNo_ForceCopyMode); -static inline size_t SkImageMinRowBytes(const SkImageInfo& info) { - size_t minRB = info.minRowBytes(); - if (kIndex_8_SkColorType != info.colorType()) { - minRB = SkAlign4(minRB); - } - return minRB; -} - // Given an image created from SkNewImageFromBitmap, return its pixelref. This // may be called to see if the surface and the image share the same pixelref, // in which case the surface may need to perform a copy-on-write. diff --git a/gfx/skia/skia/src/image/SkImage_Base.h b/gfx/skia/skia/src/image/SkImage_Base.h index c91430e5bd..83e51a58b7 100644 --- a/gfx/skia/skia/src/image/SkImage_Base.h +++ b/gfx/skia/skia/src/image/SkImage_Base.h @@ -15,6 +15,7 @@ #include class GrTextureParams; +class SkImageCacherator; enum { kNeedNewImageUniqueID = 0 @@ -32,14 +33,12 @@ public: int srcX, int srcY, CachingHint) const; virtual GrTexture* peekTexture() const { return nullptr; } + virtual SkImageCacherator* peekCacherator() const { return nullptr; } // return a read-only copy of the pixels. We promise to not modify them, // but only inspect them (or encode them). virtual bool getROPixels(SkBitmap*, CachingHint = kAllow_CachingHint) const = 0; - virtual SkImage* onApplyFilter(SkImageFilter*, SkIPoint* offset, - bool forceResultToOriginalSize) const; - virtual SkSurface* onNewSurface(const SkImageInfo& info) const { return SkSurface::NewRaster(info); } @@ -49,12 +48,19 @@ public: virtual SkImage* onNewSubset(const SkIRect&) const = 0; - virtual SkData* onRefEncoded() const { return nullptr; } + // If a ctx is specified, then only gpu-specific formats are requested. + virtual SkData* onRefEncoded(GrContext*) const { return nullptr; } virtual bool onAsLegacyBitmap(SkBitmap*, LegacyBitmapMode) const; virtual bool onIsLazyGenerated() const { return false; } + // Return a bitmap suitable for passing to image-filters + // For now, that means wrapping textures into SkGrPixelRefs... + virtual bool asBitmapForImageFilters(SkBitmap* bitmap) const { + return this->getROPixels(bitmap, kAllow_CachingHint); + } + // Call when this image is part of the key to a resourcecache entry. This allows the cache // to know automatically those entries can be purged when this SkImage deleted. void notifyAddedToCache() const { diff --git a/gfx/skia/skia/src/image/SkImage_Generator.cpp b/gfx/skia/skia/src/image/SkImage_Generator.cpp index fbde92da66..688b4ffe3b 100644 --- a/gfx/skia/skia/src/image/SkImage_Generator.cpp +++ b/gfx/skia/skia/src/image/SkImage_Generator.cpp @@ -23,7 +23,8 @@ public: bool onReadPixels(const SkImageInfo&, void*, size_t, int srcX, int srcY, CachingHint) const override; const void* onPeekPixels(SkImageInfo*, size_t* /*rowBytes*/) const override; - SkData* onRefEncoded() const override; + SkImageCacherator* peekCacherator() const override { return fCache; } + SkData* onRefEncoded(GrContext*) const override; bool isOpaque() const override { return fCache->info().isOpaque(); } SkImage* onNewSubset(const SkIRect&) const override; bool getROPixels(SkBitmap*, CachingHint) const override; @@ -65,8 +66,8 @@ const void* SkImage_Generator::onPeekPixels(SkImageInfo* infoPtr, size_t* rowByt return NULL; } -SkData* SkImage_Generator::onRefEncoded() const { - return fCache->refEncoded(); +SkData* SkImage_Generator::onRefEncoded(GrContext* ctx) const { + return fCache->refEncoded(ctx); } bool SkImage_Generator::getROPixels(SkBitmap* bitmap, CachingHint chint) const { diff --git a/gfx/skia/skia/src/image/SkImage_Gpu.cpp b/gfx/skia/skia/src/image/SkImage_Gpu.cpp index 346f7983b7..1d25635e87 100644 --- a/gfx/skia/skia/src/image/SkImage_Gpu.cpp +++ b/gfx/skia/skia/src/image/SkImage_Gpu.cpp @@ -5,16 +5,18 @@ * found in the LICENSE file. */ -#include "SkBitmapCache.h" -#include "SkImage_Gpu.h" #include "GrCaps.h" #include "GrContext.h" #include "GrDrawContext.h" -#include "GrTextureParamsAdjuster.h" +#include "GrImageIDTextureAdjuster.h" #include "effects/GrYUVtoRGBEffect.h" #include "SkCanvas.h" +#include "SkBitmapCache.h" #include "SkGpuDevice.h" +#include "SkGrPixelRef.h" #include "SkGrPriv.h" +#include "SkImageFilter.h" +#include "SkImage_Gpu.h" #include "SkPixelRef.h" SkImage_Gpu::SkImage_Gpu(int w, int h, uint32_t uniqueID, SkAlphaType at, GrTexture* tex, @@ -69,31 +71,15 @@ bool SkImage_Gpu::getROPixels(SkBitmap* dst, CachingHint chint) const { return true; } -class GpuImage_GrTextureAdjuster : public GrTextureAdjuster { -public: - GpuImage_GrTextureAdjuster(const SkImage_Gpu* image) - : INHERITED(image->peekTexture()) - , fImage(image) - {} - -protected: - void makeCopyKey(const CopyParams& params, GrUniqueKey* copyKey) override { - GrUniqueKey baseKey; - GrMakeKeyFromImageID(&baseKey, fImage->uniqueID(), - SkIRect::MakeWH(fImage->width(), fImage->height())); - MakeCopyKeyFromOrigKey(baseKey, params, copyKey); - } - - void didCacheCopy(const GrUniqueKey& copyKey) override { as_IB(fImage)->notifyAddedToCache(); } - -private: - const SkImage* fImage; - - typedef GrTextureAdjuster INHERITED; -}; +bool SkImage_Gpu::asBitmapForImageFilters(SkBitmap* bitmap) const { + bitmap->setInfo(make_info(this->width(), this->height(), this->isOpaque())); + bitmap->setPixelRef(new SkGrPixelRef(bitmap->info(), fTexture))->unref(); + bitmap->pixelRef()->setImmutableWithID(this->uniqueID()); + return true; +} GrTexture* SkImage_Gpu::asTextureRef(GrContext* ctx, const GrTextureParams& params) const { - return GpuImage_GrTextureAdjuster(this).refTextureSafeForParams(params, nullptr); + return GrImageTextureAdjuster(as_IB(this)).refTextureSafeForParams(params, nullptr); } bool SkImage_Gpu::isOpaque() const { @@ -166,85 +152,6 @@ SkImage* SkImage_Gpu::onNewSubset(const SkIRect& subset) const { /////////////////////////////////////////////////////////////////////////////////////////////////// -#include "SkGrPixelRef.h" -#include "SkImageFilter.h" - -class SkGpuImageFilterProxy : public SkImageFilter::Proxy { - GrContext* fCtx; - -public: - SkGpuImageFilterProxy(GrContext* ctx) : fCtx(ctx) {} - - SkBaseDevice* createDevice(int width, int height) override { - GrSurfaceDesc desc; - desc.fConfig = kSkia8888_GrPixelConfig; - desc.fFlags = kRenderTarget_GrSurfaceFlag; - desc.fWidth = width; - desc.fHeight = height; - desc.fSampleCnt = 0; - - SkAutoTUnref texture(fCtx->textureProvider()->createTexture(desc, true)); - - if (texture) { - SkSurfaceProps props(0, kUnknown_SkPixelGeometry); - return SkGpuDevice::Create(texture->asRenderTarget(), width, height, &props, - SkGpuDevice::kClear_InitContents); - } else { - return nullptr; - } - } - - bool filterImage(const SkImageFilter* filter, const SkBitmap& src, - const SkImageFilter::Context& ctx, SkBitmap* dst, SkIPoint* offset) override { - return filter->canFilterImageGPU() && - filter->filterImageGPU(this, src, ctx, dst, offset); - } -}; - -static SkIRect compute_fast_ibounds(SkImageFilter* filter, const SkIRect& srcBounds) { - SkRect fastBounds; - fastBounds.set(srcBounds); - filter->computeFastBounds(fastBounds, &fastBounds); - return fastBounds.roundOut(); -} - -SkImage* SkImage_Gpu::onApplyFilter(SkImageFilter* filter, SkIPoint* offsetResult, - bool forceResultToOriginalSize) const { - const SkIRect srcBounds = SkIRect::MakeWH(this->width(), this->height()); - - if (forceResultToOriginalSize) { - SkBitmap src; - GrWrapTextureInBitmap(fTexture, this->width(), this->height(), this->isOpaque(), &src); - - const SkIRect clipBounds = srcBounds; - SkGpuImageFilterProxy proxy(fTexture->getContext()); - SkAutoTUnref cache(SkGpuDevice::NewImageFilterCache()); - SkImageFilter::Context ctx(SkMatrix::I(), clipBounds, cache, SkImageFilter::kExact_SizeConstraint); - - SkBitmap dst; - if (!filter->filterImage(&proxy, src, ctx, &dst, offsetResult)) { - return nullptr; - } - return new SkImage_Gpu(dst.width(), dst.height(), kNeedNewImageUniqueID, dst.alphaType(), - dst.getTexture(), SkSurface::kNo_Budgeted); - } - - const SkIRect dstR = compute_fast_ibounds(filter, srcBounds); - - SkImageInfo info = SkImageInfo::MakeN32Premul(dstR.width(), dstR.height()); - SkAutoTUnref surface(this->onNewSurface(info)); - - SkPaint paint; - paint.setImageFilter(filter); - surface->getCanvas()->drawImage(this, SkIntToScalar(-dstR.x()), SkIntToScalar(-dstR.y()), - &paint); - - offsetResult->set(dstR.x(), dstR.y()); - return surface->newImageSnapshot(); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// - static SkImage* new_wrapped_texture_common(GrContext* ctx, const GrBackendTextureDesc& desc, SkAlphaType at, GrWrapOwnership ownership, SkImage::TextureReleaseProc releaseProc, diff --git a/gfx/skia/skia/src/image/SkImage_Gpu.h b/gfx/skia/skia/src/image/SkImage_Gpu.h index ff3b12e668..a36c8ebc13 100644 --- a/gfx/skia/skia/src/image/SkImage_Gpu.h +++ b/gfx/skia/skia/src/image/SkImage_Gpu.h @@ -43,13 +43,13 @@ public: bool isOpaque() const override; bool onReadPixels(const SkImageInfo&, void* dstPixels, size_t dstRowBytes, int srcX, int srcY, CachingHint) const override; - SkImage* onApplyFilter(SkImageFilter*, SkIPoint* offset, - bool forceResultToOriginalSize) const override; SkSurface* onNewSurface(const SkImageInfo& info) const override { return SkSurface::NewRenderTarget(fTexture->getContext(), SkSurface::kNo_Budgeted, info); } + bool asBitmapForImageFilters(SkBitmap* bitmap) const override; + private: SkAutoTUnref fTexture; const SkAlphaType fAlphaType; diff --git a/gfx/skia/skia/src/image/SkImage_Raster.cpp b/gfx/skia/skia/src/image/SkImage_Raster.cpp index 977dc01c4b..d4debdf807 100644 --- a/gfx/skia/skia/src/image/SkImage_Raster.cpp +++ b/gfx/skia/skia/src/image/SkImage_Raster.cpp @@ -48,7 +48,7 @@ public: return false; } - if (rowBytes < SkImageMinRowBytes(info)) { + if (rowBytes < info.minRowBytes()) { return false; } @@ -68,7 +68,7 @@ public: bool onReadPixels(const SkImageInfo&, void*, size_t, int srcX, int srcY, CachingHint) const override; const void* onPeekPixels(SkImageInfo*, size_t* /*rowBytes*/) const override; - SkData* onRefEncoded() const override; + SkData* onRefEncoded(GrContext*) const override; bool getROPixels(SkBitmap*, CachingHint) const override; GrTexture* asTextureRef(GrContext*, const GrTextureParams&) const override; SkImage* onNewSubset(const SkIRect&) const override; @@ -150,7 +150,7 @@ const void* SkImage_Raster::onPeekPixels(SkImageInfo* infoPtr, size_t* rowBytesP return fBitmap.getPixels(); } -SkData* SkImage_Raster::onRefEncoded() const { +SkData* SkImage_Raster::onRefEncoded(GrContext*) const { SkPixelRef* pr = fBitmap.pixelRef(); const SkImageInfo prInfo = pr->info(); const SkImageInfo bmInfo = fBitmap.info(); diff --git a/gfx/skia/skia/src/image/SkSurface_Raster.cpp b/gfx/skia/skia/src/image/SkSurface_Raster.cpp index d5593eb1d7..a606656709 100644 --- a/gfx/skia/skia/src/image/SkSurface_Raster.cpp +++ b/gfx/skia/skia/src/image/SkSurface_Raster.cpp @@ -99,10 +99,6 @@ SkSurface_Raster::SkSurface_Raster(SkPixelRef* pr, const SkSurfaceProps* props) fBitmap.setInfo(info, info.minRowBytes()); fBitmap.setPixelRef(pr); fWeOwnThePixels = true; - - if (!info.isOpaque()) { - fBitmap.eraseColor(SK_ColorTRANSPARENT); - } } SkCanvas* SkSurface_Raster::onNewCanvas() { return new SkCanvas(fBitmap, this->props()); } @@ -185,7 +181,7 @@ SkSurface* SkSurface::NewRaster(const SkImageInfo& info, const SkSurfaceProps* p return nullptr; } - SkAutoTUnref pr(SkMallocPixelRef::NewAllocate(info, 0, nullptr)); + SkAutoTUnref pr(SkMallocPixelRef::NewZeroed(info, 0, nullptr)); if (nullptr == pr.get()) { return nullptr; } diff --git a/gfx/skia/skia/src/images/SkDecodingImageGenerator.cpp b/gfx/skia/skia/src/images/SkDecodingImageGenerator.cpp index 8cfacc3fba..8d55bd36c7 100644 --- a/gfx/skia/skia/src/images/SkDecodingImageGenerator.cpp +++ b/gfx/skia/skia/src/images/SkDecodingImageGenerator.cpp @@ -37,7 +37,7 @@ public: bool ditherImage); protected: - SkData* onRefEncodedData() override; + SkData* onRefEncodedData(SK_REFENCODEDDATA_CTXPARAM) override; bool onGetPixels(const SkImageInfo& info, void* pixels, size_t rowBytes, SkPMColor ctable[], int* ctableCount) override; bool onGetYUV8Planes(SkISize sizes[3], void* planes[3], size_t rowBytes[3], @@ -126,7 +126,7 @@ DecodingImageGenerator::~DecodingImageGenerator() { SkSafeUnref(fData); } -SkData* DecodingImageGenerator::onRefEncodedData() { +SkData* DecodingImageGenerator::onRefEncodedData(SK_REFENCODEDDATA_CTXPARAM) { // This functionality is used in `gm --serialize` // Does not encode options. if (nullptr == fData) { diff --git a/gfx/skia/skia/src/images/SkImageDecoder_libgif.cpp b/gfx/skia/skia/src/images/SkImageDecoder_libgif.cpp index ef55a8f2e0..2677b13073 100644 --- a/gfx/skia/skia/src/images/SkImageDecoder_libgif.cpp +++ b/gfx/skia/skia/src/images/SkImageDecoder_libgif.cpp @@ -378,8 +378,8 @@ SkImageDecoder::Result SkGIFImageDecoder::onDecode(SkStream* sk_stream, SkBitmap SkAutoLockPixels alp(*bm); - SkAutoMalloc storage(innerWidth); - uint8_t* scanline = (uint8_t*) storage.get(); + SkAutoTMalloc storage(innerWidth); + uint8_t* scanline = storage.get(); // GIF has an option to store the scanlines of an image, plus a larger background, // filled by a fill color. In this case, we will use a subset of the larger bitmap diff --git a/gfx/skia/skia/src/images/SkImageDecoder_libico.cpp b/gfx/skia/skia/src/images/SkImageDecoder_libico.cpp index e8b4bc5c31..cb21b6906e 100644 --- a/gfx/skia/skia/src/images/SkImageDecoder_libico.cpp +++ b/gfx/skia/skia/src/images/SkImageDecoder_libico.cpp @@ -32,8 +32,8 @@ private: //read in Intel order, and return an integer #define readByte(buffer,begin) buffer[begin] -#define read2Bytes(buffer,begin) buffer[begin]+(buffer[begin+1]<<8) -#define read4Bytes(buffer,begin) buffer[begin]+(buffer[begin+1]<<8)+(buffer[begin+2]<<16)+(buffer[begin+3]<<24) +#define read2Bytes(buffer,begin) buffer[begin]+SkLeftShift(buffer[begin+1],8) +#define read4Bytes(buffer,begin) buffer[begin]+SkLeftShift(buffer[begin+1],8)+SkLeftShift(buffer[begin+2],16)+SkLeftShift(buffer[begin+3],24) ///////////////////////////////////////////////////////////////////////////////////////// diff --git a/gfx/skia/skia/src/images/SkImageDecoder_libjpeg.cpp b/gfx/skia/skia/src/images/SkImageDecoder_libjpeg.cpp index 58eff75dc9..0d02a658f2 100644 --- a/gfx/skia/skia/src/images/SkImageDecoder_libjpeg.cpp +++ b/gfx/skia/skia/src/images/SkImageDecoder_libjpeg.cpp @@ -37,13 +37,8 @@ extern "C" { // If ANDROID_RGB is defined by in the jpeg headers it indicates that jpeg offers // support for two additional formats (1) JCS_RGBA_8888 and (2) JCS_RGB_565. -#if defined(SK_DEBUG) -#define DEFAULT_FOR_SUPPRESS_JPEG_IMAGE_DECODER_WARNINGS false -#define DEFAULT_FOR_SUPPRESS_JPEG_IMAGE_DECODER_ERRORS false -#else // !defined(SK_DEBUG) #define DEFAULT_FOR_SUPPRESS_JPEG_IMAGE_DECODER_WARNINGS true #define DEFAULT_FOR_SUPPRESS_JPEG_IMAGE_DECODER_ERRORS true -#endif // defined(SK_DEBUG) SK_CONF_DECLARE(bool, c_suppressJPEGImageDecoderWarnings, "images.jpeg.suppressDecoderWarnings", DEFAULT_FOR_SUPPRESS_JPEG_IMAGE_DECODER_WARNINGS, @@ -501,8 +496,8 @@ SkImageDecoder::Result SkJPEGImageDecoder::onDecode(SkStream* stream, SkBitmap* return return_failure(cinfo, *bm, "sampler.begin"); } - SkAutoMalloc srcStorage(cinfo.output_width * srcBytesPerPixel); - uint8_t* srcRow = (uint8_t*)srcStorage.get(); + SkAutoTMalloc srcStorage(cinfo.output_width * srcBytesPerPixel); + uint8_t* srcRow = srcStorage.get(); // Possibly skip initial rows [sampler.srcY0] if (!skip_src_rows(&cinfo, srcRow, sampler.srcY0())) { @@ -936,7 +931,7 @@ protected: skjpeg_destination_mgr sk_wstream(stream); // allocate these before set call setjmp - SkAutoMalloc oneRow; + SkAutoTMalloc oneRow; cinfo.err = jpeg_std_error(&sk_err); sk_err.error_exit = skjpeg_error_exit; @@ -971,7 +966,7 @@ protected: jpeg_start_compress(&cinfo, TRUE); const int width = bm.width(); - uint8_t* oneRowP = (uint8_t*)oneRow.reset(width * 3); + uint8_t* oneRowP = oneRow.reset(width * 3); const SkPMColor* colors = bm.getColorTable() ? bm.getColorTable()->readColors() : nullptr; const void* srcRow = bm.getPixels(); diff --git a/gfx/skia/skia/src/images/SkImageDecoder_libpng.cpp b/gfx/skia/skia/src/images/SkImageDecoder_libpng.cpp index a03ed10453..cd8152a36b 100644 --- a/gfx/skia/skia/src/images/SkImageDecoder_libpng.cpp +++ b/gfx/skia/skia/src/images/SkImageDecoder_libpng.cpp @@ -37,11 +37,7 @@ #define png_flush_ptr_NULL nullptr #endif -#if defined(SK_DEBUG) -#define DEFAULT_FOR_SUPPRESS_PNG_IMAGE_DECODER_WARNINGS false -#else // !defined(SK_DEBUG) #define DEFAULT_FOR_SUPPRESS_PNG_IMAGE_DECODER_WARNINGS true -#endif // defined(SK_DEBUG) SK_CONF_DECLARE(bool, c_suppressPNGImageDecoderWarnings, "images.png.suppressDecoderWarnings", DEFAULT_FOR_SUPPRESS_PNG_IMAGE_DECODER_WARNINGS, @@ -132,7 +128,9 @@ static int sk_read_user_chunk(png_structp png_ptr, png_unknown_chunkp chunk) { #endif static void sk_error_fn(png_structp png_ptr, png_const_charp msg) { - SkDEBUGF(("------ png error %s\n", msg)); + if (!c_suppressPNGImageDecoderWarnings) { + SkDEBUGF(("------ png error %s\n", msg)); + } longjmp(png_jmpbuf(png_ptr), 1); } @@ -403,8 +401,8 @@ SkImageDecoder::Result SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap const int height = decodedBitmap->height(); if (number_passes > 1) { - SkAutoMalloc storage(origWidth * origHeight * srcBytesPerPixel); - uint8_t* base = (uint8_t*)storage.get(); + SkAutoTMalloc storage(origWidth * origHeight * srcBytesPerPixel); + uint8_t* base = storage.get(); size_t rowBytes = origWidth * srcBytesPerPixel; for (int i = 0; i < number_passes; i++) { @@ -422,8 +420,8 @@ SkImageDecoder::Result SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap base += sampler.srcDY() * rowBytes; } } else { - SkAutoMalloc storage(origWidth * srcBytesPerPixel); - uint8_t* srcRow = (uint8_t*)storage.get(); + SkAutoTMalloc storage(origWidth * srcBytesPerPixel); + uint8_t* srcRow = storage.get(); skip_src_rows(png_ptr, srcRow, sampler.srcY0()); for (int y = 0; y < height; y++) { @@ -827,10 +825,28 @@ private: typedef SkImageEncoder INHERITED; }; -bool SkPNGImageEncoder::onEncode(SkWStream* stream, const SkBitmap& bitmap, int /*quality*/) { - SkColorType ct = bitmap.colorType(); +bool SkPNGImageEncoder::onEncode(SkWStream* stream, + const SkBitmap& originalBitmap, + int /*quality*/) { + SkBitmap copy; + const SkBitmap* bitmap = &originalBitmap; + switch (originalBitmap.colorType()) { + case kIndex_8_SkColorType: + case kN32_SkColorType: + case kARGB_4444_SkColorType: + case kRGB_565_SkColorType: + break; + default: + // TODO(scroggo): support 8888-but-not-N32 natively. + // TODO(scroggo): support kGray_8 directly. + // TODO(scroggo): support Alpha_8 as Grayscale(black)+Alpha + if (originalBitmap.copyTo(©, kN32_SkColorType)) { + bitmap = © + } + } + SkColorType ct = bitmap->colorType(); - const bool hasAlpha = !bitmap.isOpaque(); + const bool hasAlpha = !bitmap->isOpaque(); int colorType = PNG_COLOR_MASK_COLOR; int bitDepth = 8; // default for color png_color_8 sig_bit; @@ -870,14 +886,14 @@ bool SkPNGImageEncoder::onEncode(SkWStream* stream, const SkBitmap& bitmap, int sig_bit.alpha = 0; } - SkAutoLockPixels alp(bitmap); + SkAutoLockPixels alp(*bitmap); // readyToDraw checks for pixels (and colortable if that is required) - if (!bitmap.readyToDraw()) { + if (!bitmap->readyToDraw()) { return false; } // we must do this after we have locked the pixels - SkColorTable* ctable = bitmap.getColorTable(); + SkColorTable* ctable = bitmap->getColorTable(); if (ctable) { if (ctable->count() == 0) { return false; @@ -886,7 +902,7 @@ bool SkPNGImageEncoder::onEncode(SkWStream* stream, const SkBitmap& bitmap, int bitDepth = computeBitDepth(ctable->count()); } - return doEncode(stream, bitmap, hasAlpha, colorType, bitDepth, ct, sig_bit); + return doEncode(stream, *bitmap, hasAlpha, colorType, bitDepth, ct, sig_bit); } bool SkPNGImageEncoder::doEncode(SkWStream* stream, const SkBitmap& bitmap, @@ -950,8 +966,8 @@ bool SkPNGImageEncoder::doEncode(SkWStream* stream, const SkBitmap& bitmap, png_write_info(png_ptr, info_ptr); const char* srcImage = (const char*)bitmap.getPixels(); - SkAutoSMalloc<1024> rowStorage(bitmap.width() << 2); - char* storage = (char*)rowStorage.get(); + SkAutoSTMalloc<1024, char> rowStorage(bitmap.width() << 2); + char* storage = rowStorage.get(); transform_scanline_proc proc = choose_proc(ct, hasAlpha); for (int y = 0; y < bitmap.height(); y++) { diff --git a/gfx/skia/skia/src/images/SkImageDecoder_libwebp.cpp b/gfx/skia/skia/src/images/SkImageDecoder_libwebp.cpp index 07ff83de9b..52535779fa 100644 --- a/gfx/skia/skia/src/images/SkImageDecoder_libwebp.cpp +++ b/gfx/skia/skia/src/images/SkImageDecoder_libwebp.cpp @@ -201,8 +201,8 @@ static bool webp_idecode(SkStream* stream, WebPDecoderConfig* config) { } const size_t readBufferSize = stream->hasLength() ? SkTMin(stream->getLength(), WEBP_IDECODE_BUFFER_SZ) : WEBP_IDECODE_BUFFER_SZ; - SkAutoMalloc srcStorage(readBufferSize); - unsigned char* input = (uint8_t*)srcStorage.get(); + SkAutoTMalloc srcStorage(readBufferSize); + unsigned char* input = srcStorage.get(); if (nullptr == input) { WebPIDelete(idec); WebPFreeDecBuffer(&config->output); diff --git a/gfx/skia/skia/src/images/SkImageEncoder.cpp b/gfx/skia/skia/src/images/SkImageEncoder.cpp index cc1b73baa5..22bbc57c2f 100644 --- a/gfx/skia/skia/src/images/SkImageEncoder.cpp +++ b/gfx/skia/skia/src/images/SkImageEncoder.cpp @@ -7,6 +7,8 @@ #include "SkImageEncoder.h" #include "SkBitmap.h" +#include "SkPixelSerializer.h" +#include "SkPixmap.h" #include "SkStream.h" #include "SkTemplates.h" @@ -57,6 +59,30 @@ SkData* SkImageEncoder::EncodeData(const SkImageInfo& info, const void* pixels, if (!bm.installPixels(info, const_cast(pixels), rowBytes)) { return nullptr; } - SkAutoTDelete enc(SkImageEncoder::Create(t)); - return enc.get() ? enc.get()->encodeData(bm, quality) : nullptr; + bm.setImmutable(); + return SkImageEncoder::EncodeData(bm, t, quality); +} + +SkData* SkImageEncoder::EncodeData(const SkPixmap& pixmap, + Type t, int quality) { + SkBitmap bm; + if (!bm.installPixels(pixmap)) { + return nullptr; + } + bm.setImmutable(); + return SkImageEncoder::EncodeData(bm, t, quality); +} + +namespace { +class ImageEncoderPixelSerializer final : public SkPixelSerializer { +protected: + bool onUseEncodedData(const void*, size_t) override { return true; } + SkData* onEncode(const SkPixmap& pmap) override { + return SkImageEncoder::EncodeData(pmap, SkImageEncoder::kPNG_Type, 100); + } +}; +} // namespace + +SkPixelSerializer* SkImageEncoder::CreatePixelSerializer() { + return new ImageEncoderPixelSerializer; } diff --git a/gfx/skia/skia/src/opts/Sk4px_NEON.h b/gfx/skia/skia/src/opts/Sk4px_NEON.h index c27bb13764..62f1deb4ac 100644 --- a/gfx/skia/skia/src/opts/Sk4px_NEON.h +++ b/gfx/skia/skia/src/opts/Sk4px_NEON.h @@ -58,9 +58,9 @@ inline Sk4px Sk4px::Wide::addNarrowHi(const Sk16h& other) const { } inline Sk4px Sk4px::Wide::div255() const { - // Calculated as ((x+128) + ((x+128)>>8)) >> 8. - auto v = *this + Sk16h(128); - return v.addNarrowHi(v>>8); + // Calculated as (x + (x+128)>>8 +128) >> 8. The 'r' in each instruction provides each +128. + return Sk16b(vcombine_u8(vraddhn_u16(this->fLo.fVec, vrshrq_n_u16(this->fLo.fVec, 8)), + vraddhn_u16(this->fHi.fVec, vrshrq_n_u16(this->fHi.fVec, 8)))); } inline Sk4px Sk4px::alphas() const { @@ -95,76 +95,5 @@ inline Sk4px Sk4px::zeroAlphas() const { return Sk16b(vbicq_u8(this->fVec, (uint8x16_t)vdupq_n_u32(0xFF << SK_A32_SHIFT))); } -static inline uint8x16_t widen_to_8888(uint16x4_t v) { - // RGB565 format: |R....|G.....|B....| - // Bit: 16 11 5 0 - - // First get each pixel into its own 32-bit lane. - // v == rgb3 rgb2 rgb1 rgb0 - // spread == 0000 rgb3 0000 rgb2 0000 rgb1 0000 rgb0 - uint32x4_t spread = vmovl_u16(v); - - // Get each color independently, still in 565 precison but down at bit 0. - auto r5 = vshrq_n_u32(spread, 11), - g6 = vandq_u32(vdupq_n_u32(63), vshrq_n_u32(spread, 5)), - b5 = vandq_u32(vdupq_n_u32(31), spread); - - // Scale 565 precision up to 8-bit each, filling low 323 bits with high bits of each component. - auto r8 = vorrq_u32(vshlq_n_u32(r5, 3), vshrq_n_u32(r5, 2)), - g8 = vorrq_u32(vshlq_n_u32(g6, 2), vshrq_n_u32(g6, 4)), - b8 = vorrq_u32(vshlq_n_u32(b5, 3), vshrq_n_u32(b5, 2)); - - // Now put all the 8-bit components into SkPMColor order. - return (uint8x16_t)vorrq_u32(vshlq_n_u32(r8, SK_R32_SHIFT), // TODO: one shift is zero... - vorrq_u32(vshlq_n_u32(g8, SK_G32_SHIFT), - vorrq_u32(vshlq_n_u32(b8, SK_B32_SHIFT), - vdupq_n_u32(0xFF << SK_A32_SHIFT)))); -} - -static inline uint16x4_t narrow_to_565(uint8x16_t w8x16) { - uint32x4_t w = (uint32x4_t)w8x16; - - // Extract out top RGB 565 bits of each pixel, with no rounding. - auto r5 = vandq_u32(vdupq_n_u32(31), vshrq_n_u32(w, SK_R32_SHIFT + 3)), - g6 = vandq_u32(vdupq_n_u32(63), vshrq_n_u32(w, SK_G32_SHIFT + 2)), - b5 = vandq_u32(vdupq_n_u32(31), vshrq_n_u32(w, SK_B32_SHIFT + 3)); - - // Now put the bits in place in the low 16-bits of each 32-bit lane. - auto spread = vorrq_u32(vshlq_n_u32(r5, 11), - vorrq_u32(vshlq_n_u32(g6, 5), - b5)); - - // Pack the low 16-bits of our 128-bit register down into a 64-bit register. - // spread == 0000 rgb3 0000 rgb2 0000 rgb1 0000 rgb0 - // v == rgb3 rgb2 rgb1 rgb0 - auto v = vmovn_u32(spread); - return v; -} - - -inline Sk4px Sk4px::Load4(const SkPMColor16 src[4]) { - return Sk16b(widen_to_8888(vld1_u16(src))); -} -inline Sk4px Sk4px::Load2(const SkPMColor16 src[2]) { - auto src2 = ((uint32_t)src[0] ) - | ((uint32_t)src[1] << 16); - return Sk16b(widen_to_8888(vcreate_u16(src2))); -} -inline Sk4px Sk4px::Load1(const SkPMColor16 src[1]) { - return Sk16b(widen_to_8888(vcreate_u16(src[0]))); -} - -inline void Sk4px::store4(SkPMColor16 dst[4]) const { - vst1_u16(dst, narrow_to_565(this->fVec)); -} -inline void Sk4px::store2(SkPMColor16 dst[2]) const { - auto v = narrow_to_565(this->fVec); - dst[0] = vget_lane_u16(v, 0); - dst[1] = vget_lane_u16(v, 1); -} -inline void Sk4px::store1(SkPMColor16 dst[1]) const { - dst[0] = vget_lane_u16(narrow_to_565(this->fVec), 0); -} - } // namespace diff --git a/gfx/skia/skia/src/opts/Sk4px_SSE2.h b/gfx/skia/skia/src/opts/Sk4px_SSE2.h index 96f21db399..dc0c8ace70 100644 --- a/gfx/skia/skia/src/opts/Sk4px_SSE2.h +++ b/gfx/skia/skia/src/opts/Sk4px_SSE2.h @@ -101,79 +101,4 @@ inline Sk4px Sk4px::zeroAlphas() const { return Sk16b(_mm_andnot_si128(_mm_set1_epi32(0xFF << SK_A32_SHIFT), this->fVec)); } -static inline __m128i widen_low_half_to_8888(__m128i v) { - // RGB565 format: |R....|G.....|B....| - // Bit: 16 11 5 0 - - // First get each pixel into its own 32-bit lane. - // v == ____ ____ ____ ____ rgb3 rgb2 rgb1 rgb0 - // spread == 0000 rgb3 0000 rgb2 0000 rgb1 0000 rgb0 - auto spread = _mm_unpacklo_epi16(v, _mm_setzero_si128()); - - // Get each color independently, still in 565 precison but down at bit 0. - auto r5 = _mm_srli_epi32(spread, 11), - g6 = _mm_and_si128(_mm_set1_epi32(63), _mm_srli_epi32(spread, 5)), - b5 = _mm_and_si128(_mm_set1_epi32(31), spread); - - // Scale 565 precision up to 8-bit each, filling low 323 bits with high bits of each component. - auto r8 = _mm_or_si128(_mm_slli_epi32(r5, 3), _mm_srli_epi32(r5, 2)), - g8 = _mm_or_si128(_mm_slli_epi32(g6, 2), _mm_srli_epi32(g6, 4)), - b8 = _mm_or_si128(_mm_slli_epi32(b5, 3), _mm_srli_epi32(b5, 2)); - - // Now put all the 8-bit components into SkPMColor order. - return _mm_or_si128(_mm_slli_epi32(r8, SK_R32_SHIFT), // TODO: one of these shifts is zero... - _mm_or_si128(_mm_slli_epi32(g8, SK_G32_SHIFT), - _mm_or_si128(_mm_slli_epi32(b8, SK_B32_SHIFT), - _mm_set1_epi32(0xFF << SK_A32_SHIFT)))); -} - -static inline __m128i narrow_to_565(__m128i w) { - // Extract out top RGB 565 bits of each pixel, with no rounding. - auto r5 = _mm_and_si128(_mm_set1_epi32(31), _mm_srli_epi32(w, SK_R32_SHIFT + 3)), - g6 = _mm_and_si128(_mm_set1_epi32(63), _mm_srli_epi32(w, SK_G32_SHIFT + 2)), - b5 = _mm_and_si128(_mm_set1_epi32(31), _mm_srli_epi32(w, SK_B32_SHIFT + 3)); - - // Now put the bits in place in the low 16-bits of each 32-bit lane. - auto spread = _mm_or_si128(_mm_slli_epi32(r5, 11), - _mm_or_si128(_mm_slli_epi32(g6, 5), - b5)); - - // We want to pack the bottom 16-bits of spread down into the low half of the register, v. - // spread == 0000 rgb3 0000 rgb2 0000 rgb1 0000 rgb0 - // v == ____ ____ ____ ____ rgb3 rgb2 rgb1 rgb0 - - // Ideally now we'd use _mm_packus_epi32(spread, ) to pack v. But that's from SSE4. - // With only SSE2, we need to use _mm_packs_epi32. That does signed saturation, and - // we need to preserve all 16 bits. So we pretend our data is signed by sign-extending first. - // TODO: is it faster to just _mm_shuffle_epi8 this when we have SSSE3? - auto signExtended = _mm_srai_epi32(_mm_slli_epi32(spread, 16), 16); - auto v = _mm_packs_epi32(signExtended, signExtended); - return v; -} - -inline Sk4px Sk4px::Load4(const SkPMColor16 src[4]) { - return Sk16b(widen_low_half_to_8888(_mm_loadl_epi64((const __m128i*)src))); -} -inline Sk4px Sk4px::Load2(const SkPMColor16 src[2]) { - auto src2 = ((uint32_t)src[0] ) - | ((uint32_t)src[1] << 16); - return Sk16b(widen_low_half_to_8888(_mm_cvtsi32_si128(src2))); -} -inline Sk4px Sk4px::Load1(const SkPMColor16 src[1]) { - return Sk16b(widen_low_half_to_8888(_mm_insert_epi16(_mm_setzero_si128(), src[0], 0))); -} - -inline void Sk4px::store4(SkPMColor16 dst[4]) const { - _mm_storel_epi64((__m128i*)dst, narrow_to_565(this->fVec)); -} -inline void Sk4px::store2(SkPMColor16 dst[2]) const { - uint32_t dst2 = _mm_cvtsi128_si32(narrow_to_565(this->fVec)); - dst[0] = dst2; - dst[1] = dst2 >> 16; -} -inline void Sk4px::store1(SkPMColor16 dst[1]) const { - uint32_t dst2 = _mm_cvtsi128_si32(narrow_to_565(this->fVec)); - dst[0] = dst2; -} - } // namespace diff --git a/gfx/skia/skia/src/opts/Sk4px_none.h b/gfx/skia/skia/src/opts/Sk4px_none.h index efbd780c9f..b43ee875b2 100644 --- a/gfx/skia/skia/src/opts/Sk4px_none.h +++ b/gfx/skia/skia/src/opts/Sk4px_none.h @@ -106,35 +106,4 @@ inline Sk4px Sk4px::zeroColors() const { 0,0,0, this->kth<15>()); } -inline Sk4px Sk4px::Load4(const SkPMColor16 src[4]) { - SkPMColor src32[4]; - for (int i = 0; i < 4; i++) { src32[i] = SkPixel16ToPixel32(src[i]); } - return Load4(src32); -} -inline Sk4px Sk4px::Load2(const SkPMColor16 src[2]) { - SkPMColor src32[2]; - for (int i = 0; i < 2; i++) { src32[i] = SkPixel16ToPixel32(src[i]); } - return Load2(src32); -} -inline Sk4px Sk4px::Load1(const SkPMColor16 src[1]) { - SkPMColor src32 = SkPixel16ToPixel32(src[0]); - return Load1(&src32); -} - -inline void Sk4px::store4(SkPMColor16 dst[4]) const { - SkPMColor dst32[4]; - this->store4(dst32); - for (int i = 0; i < 4; i++) { dst[i] = SkPixel32ToPixel16(dst32[i]); } -} -inline void Sk4px::store2(SkPMColor16 dst[2]) const { - SkPMColor dst32[2]; - this->store2(dst32); - for (int i = 0; i < 2; i++) { dst[i] = SkPixel32ToPixel16(dst32[i]); } -} -inline void Sk4px::store1(SkPMColor16 dst[1]) const { - SkPMColor dst32; - this->store1(&dst32); - dst[0] = SkPixel32ToPixel16(dst32); -} - } // namespace diff --git a/gfx/skia/skia/src/opts/SkBitmapProcState_arm_neon.cpp b/gfx/skia/skia/src/opts/SkBitmapProcState_arm_neon.cpp index d1f71c270c..ce2656da65 100644 --- a/gfx/skia/skia/src/opts/SkBitmapProcState_arm_neon.cpp +++ b/gfx/skia/skia/src/opts/SkBitmapProcState_arm_neon.cpp @@ -15,7 +15,6 @@ // Required to ensure the table is part of the final binary. extern const SkBitmapProcState::SampleProc32 gSkBitmapProcStateSample32_neon[]; -extern const SkBitmapProcState::SampleProc16 gSkBitmapProcStateSample16_neon[]; #define NAME_WRAP(x) x ## _neon #include "SkBitmapProcState_filter_neon.h" @@ -79,30 +78,6 @@ const SkBitmapProcState::SampleProc32 gSkBitmapProcStateSample32_neon[] = { SG8_alpha_D32_filter_DX_neon, }; -const SkBitmapProcState::SampleProc16 gSkBitmapProcStateSample16_neon[] = { - S32_D16_nofilter_DXDY_neon, - S32_D16_nofilter_DX_neon, - S32_D16_filter_DXDY_neon, - S32_D16_filter_DX_neon, - - S16_D16_nofilter_DXDY_neon, - S16_D16_nofilter_DX_neon, - S16_D16_filter_DXDY_neon, - S16_D16_filter_DX_neon, - - SI8_D16_nofilter_DXDY_neon, - SI8_D16_nofilter_DX_neon, - SI8_D16_filter_DXDY_neon, - SI8_D16_filter_DX_neon, - - // Don't support 4444 -> 565 - nullptr, nullptr, nullptr, nullptr, - // Don't support A8 -> 565 - nullptr, nullptr, nullptr, nullptr, - // Don't support G8 -> 565 (but we could) - nullptr, nullptr, nullptr, nullptr, -}; - /////////////////////////////////////////////////////////////////////////////// #include diff --git a/gfx/skia/skia/src/opts/SkBitmapProcState_opts_SSE2.cpp b/gfx/skia/skia/src/opts/SkBitmapProcState_opts_SSE2.cpp index 68d920d8d8..cffe9625e5 100644 --- a/gfx/skia/skia/src/opts/SkBitmapProcState_opts_SSE2.cpp +++ b/gfx/skia/skia/src/opts/SkBitmapProcState_opts_SSE2.cpp @@ -632,117 +632,3 @@ void ClampX_ClampY_nofilter_affine_SSE2(const SkBitmapProcState& s, fy += dy; } } - -/* SSE version of S32_D16_filter_DX_SSE2 - * Definition is in section of "D16 functions for SRC == 8888" in SkBitmapProcState.cpp - * It combines S32_opaque_D32_filter_DX_SSE2 and SkPixel32ToPixel16 - */ -void S32_D16_filter_DX_SSE2(const SkBitmapProcState& s, - const uint32_t* xy, - int count, uint16_t* colors) { - SkASSERT(count > 0 && colors != nullptr); - SkASSERT(s.fFilterLevel != kNone_SkFilterQuality); - SkASSERT(kN32_SkColorType == s.fPixmap.colorType()); - SkASSERT(s.fPixmap.isOpaque()); - - SkPMColor dstColor; - const char* srcAddr = static_cast(s.fPixmap.addr()); - size_t rb = s.fPixmap.rowBytes(); - uint32_t XY = *xy++; - unsigned y0 = XY >> 14; - const uint32_t* row0 = reinterpret_cast(srcAddr + (y0 >> 4) * rb); - const uint32_t* row1 = reinterpret_cast(srcAddr + (XY & 0x3FFF) * rb); - unsigned subY = y0 & 0xF; - - // ( 0, 0, 0, 0, 0, 0, 0, 16) - __m128i sixteen = _mm_cvtsi32_si128(16); - - // ( 0, 0, 0, 0, 16, 16, 16, 16) - sixteen = _mm_shufflelo_epi16(sixteen, 0); - - // ( 0, 0, 0, 0, 0, 0, 0, y) - __m128i allY = _mm_cvtsi32_si128(subY); - - // ( 0, 0, 0, 0, y, y, y, y) - allY = _mm_shufflelo_epi16(allY, 0); - - // ( 0, 0, 0, 0, 16-y, 16-y, 16-y, 16-y) - __m128i negY = _mm_sub_epi16(sixteen, allY); - - // (16-y, 16-y, 16-y, 16-y, y, y, y, y) - allY = _mm_unpacklo_epi64(allY, negY); - - // (16, 16, 16, 16, 16, 16, 16, 16 ) - sixteen = _mm_shuffle_epi32(sixteen, 0); - - // ( 0, 0, 0, 0, 0, 0, 0, 0) - __m128i zero = _mm_setzero_si128(); - - do { - uint32_t XX = *xy++; // x0:14 | 4 | x1:14 - unsigned x0 = XX >> 18; - unsigned x1 = XX & 0x3FFF; - - // (0, 0, 0, 0, 0, 0, 0, x) - __m128i allX = _mm_cvtsi32_si128((XX >> 14) & 0x0F); - - // (0, 0, 0, 0, x, x, x, x) - allX = _mm_shufflelo_epi16(allX, 0); - - // (x, x, x, x, x, x, x, x) - allX = _mm_shuffle_epi32(allX, 0); - - // (16-x, 16-x, 16-x, 16-x, 16-x, 16-x, 16-x) - __m128i negX = _mm_sub_epi16(sixteen, allX); - - // Load 4 samples (pixels). - __m128i a00 = _mm_cvtsi32_si128(row0[x0]); - __m128i a01 = _mm_cvtsi32_si128(row0[x1]); - __m128i a10 = _mm_cvtsi32_si128(row1[x0]); - __m128i a11 = _mm_cvtsi32_si128(row1[x1]); - - // (0, 0, a00, a10) - __m128i a00a10 = _mm_unpacklo_epi32(a10, a00); - - // Expand to 16 bits per component. - a00a10 = _mm_unpacklo_epi8(a00a10, zero); - - // ((a00 * (16-y)), (a10 * y)). - a00a10 = _mm_mullo_epi16(a00a10, allY); - - // (a00 * (16-y) * (16-x), a10 * y * (16-x)). - a00a10 = _mm_mullo_epi16(a00a10, negX); - - // (0, 0, a01, a10) - __m128i a01a11 = _mm_unpacklo_epi32(a11, a01); - - // Expand to 16 bits per component. - a01a11 = _mm_unpacklo_epi8(a01a11, zero); - - // (a01 * (16-y)), (a11 * y) - a01a11 = _mm_mullo_epi16(a01a11, allY); - - // (a01 * (16-y) * x), (a11 * y * x) - a01a11 = _mm_mullo_epi16(a01a11, allX); - - // (a00*w00 + a01*w01, a10*w10 + a11*w11) - __m128i sum = _mm_add_epi16(a00a10, a01a11); - - // (DC, a00*w00 + a01*w01) - __m128i shifted = _mm_shuffle_epi32(sum, 0xEE); - - // (DC, a00*w00 + a01*w01 + a10*w10 + a11*w11) - sum = _mm_add_epi16(sum, shifted); - - // Divide each 16 bit component by 256. - sum = _mm_srli_epi16(sum, 8); - - // Pack lower 4 16 bit values of sum into lower 4 bytes. - sum = _mm_packus_epi16(sum, zero); - - // Extract low int and store. - dstColor = _mm_cvtsi128_si32(sum); - - *colors++ = SkPixel32ToPixel16(dstColor); - } while (--count > 0); -} diff --git a/gfx/skia/skia/src/opts/SkBitmapProcState_opts_SSE2.h b/gfx/skia/skia/src/opts/SkBitmapProcState_opts_SSE2.h index 82bf2cdae1..d14f282dee 100644 --- a/gfx/skia/skia/src/opts/SkBitmapProcState_opts_SSE2.h +++ b/gfx/skia/skia/src/opts/SkBitmapProcState_opts_SSE2.h @@ -24,8 +24,5 @@ void ClampX_ClampY_filter_affine_SSE2(const SkBitmapProcState& s, uint32_t xy[], int count, int x, int y); void ClampX_ClampY_nofilter_affine_SSE2(const SkBitmapProcState& s, uint32_t xy[], int count, int x, int y); -void S32_D16_filter_DX_SSE2(const SkBitmapProcState& s, - const uint32_t* xy, - int count, uint16_t* colors); #endif diff --git a/gfx/skia/skia/src/opts/SkBitmapProcState_opts_SSSE3.cpp b/gfx/skia/skia/src/opts/SkBitmapProcState_opts_SSSE3.cpp index 8e381eb3c2..2de8e4049a 100644 --- a/gfx/skia/skia/src/opts/SkBitmapProcState_opts_SSSE3.cpp +++ b/gfx/skia/skia/src/opts/SkBitmapProcState_opts_SSSE3.cpp @@ -732,28 +732,6 @@ void S32_alpha_D32_filter_DXDY_SSSE3(const SkBitmapProcState& s, S32_generic_D32_filter_DXDY_SSSE3(s, xy, count, colors); } -void S32_D16_filter_DX_SSSE3(const SkBitmapProcState& s, - const uint32_t* xy, - int count, uint16_t* colors) { - SkASSERT(254 >= count); - SkAutoSTMalloc<254, uint32_t> colors32(count); - S32_generic_D32_filter_DX_SSSE3(s, xy, count, colors32); - for(int i = 0; i < count; i++) { - *colors++ = SkPixel32ToPixel16(colors32[i]); - } -} - -void S32_D16_filter_DXDY_SSSE3(const SkBitmapProcState& s, - const uint32_t* xy, - int count, uint16_t* colors) { - SkASSERT(64 >= count); - SkAutoSTMalloc<64, uint32_t> colors32(count); - S32_generic_D32_filter_DXDY_SSSE3(s, xy, count, colors32); - for(int i = 0; i < count; i++) { - *colors++ = SkPixel32ToPixel16(colors32[i]); - } -} - #else // SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSSE3 void S32_opaque_D32_filter_DX_SSSE3(const SkBitmapProcState& s, @@ -780,16 +758,4 @@ void S32_alpha_D32_filter_DXDY_SSSE3(const SkBitmapProcState& s, sk_throw(); } -void S32_D16_filter_DX_SSSE3(const SkBitmapProcState& s, - const uint32_t* xy, - int count, uint16_t* colors) { - sk_throw(); -} - -void S32_D16_filter_DXDY_SSSE3(const SkBitmapProcState& s, - const uint32_t* xy, - int count, uint16_t* colors) { - sk_throw(); -} - #endif diff --git a/gfx/skia/skia/src/opts/SkBitmapProcState_opts_SSSE3.h b/gfx/skia/skia/src/opts/SkBitmapProcState_opts_SSSE3.h index c7a9a89923..e7799fa2c7 100644 --- a/gfx/skia/skia/src/opts/SkBitmapProcState_opts_SSSE3.h +++ b/gfx/skia/skia/src/opts/SkBitmapProcState_opts_SSSE3.h @@ -22,12 +22,4 @@ void S32_opaque_D32_filter_DXDY_SSSE3(const SkBitmapProcState& s, void S32_alpha_D32_filter_DXDY_SSSE3(const SkBitmapProcState& s, const uint32_t* xy, int count, uint32_t* colors); - -void S32_D16_filter_DX_SSSE3(const SkBitmapProcState& s, - const uint32_t* xy, - int count, uint16_t* colors); -void S32_D16_filter_DXDY_SSSE3(const SkBitmapProcState& s, - const uint32_t* xy, - int count, uint16_t* colors); - #endif diff --git a/gfx/skia/skia/src/opts/SkBitmapProcState_opts_mips_dsp.cpp b/gfx/skia/skia/src/opts/SkBitmapProcState_opts_mips_dsp.cpp index dac4e20381..a80e955aeb 100644 --- a/gfx/skia/skia/src/opts/SkBitmapProcState_opts_mips_dsp.cpp +++ b/gfx/skia/skia/src/opts/SkBitmapProcState_opts_mips_dsp.cpp @@ -12,138 +12,6 @@ #include "SkPaint.h" #include "SkUtils.h" -static void SI8_D16_nofilter_DX_mips_dsp(const SkBitmapProcState& s, - const uint32_t* SK_RESTRICT xy, - int count, uint16_t* SK_RESTRICT colors) { - SkASSERT(count > 0 && colors != nullptr); - SkASSERT(s.fInvType <= (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)); - SkASSERT(kNone_SkFilterQuality == s.fFilterLevel); - const uint16_t* SK_RESTRICT table = s.fPixmap.ctable()->read16BitCache(); - const uint8_t* SK_RESTRICT srcAddr = (const uint8_t*)s.fPixmap.addr(); - SkASSERT((unsigned)xy[0] < (unsigned)s.fPixmap.height()); - srcAddr = (const uint8_t*)((const char*)srcAddr + xy[0] * s.fPixmap.rowBytes()); - uint8_t src; - - if (1 == s.fPixmap.width()) { - src = srcAddr[0]; - uint16_t dstValue = table[src]; - sk_memset16(colors, dstValue, count); - } else { - int count8; - const uint16_t* SK_RESTRICT xx = (const uint16_t*)(xy + 1); - - __asm__ volatile ( - ".set push \n\t" - ".set noreorder \n\t" - ".set noat \n\t" - "sra %[count8], %[count], 3 \n\t" - "beqz %[count8], 3f \n\t" - " addiu %[count8], %[count8], -1 \n\t" - "1: \n\t" - "beqz %[count8], 2f \n\t" - " addiu %[count8], %[count8], -1 \n\t" - "pref 0, 16(%[xx]) \n\t" - "lhu $t0, 0(%[xx]) \n\t" - "lhu $t1, 2(%[xx]) \n\t" - "lhu $t2, 4(%[xx]) \n\t" - "lhu $t3, 6(%[xx]) \n\t" - "lhu $t4, 8(%[xx]) \n\t" - "lhu $t5, 10(%[xx]) \n\t" - "lhu $t6, 12(%[xx]) \n\t" - "lhu $t7, 14(%[xx]) \n\t" - "pref 0, 8(%[srcAddr]) \n\t" - "lbux $t0, $t0(%[srcAddr]) \n\t" - "lbux $t1, $t1(%[srcAddr]) \n\t" - "lbux $t2, $t2(%[srcAddr]) \n\t" - "lbux $t3, $t3(%[srcAddr]) \n\t" - "lbux $t4, $t4(%[srcAddr]) \n\t" - "lbux $t5, $t5(%[srcAddr]) \n\t" - "lbux $t6, $t6(%[srcAddr]) \n\t" - "lbux $t7, $t7(%[srcAddr]) \n\t" - "addu $t0, $t0, $t0 \n\t" - "addu $t1, $t1, $t1 \n\t" - "addu $t2, $t2, $t2 \n\t" - "addu $t3, $t3, $t3 \n\t" - "addu $t4, $t4, $t4 \n\t" - "addu $t5, $t5, $t5 \n\t" - "addu $t6, $t6, $t6 \n\t" - "addu $t7, $t7, $t7 \n\t" - "pref 0, 16(%[table]) \n\t" - "lhx $t0, $t0(%[table]) \n\t" - "lhx $t1, $t1(%[table]) \n\t" - "lhx $t2, $t2(%[table]) \n\t" - "lhx $t3, $t3(%[table]) \n\t" - "lhx $t4, $t4(%[table]) \n\t" - "lhx $t5, $t5(%[table]) \n\t" - "lhx $t6, $t6(%[table]) \n\t" - "lhx $t7, $t7(%[table]) \n\t" - "sh $t0, 0(%[colors]) \n\t" - "sh $t1, 2(%[colors]) \n\t" - "sh $t2, 4(%[colors]) \n\t" - "sh $t3, 6(%[colors]) \n\t" - "sh $t4, 8(%[colors]) \n\t" - "sh $t5, 10(%[colors]) \n\t" - "sh $t6, 12(%[colors]) \n\t" - "sh $t7, 14(%[colors]) \n\t" - "addiu %[xx], %[xx], 16 \n\t" - "bgtz %[count8], 1b \n\t" - " addiu %[colors], %[colors], 16 \n\t" - "2: \n\t" - "lhu $t0, 0(%[xx]) \n\t" - "lhu $t1, 2(%[xx]) \n\t" - "lhu $t2, 4(%[xx]) \n\t" - "lhu $t3, 6(%[xx]) \n\t" - "lhu $t4, 8(%[xx]) \n\t" - "lhu $t5, 10(%[xx]) \n\t" - "lhu $t6, 12(%[xx]) \n\t" - "lhu $t7, 14(%[xx]) \n\t" - "lbux $t0, $t0(%[srcAddr]) \n\t" - "lbux $t1, $t1(%[srcAddr]) \n\t" - "lbux $t2, $t2(%[srcAddr]) \n\t" - "lbux $t3, $t3(%[srcAddr]) \n\t" - "lbux $t4, $t4(%[srcAddr]) \n\t" - "lbux $t5, $t5(%[srcAddr]) \n\t" - "lbux $t6, $t6(%[srcAddr]) \n\t" - "lbux $t7, $t7(%[srcAddr]) \n\t" - "addu $t0, $t0, $t0 \n\t" - "addu $t1, $t1, $t1 \n\t" - "addu $t2, $t2, $t2 \n\t" - "addu $t3, $t3, $t3 \n\t" - "addu $t4, $t4, $t4 \n\t" - "addu $t5, $t5, $t5 \n\t" - "addu $t6, $t6, $t6 \n\t" - "addu $t7, $t7, $t7 \n\t" - "lhx $t0, $t0(%[table]) \n\t" - "lhx $t1, $t1(%[table]) \n\t" - "lhx $t2, $t2(%[table]) \n\t" - "lhx $t3, $t3(%[table]) \n\t" - "lhx $t4, $t4(%[table]) \n\t" - "lhx $t5, $t5(%[table]) \n\t" - "lhx $t6, $t6(%[table]) \n\t" - "lhx $t7, $t7(%[table]) \n\t" - "sh $t0, 0(%[colors]) \n\t" - "sh $t1, 2(%[colors]) \n\t" - "sh $t2, 4(%[colors]) \n\t" - "sh $t3, 6(%[colors]) \n\t" - "sh $t4, 8(%[colors]) \n\t" - "sh $t5, 10(%[colors]) \n\t" - "sh $t6, 12(%[colors]) \n\t" - "sh $t7, 14(%[colors]) \n\t" - "addiu %[xx], %[xx], 16 \n\t" - "addiu %[colors], %[colors], 16 \n\t" - "3: \n\t" - ".set pop \n\t" - : [xx]"+r"(xx), [count8]"=&r"(count8), [colors]"+r"(colors) - : [table]"r"(table), [srcAddr]"r"(srcAddr), [count]"r"(count) - : "memory","t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7" - ); - - for (int i = (count & 7); i > 0; --i) { - src = srcAddr[*xx++]; *colors++ = table[src]; - } - } -} - static void SI8_opaque_D32_nofilter_DX_mips_dsp(const SkBitmapProcState& s, const uint32_t* SK_RESTRICT xy, int count, SkPMColor* SK_RESTRICT colors) { @@ -379,8 +247,6 @@ void SkBitmapProcState::platformProcs() { switch (fPixmap.colorType()) { case kIndex_8_SkColorType: if (justDx && kNone_SkFilterQuality == fFilterLevel) { - fSampleProc16 = SI8_D16_nofilter_DX_mips_dsp; - fShaderProc16 = nullptr; if (isOpaque) { fSampleProc32 = SI8_opaque_D32_nofilter_DX_mips_dsp; fShaderProc32 = nullptr; diff --git a/gfx/skia/skia/src/opts/SkBlitRow_opts_arm_neon.cpp b/gfx/skia/skia/src/opts/SkBlitRow_opts_arm_neon.cpp index 1f97971ee4..95bd229486 100644 --- a/gfx/skia/skia/src/opts/SkBlitRow_opts_arm_neon.cpp +++ b/gfx/skia/skia/src/opts/SkBlitRow_opts_arm_neon.cpp @@ -390,7 +390,7 @@ void S32A_D565_Opaque_neon(uint16_t* SK_RESTRICT dst, "movi v4.8h, #0x80 \t\n" "1: \t\n" - "sub %[count], %[count], #16 \t\n" + "sub %w[count], %w[count], #16 \t\n" "ld1 {v16.8h-v17.8h}, [%[dst]] \t\n" "ld4 {v0.16b-v3.16b}, [%[src]], #64 \t\n" "prfm pldl1keep, [%[src],#512] \t\n" @@ -416,7 +416,7 @@ void S32A_D565_Opaque_neon(uint16_t* SK_RESTRICT dst, "umlal v22.8h, v3.8b, v18.8b \t\n" "ushr v20.8h, v22.8h, #5 \t\n" "addhn v20.8b, v22.8h, v20.8h \t\n" - "cmp %[count], #16 \t\n" + "cmp %w[count], #16 \t\n" "mov v6.16b, v4.16b \t\n" "mov v5.16b, v4.16b \t\n" "umlal v6.8h, v3.8b, v16.8b \t\n" diff --git a/gfx/skia/skia/src/opts/SkColorCubeFilter_opts.h b/gfx/skia/skia/src/opts/SkColorCubeFilter_opts.h index 4c394051bb..ab8d1d4c9b 100644 --- a/gfx/skia/skia/src/opts/SkColorCubeFilter_opts.h +++ b/gfx/skia/skia/src/opts/SkColorCubeFilter_opts.h @@ -59,10 +59,10 @@ void color_cube_filter_span(const SkPMColor src[], const SkColor lutColor10 = colorCube[ix + i10]; const SkColor lutColor11 = colorCube[ix + i11]; - Sk4f sum = Sk4f::FromBytes((const uint8_t*)&lutColor00) * g0b0; - sum = sum + Sk4f::FromBytes((const uint8_t*)&lutColor01) * g0b1; - sum = sum + Sk4f::FromBytes((const uint8_t*)&lutColor10) * g1b0; - sum = sum + Sk4f::FromBytes((const uint8_t*)&lutColor11) * g1b1; + Sk4f sum = SkNx_cast(Sk4b::Load((const uint8_t*)&lutColor00)) * g0b0; + sum = sum + SkNx_cast(Sk4b::Load((const uint8_t*)&lutColor01)) * g0b1; + sum = sum + SkNx_cast(Sk4b::Load((const uint8_t*)&lutColor10)) * g1b0; + sum = sum + SkNx_cast(Sk4b::Load((const uint8_t*)&lutColor11)) * g1b1; color = color + sum * Sk4f((float)colorToFactors[x][r]); } if (a != 255) { @@ -74,7 +74,7 @@ void color_cube_filter_span(const SkPMColor src[], color = SkNx_shuffle<2,1,0,3>(color); #endif uint8_t* dstBytes = (uint8_t*)(dst+i); - color.toBytes(dstBytes); + SkNx_cast(color).store(dstBytes); dstBytes[SK_A32_SHIFT/8] = a; } } diff --git a/gfx/skia/skia/src/opts/SkNx_avx.h b/gfx/skia/skia/src/opts/SkNx_avx.h index 9697303e37..f635181a92 100644 --- a/gfx/skia/skia/src/opts/SkNx_avx.h +++ b/gfx/skia/skia/src/opts/SkNx_avx.h @@ -13,6 +13,8 @@ // All the SSE specializations are still good ideas. We'll just add Sk8f. #include "SkNx_sse.h" +// SkNx_sse.h defines SKNX_IS_FAST. + namespace { // See SkNx.h template <> @@ -24,27 +26,10 @@ public: SkNx(float val) : fVec(_mm256_set1_ps(val)) {} static SkNx Load(const float vals[8]) { return _mm256_loadu_ps(vals); } - static SkNx FromBytes(const uint8_t bytes[8]) { - __m128i fix8 = _mm_loadl_epi64((const __m128i*)bytes), - fix16 = _mm_unpacklo_epi8 (fix8 , _mm_setzero_si128()), - lo32 = _mm_unpacklo_epi16(fix16, _mm_setzero_si128()), - hi32 = _mm_unpackhi_epi16(fix16, _mm_setzero_si128()); - __m256i fix32 = _mm256_insertf128_si256(_mm256_castsi128_si256(lo32), hi32, 1); - return _mm256_cvtepi32_ps(fix32); - } - SkNx(float a, float b, float c, float d, float e, float f, float g, float h) : fVec(_mm256_setr_ps(a,b,c,d,e,f,g,h)) {} void store(float vals[8]) const { _mm256_storeu_ps(vals, fVec); } - void toBytes(uint8_t bytes[8]) const { - __m256i fix32 = _mm256_cvttps_epi32(fVec); - __m128i lo32 = _mm256_extractf128_si256(fix32, 0), - hi32 = _mm256_extractf128_si256(fix32, 1), - fix16 = _mm_packus_epi32(lo32, hi32), - fix8 = _mm_packus_epi16(fix16, fix16); - _mm_storel_epi64((__m128i*)bytes, fix8); - } SkNx operator + (const SkNx& o) const { return _mm256_add_ps(fVec, o.fVec); } SkNx operator - (const SkNx& o) const { return _mm256_sub_ps(fVec, o.fVec); } @@ -85,6 +70,25 @@ public: __m256 fVec; }; +template<> inline Sk8b SkNx_cast(const Sk8f& src) { + __m256i _32 = _mm256_cvttps_epi32(src.fVec); + __m128i lo = _mm256_extractf128_si256(_32, 0), + hi = _mm256_extractf128_si256(_32, 1), + _16 = _mm_packus_epi32(lo, hi); + return _mm_packus_epi16(_16, _16); +} + +template<> inline Sk8f SkNx_cast(const Sk8b& src) { + /* TODO lo = _mm_cvtepu8_epi32(src.fVec), + * hi = _mm_cvtepu8_epi32(_mm_srli_si128(src.fVec, 4)) + */ + __m128i _16 = _mm_unpacklo_epi8(src.fVec, _mm_setzero_si128()), + lo = _mm_unpacklo_epi16(_16, _mm_setzero_si128()), + hi = _mm_unpackhi_epi16(_16, _mm_setzero_si128()); + __m256i _32 = _mm256_insertf128_si256(_mm256_castsi128_si256(lo), hi, 1); + return _mm256_cvtepi32_ps(_32); +} + } // namespace #endif//SkNx_avx_DEFINED diff --git a/gfx/skia/skia/src/opts/SkNx_neon.h b/gfx/skia/skia/src/opts/SkNx_neon.h index 6fe6137e5f..0955cb2b5b 100644 --- a/gfx/skia/skia/src/opts/SkNx_neon.h +++ b/gfx/skia/skia/src/opts/SkNx_neon.h @@ -8,6 +8,8 @@ #ifndef SkNx_neon_DEFINED #define SkNx_neon_DEFINED +#define SKNX_IS_FAST + namespace { // See SkNx.h // Well, this is absurd. The shifts require compile-time constant arguments. @@ -148,31 +150,9 @@ public: SkNx() {} SkNx(float val) : fVec(vdupq_n_f32(val)) {} static SkNx Load(const float vals[4]) { return vld1q_f32(vals); } - static SkNx FromBytes(const uint8_t vals[4]) { - uint8x8_t fix8 = (uint8x8_t)vld1_dup_u32((const uint32_t*)vals); - uint16x8_t fix8_16 = vmovl_u8(fix8); - uint32x4_t fix8_32 = vmovl_u16(vget_low_u16(fix8_16)); - return SkNx(vcvtq_f32_u32(fix8_32)); - } - SkNx(float a, float b, float c, float d) { fVec = (float32x4_t) { a, b, c, d }; } void store(float vals[4]) const { vst1q_f32(vals, fVec); } - void toBytes(uint8_t bytes[4]) const { - uint32x4_t fix8_32 = vcvtq_u32_f32(fVec); - uint16x4_t fix8_16 = vqmovn_u32(fix8_32); - uint8x8_t fix8 = vqmovn_u16(vcombine_u16(fix8_16, vdup_n_u16(0))); - vst1_lane_u32((uint32_t*)bytes, (uint32x2_t)fix8, 0); - } - - static void ToBytes(uint8_t bytes[16], - const SkNx& a, const SkNx& b, const SkNx& c, const SkNx& d) { - vst1q_u8(bytes, vuzpq_u8(vuzpq_u8((uint8x16_t)vcvtq_u32_f32(a.fVec), - (uint8x16_t)vcvtq_u32_f32(b.fVec)).val[0], - vuzpq_u8((uint8x16_t)vcvtq_u32_f32(c.fVec), - (uint8x16_t)vcvtq_u32_f32(d.fVec)).val[0]).val[0]); - } - SkNx approxInvert() const { float32x4_t est0 = vrecpeq_f32(fVec), est1 = vmulq_f32(vrecpsq_f32(est0, fVec), est0); @@ -285,6 +265,24 @@ public: uint16x8_t fVec; }; +template <> +class SkNx<4, uint8_t> { +public: + SkNx(const uint8x8_t& vec) : fVec(vec) {} + + SkNx() {} + static SkNx Load(const uint8_t vals[4]) { + return (uint8x8_t)vld1_dup_u32((const uint32_t*)vals); + } + void store(uint8_t vals[4]) const { + return vst1_lane_u32((uint32_t*)vals, (uint32x2_t)fVec, 0); + } + + // TODO as needed + + uint8x8_t fVec; +}; + template <> class SkNx<16, uint8_t> { public: @@ -327,11 +325,30 @@ public: #undef SHIFT16 #undef SHIFT8 -template<> -inline SkNx<4, int> SkNx_cast(const SkNx<4, float>& src) { +template<> inline Sk4i SkNx_cast(const Sk4f& src) { return vcvtq_s32_f32(src.fVec); } +template<> inline Sk4b SkNx_cast(const Sk4f& src) { + uint32x4_t _32 = vcvtq_u32_f32(src.fVec); + uint16x4_t _16 = vqmovn_u32(_32); + return vqmovn_u16(vcombine_u16(_16, _16)); +} + +template<> inline Sk4f SkNx_cast(const Sk4b& src) { + uint16x8_t _16 = vmovl_u8 (src.fVec) ; + uint32x4_t _32 = vmovl_u16(vget_low_u16(_16)); + return vcvtq_f32_u32(_32); +} + +static inline void Sk4f_ToBytes(uint8_t bytes[16], + const Sk4f& a, const Sk4f& b, const Sk4f& c, const Sk4f& d) { + vst1q_u8(bytes, vuzpq_u8(vuzpq_u8((uint8x16_t)vcvtq_u32_f32(a.fVec), + (uint8x16_t)vcvtq_u32_f32(b.fVec)).val[0], + vuzpq_u8((uint8x16_t)vcvtq_u32_f32(c.fVec), + (uint8x16_t)vcvtq_u32_f32(d.fVec)).val[0]).val[0]); +} + } // namespace #endif//SkNx_neon_DEFINED diff --git a/gfx/skia/skia/src/opts/SkNx_sse.h b/gfx/skia/skia/src/opts/SkNx_sse.h index a4f8656536..b3f5091406 100644 --- a/gfx/skia/skia/src/opts/SkNx_sse.h +++ b/gfx/skia/skia/src/opts/SkNx_sse.h @@ -10,6 +10,8 @@ // This file may assume <= SSE2, but must check SK_CPU_SSE_LEVEL for anything more recent. +#define SKNX_IS_FAST + namespace { // See SkNx.h @@ -62,6 +64,52 @@ public: __m128 fVec; }; +template <> +class SkNx<2, double> { +public: + SkNx(const __m128d& vec) : fVec(vec) {} + + SkNx() {} + SkNx(double val) : fVec(_mm_set1_pd(val)) {} + static SkNx Load(const double vals[2]) { return _mm_loadu_pd(vals); } + SkNx(double a, double b) : fVec(_mm_setr_pd(a,b)) {} + + void store(double vals[2]) const { _mm_storeu_pd(vals, fVec); } + + SkNx operator + (const SkNx& o) const { return _mm_add_pd(fVec, o.fVec); } + SkNx operator - (const SkNx& o) const { return _mm_sub_pd(fVec, o.fVec); } + SkNx operator * (const SkNx& o) const { return _mm_mul_pd(fVec, o.fVec); } + SkNx operator / (const SkNx& o) const { return _mm_div_pd(fVec, o.fVec); } + + SkNx operator == (const SkNx& o) const { return _mm_cmpeq_pd (fVec, o.fVec); } + SkNx operator != (const SkNx& o) const { return _mm_cmpneq_pd(fVec, o.fVec); } + SkNx operator < (const SkNx& o) const { return _mm_cmplt_pd (fVec, o.fVec); } + SkNx operator > (const SkNx& o) const { return _mm_cmpgt_pd (fVec, o.fVec); } + SkNx operator <= (const SkNx& o) const { return _mm_cmple_pd (fVec, o.fVec); } + SkNx operator >= (const SkNx& o) const { return _mm_cmpge_pd (fVec, o.fVec); } + + static SkNx Min(const SkNx& l, const SkNx& r) { return _mm_min_pd(l.fVec, r.fVec); } + static SkNx Max(const SkNx& l, const SkNx& r) { return _mm_max_pd(l.fVec, r.fVec); } + + SkNx sqrt() const { return _mm_sqrt_pd(fVec); } + + template double kth() const { + SkASSERT(0 <= k && k < 2); + union { __m128d v; double fs[2]; } pun = {fVec}; + return pun.fs[k&1]; + } + + bool allTrue() const { return 0x3 == _mm_movemask_pd(fVec); } + bool anyTrue() const { return 0x0 != _mm_movemask_pd(fVec); } + + SkNx thenElse(const SkNx& t, const SkNx& e) const { + return _mm_or_pd(_mm_and_pd (fVec, t.fVec), + _mm_andnot_pd(fVec, e.fVec)); + } + + __m128d fVec; +}; + template <> class SkNx<4, int> { public: @@ -109,37 +157,9 @@ public: SkNx(float val) : fVec( _mm_set1_ps(val) ) {} static SkNx Load(const float vals[4]) { return _mm_loadu_ps(vals); } - static SkNx FromBytes(const uint8_t bytes[4]) { - __m128i fix8 = _mm_cvtsi32_si128(*(const int*)bytes); - #if SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSSE3 - const char _ = ~0; // Zero these bytes. - __m128i fix8_32 = _mm_shuffle_epi8(fix8, _mm_setr_epi8(0,_,_,_, 1,_,_,_, 2,_,_,_, 3,_,_,_)); - #else - __m128i fix8_16 = _mm_unpacklo_epi8 (fix8, _mm_setzero_si128()), - fix8_32 = _mm_unpacklo_epi16(fix8_16, _mm_setzero_si128()); - #endif - return SkNx(_mm_cvtepi32_ps(fix8_32)); - // TODO: use _mm_cvtepu8_epi32 w/SSE4.1? - } - SkNx(float a, float b, float c, float d) : fVec(_mm_setr_ps(a,b,c,d)) {} void store(float vals[4]) const { _mm_storeu_ps(vals, fVec); } - void toBytes(uint8_t bytes[4]) const { - __m128i fix8_32 = _mm_cvttps_epi32(fVec), - fix8_16 = _mm_packus_epi16(fix8_32, fix8_32), - fix8 = _mm_packus_epi16(fix8_16, fix8_16); - *(int*)bytes = _mm_cvtsi128_si32(fix8); - } - - static void ToBytes(uint8_t bytes[16], - const SkNx& a, const SkNx& b, const SkNx& c, const SkNx& d) { - _mm_storeu_si128((__m128i*)bytes, - _mm_packus_epi16(_mm_packus_epi16(_mm_cvttps_epi32(a.fVec), - _mm_cvttps_epi32(b.fVec)), - _mm_packus_epi16(_mm_cvttps_epi32(c.fVec), - _mm_cvttps_epi32(d.fVec)))); - } SkNx operator + (const SkNx& o) const { return _mm_add_ps(fVec, o.fVec); } SkNx operator - (const SkNx& o) const { return _mm_sub_ps(fVec, o.fVec); } @@ -250,6 +270,34 @@ public: __m128i fVec; }; +template <> +class SkNx<4, uint8_t> { +public: + SkNx(const __m128i& vec) : fVec(vec) {} + + SkNx() {} + static SkNx Load(const uint8_t vals[4]) { return _mm_cvtsi32_si128(*(const int*)vals); } + void store(uint8_t vals[4]) const { *(int*)vals = _mm_cvtsi128_si32(fVec); } + + // TODO as needed + + __m128i fVec; +}; + +template <> +class SkNx<8, uint8_t> { +public: + SkNx(const __m128i& vec) : fVec(vec) {} + + SkNx() {} + static SkNx Load(const uint8_t vals[8]) { return _mm_loadl_epi64((const __m128i*)vals); } + void store(uint8_t vals[8]) const { _mm_storel_epi64((__m128i*)vals, fVec); } + + // TODO as needed + + __m128i fVec; +}; + template <> class SkNx<16, uint8_t> { public: @@ -294,11 +342,42 @@ public: }; -template<> -inline SkNx<4, int> SkNx_cast(const SkNx<4, float>& src) { +template<> inline Sk4i SkNx_cast(const Sk4f& src) { return _mm_cvttps_epi32(src.fVec); } +template<> inline Sk4b SkNx_cast(const Sk4f& src) { + auto _32 = _mm_cvttps_epi32(src.fVec); +#if SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSSE3 + const int _ = ~0; + return _mm_shuffle_epi8(_32, _mm_setr_epi8(0,4,8,12, _,_,_,_, _,_,_,_, _,_,_,_)); +#else + auto _16 = _mm_packus_epi16(_32, _32); + return _mm_packus_epi16(_16, _16); +#endif +} + +template<> inline Sk4f SkNx_cast(const Sk4b& src) { +#if SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSSE3 + const int _ = ~0; + auto _32 = _mm_shuffle_epi8(src.fVec, _mm_setr_epi8(0,_,_,_, 1,_,_,_, 2,_,_,_, 3,_,_,_)); +#else + auto _16 = _mm_unpacklo_epi8(src.fVec, _mm_setzero_si128()), + _32 = _mm_unpacklo_epi16(_16, _mm_setzero_si128()); +#endif + return _mm_cvtepi32_ps(_32); +} + +static inline void Sk4f_ToBytes(uint8_t bytes[16], + const Sk4f& a, const Sk4f& b, const Sk4f& c, const Sk4f& d) { + _mm_storeu_si128((__m128i*)bytes, + _mm_packus_epi16(_mm_packus_epi16(_mm_cvttps_epi32(a.fVec), + _mm_cvttps_epi32(b.fVec)), + _mm_packus_epi16(_mm_cvttps_epi32(c.fVec), + _mm_cvttps_epi32(d.fVec)))); +} + + } // namespace #endif//SkNx_sse_DEFINED diff --git a/gfx/skia/skia/src/opts/SkOpts_neon.cpp b/gfx/skia/skia/src/opts/SkOpts_neon.cpp index a0388b0654..3a07ebb765 100644 --- a/gfx/skia/skia/src/opts/SkOpts_neon.cpp +++ b/gfx/skia/skia/src/opts/SkOpts_neon.cpp @@ -15,6 +15,7 @@ #include "SkFloatingPoint_opts.h" #include "SkMatrix_opts.h" #include "SkMorphologyImageFilter_opts.h" +#include "SkSwizzler_opts.h" #include "SkTextureCompressor_opts.h" #include "SkUtils_opts.h" #include "SkXfermode_opts.h" @@ -47,5 +48,9 @@ namespace SkOpts { matrix_translate = sk_neon::matrix_translate; matrix_scale_translate = sk_neon::matrix_scale_translate; matrix_affine = sk_neon::matrix_affine; + + premul_xxxa = sk_neon::premul_xxxa; + premul_swaprb_xxxa = sk_neon::premul_swaprb_xxxa; + swaprb_xxxa = sk_neon::swaprb_xxxa; } } diff --git a/gfx/skia/skia/src/opts/SkOpts_sse2.cpp b/gfx/skia/skia/src/opts/SkOpts_sse2.cpp index 8cb5bd0274..f14b4360a9 100644 --- a/gfx/skia/skia/src/opts/SkOpts_sse2.cpp +++ b/gfx/skia/skia/src/opts/SkOpts_sse2.cpp @@ -13,6 +13,7 @@ #include "SkColorCubeFilter_opts.h" #include "SkMatrix_opts.h" #include "SkMorphologyImageFilter_opts.h" +#include "SkSwizzler_opts.h" #include "SkUtils_opts.h" #include "SkXfermode_opts.h" @@ -39,6 +40,10 @@ namespace SkOpts { matrix_translate = sk_sse2::matrix_translate; matrix_scale_translate = sk_sse2::matrix_scale_translate; matrix_affine = sk_sse2::matrix_affine; + + premul_xxxa = sk_sse2::premul_xxxa; + swaprb_xxxa = sk_sse2::swaprb_xxxa; + premul_swaprb_xxxa = sk_sse2::premul_swaprb_xxxa; } } diff --git a/gfx/skia/skia/src/opts/SkOpts_sse41.cpp b/gfx/skia/skia/src/opts/SkOpts_sse41.cpp index bbb77e3b8d..16ba87ad87 100644 --- a/gfx/skia/skia/src/opts/SkOpts_sse41.cpp +++ b/gfx/skia/skia/src/opts/SkOpts_sse41.cpp @@ -10,10 +10,183 @@ #define SK_OPTS_NS sk_sse41 #include "SkBlurImageFilter_opts.h" +#ifndef SK_SUPPORT_LEGACY_X86_BLITS + +// This file deals mostly with unpacked 8-bit values, +// i.e. values between 0 and 255, but in 16-bit lanes with 0 at the top. + +// So __m128i typically represents 1 or 2 pixels, and m128ix2 represents 4. +struct m128ix2 { __m128i lo, hi; }; + +// unpack{lo,hi}() get our raw pixels unpacked, from half of 4 packed pixels to 2 unpacked pixels. +static inline __m128i unpacklo(__m128i x) { return _mm_cvtepu8_epi16(x); } +static inline __m128i unpackhi(__m128i x) { return _mm_unpackhi_epi8(x, _mm_setzero_si128()); } + +// pack() converts back, from 4 unpacked pixels to 4 packed pixels. +static inline __m128i pack(__m128i lo, __m128i hi) { return _mm_packus_epi16(lo, hi); } + +// These nextN() functions abstract over the difference between iterating over +// an array of values and returning a constant value, for uint8_t and uint32_t. +// The nextN() taking pointers increment that pointer past where they read. +// +// nextN() returns N unpacked pixels or 4N unpacked coverage values. + +static inline __m128i next1(uint8_t val) { return _mm_set1_epi16(val); } +static inline __m128i next2(uint8_t val) { return _mm_set1_epi16(val); } +static inline m128ix2 next4(uint8_t val) { return { next2(val), next2(val) }; } + +static inline __m128i next1(uint32_t val) { return unpacklo(_mm_cvtsi32_si128(val)); } +static inline __m128i next2(uint32_t val) { return unpacklo(_mm_set1_epi32(val)); } +static inline m128ix2 next4(uint32_t val) { return { next2(val), next2(val) }; } + +static inline __m128i next1(const uint8_t*& ptr) { return _mm_set1_epi16(*ptr++); } +static inline __m128i next2(const uint8_t*& ptr) { + auto r = _mm_cvtsi32_si128(*(const uint16_t*)ptr); + ptr += 2; + const int _ = ~0; + return _mm_shuffle_epi8(r, _mm_setr_epi8(0,_,0,_,0,_,0,_, 1,_,1,_,1,_,1,_)); +} +static inline m128ix2 next4(const uint8_t*& ptr) { + auto r = _mm_cvtsi32_si128(*(const uint32_t*)ptr); + ptr += 4; + const int _ = ~0; + auto lo = _mm_shuffle_epi8(r, _mm_setr_epi8(0,_,0,_,0,_,0,_, 1,_,1,_,1,_,1,_)), + hi = _mm_shuffle_epi8(r, _mm_setr_epi8(2,_,2,_,2,_,2,_, 3,_,3,_,3,_,3,_)); + return { lo, hi }; +} + +static inline __m128i next1(const uint32_t*& ptr) { return unpacklo(_mm_cvtsi32_si128(*ptr++)); } +static inline __m128i next2(const uint32_t*& ptr) { + auto r = unpacklo(_mm_loadl_epi64((const __m128i*)ptr)); + ptr += 2; + return r; +} +static inline m128ix2 next4(const uint32_t*& ptr) { + auto packed = _mm_loadu_si128((const __m128i*)ptr); + ptr += 4; + return { unpacklo(packed), unpackhi(packed) }; +} + +// Divide by 255 with rounding. +// (x+127)/255 == ((x+128)*257)>>16. +// Sometimes we can be more efficient by breaking this into two parts. +static inline __m128i div255_part1(__m128i x) { return _mm_add_epi16(x, _mm_set1_epi16(128)); } +static inline __m128i div255_part2(__m128i x) { return _mm_mulhi_epu16(x, _mm_set1_epi16(257)); } +static inline __m128i div255(__m128i x) { return div255_part2(div255_part1(x)); } + +// (x*y+127)/255, a byte multiply. +static inline __m128i scale(__m128i x, __m128i y) { + return div255(_mm_mullo_epi16(x, y)); +} + +// (255 - x). +static inline __m128i inv(__m128i x) { + return _mm_xor_si128(_mm_set1_epi16(0x00ff), x); // This seems a bit faster than _mm_sub_epi16. +} + +// ARGB argb -> AAAA aaaa +static inline __m128i alphas(__m128i px) { + const int a = 2 * (SK_A32_SHIFT/8); // SK_A32_SHIFT is typically 24, so this is typically 6. + const int _ = ~0; + return _mm_shuffle_epi8(px, _mm_setr_epi8(a+0,_,a+0,_,a+0,_,a+0,_, a+8,_,a+8,_,a+8,_,a+8,_)); +} + +// For i = 0...n, tgt = fn(dst,src,cov), where Dst,Src,and Cov can be constants or arrays. +template +static inline void loop(int n, uint32_t* t, const Dst dst, const Src src, const Cov cov, Fn&& fn) { + // We don't want to muck with the callers' pointers, so we make them const and copy here. + Dst d = dst; + Src s = src; + Cov c = cov; + + // Writing this as a single while-loop helps hoist loop invariants from fn. + while (n) { + if (n >= 4) { + auto d4 = next4(d), + s4 = next4(s), + c4 = next4(c); + auto lo = fn(d4.lo, s4.lo, c4.lo), + hi = fn(d4.hi, s4.hi, c4.hi); + _mm_storeu_si128((__m128i*)t, pack(lo,hi)); + t += 4; + n -= 4; + continue; + } + if (n & 2) { + auto r = fn(next2(d), next2(s), next2(c)); + _mm_storel_epi64((__m128i*)t, pack(r,r)); + t += 2; + } + if (n & 1) { + auto r = fn(next1(d), next1(s), next1(c)); + *t = _mm_cvtsi128_si32(pack(r,r)); + } + return; + } +} + +namespace sk_sse41 { + +// SrcOver, with a constant source and full coverage. +static void blit_row_color32(SkPMColor* tgt, const SkPMColor* dst, int n, SkPMColor src) { + // We want to calculate s + (d * inv(alphas(s)) + 127)/255. + // We'd generally do that div255 as s + ((d * inv(alphas(s)) + 128)*257)>>16. + + // But we can go one step further to ((s*255 + 128 + d*inv(alphas(s)))*257)>>16. + // This lets us hoist (s*255+128) and inv(alphas(s)) out of the loop. + __m128i s = next2(src), + s_255_128 = div255_part1(_mm_mullo_epi16(s, _mm_set1_epi16(255))), + A = inv(alphas(s)); + + const uint8_t cov = 0xff; + loop(n, tgt, dst, src, cov, [=](__m128i d, __m128i, __m128i) { + return div255_part2(_mm_add_epi16(s_255_128, _mm_mullo_epi16(d, A))); + }); +} + +// SrcOver, with a constant source and variable coverage. +// If the source is opaque, SrcOver becomes Src. +static void blit_mask_d32_a8(SkPMColor* dst, size_t dstRB, + const SkAlpha* cov, size_t covRB, + SkColor color, int w, int h) { + if (SkColorGetA(color) == 0xFF) { + const SkPMColor src = SkSwizzle_BGRA_to_PMColor(color); + while (h --> 0) { + loop(w, dst, (const SkPMColor*)dst, src, cov, [](__m128i d, __m128i s, __m128i c) { + // Src blend mode: a simple lerp from d to s by c. + // TODO: try a pmaddubsw version? + return div255(_mm_add_epi16(_mm_mullo_epi16(inv(c),d), _mm_mullo_epi16(c,s))); + }); + dst += dstRB / sizeof(*dst); + cov += covRB / sizeof(*cov); + } + } else { + const SkPMColor src = SkPreMultiplyColor(color); + while (h --> 0) { + loop(w, dst, (const SkPMColor*)dst, src, cov, [](__m128i d, __m128i s, __m128i c) { + // SrcOver blend mode, with coverage folded into source alpha. + __m128i sc = scale(s,c), + AC = inv(alphas(sc)); + return _mm_add_epi16(sc, scale(d,AC)); + }); + dst += dstRB / sizeof(*dst); + cov += covRB / sizeof(*cov); + } + } +} + +} // namespace sk_sse41 +#endif + namespace SkOpts { void Init_sse41() { box_blur_xx = sk_sse41::box_blur_xx; box_blur_xy = sk_sse41::box_blur_xy; box_blur_yx = sk_sse41::box_blur_yx; + + #ifndef SK_SUPPORT_LEGACY_X86_BLITS + blit_row_color32 = sk_sse41::blit_row_color32; + blit_mask_d32_a8 = sk_sse41::blit_mask_d32_a8; + #endif } } diff --git a/gfx/skia/skia/src/opts/SkPx_neon.h b/gfx/skia/skia/src/opts/SkPx_neon.h deleted file mode 100644 index 23a0934ab2..0000000000 --- a/gfx/skia/skia/src/opts/SkPx_neon.h +++ /dev/null @@ -1,188 +0,0 @@ -/* - * Copyright 2015 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#ifndef SkPx_neon_DEFINED -#define SkPx_neon_DEFINED - -// When we have NEON, we like to work 8 pixels at a time. -// This lets us exploit vld4/vst4 and represent SkPx as planar uint8x8x4_t, -// Wide as planar uint16x8x4_t, and Alpha as a single uint8x8_t plane. - -namespace neon { - -struct SkPx { - static const int N = 8; - - uint8x8x4_t fVec; - SkPx(uint8x8x4_t vec) : fVec(vec) {} - - static SkPx Dup(uint32_t px) { return vld4_dup_u8((const uint8_t*)&px); } - static SkPx Load(const uint32_t* px) { return vld4_u8((const uint8_t*)px); } - static SkPx Load(const uint32_t* px, int n) { - SkASSERT(0 < n && n < 8); - uint8x8x4_t v = vld4_dup_u8((const uint8_t*)px); // n>=1, so start all lanes with pixel 0. - switch (n) { - case 7: v = vld4_lane_u8((const uint8_t*)(px+6), v, 6); // fall through - case 6: v = vld4_lane_u8((const uint8_t*)(px+5), v, 5); // fall through - case 5: v = vld4_lane_u8((const uint8_t*)(px+4), v, 4); // fall through - case 4: v = vld4_lane_u8((const uint8_t*)(px+3), v, 3); // fall through - case 3: v = vld4_lane_u8((const uint8_t*)(px+2), v, 2); // fall through - case 2: v = vld4_lane_u8((const uint8_t*)(px+1), v, 1); - } - return v; - } - - void store(uint32_t* px) const { vst4_u8((uint8_t*)px, fVec); } - void store(uint32_t* px, int n) const { - SkASSERT(0 < n && n < 8); - switch (n) { - case 7: vst4_lane_u8((uint8_t*)(px+6), fVec, 6); - case 6: vst4_lane_u8((uint8_t*)(px+5), fVec, 5); - case 5: vst4_lane_u8((uint8_t*)(px+4), fVec, 4); - case 4: vst4_lane_u8((uint8_t*)(px+3), fVec, 3); - case 3: vst4_lane_u8((uint8_t*)(px+2), fVec, 2); - case 2: vst4_lane_u8((uint8_t*)(px+1), fVec, 1); - case 1: vst4_lane_u8((uint8_t*)(px+0), fVec, 0); - } - } - - struct Alpha { - uint8x8_t fA; - Alpha(uint8x8_t a) : fA(a) {} - - static Alpha Dup(uint8_t a) { return vdup_n_u8(a); } - static Alpha Load(const uint8_t* a) { return vld1_u8(a); } - static Alpha Load(const uint8_t* a, int n) { - SkASSERT(0 < n && n < 8); - uint8x8_t v = vld1_dup_u8(a); // n>=1, so start all lanes with alpha 0. - switch (n) { - case 7: v = vld1_lane_u8(a+6, v, 6); // fall through - case 6: v = vld1_lane_u8(a+5, v, 5); // fall through - case 5: v = vld1_lane_u8(a+4, v, 4); // fall through - case 4: v = vld1_lane_u8(a+3, v, 3); // fall through - case 3: v = vld1_lane_u8(a+2, v, 2); // fall through - case 2: v = vld1_lane_u8(a+1, v, 1); - } - return v; - } - Alpha inv() const { return vsub_u8(vdup_n_u8(255), fA); } - }; - - struct Wide { - uint16x8x4_t fVec; - Wide(uint16x8x4_t vec) : fVec(vec) {} - - Wide operator+(const Wide& o) const { - return (uint16x8x4_t) {{ - vaddq_u16(fVec.val[0], o.fVec.val[0]), - vaddq_u16(fVec.val[1], o.fVec.val[1]), - vaddq_u16(fVec.val[2], o.fVec.val[2]), - vaddq_u16(fVec.val[3], o.fVec.val[3]), - }}; - } - Wide operator-(const Wide& o) const { - return (uint16x8x4_t) {{ - vsubq_u16(fVec.val[0], o.fVec.val[0]), - vsubq_u16(fVec.val[1], o.fVec.val[1]), - vsubq_u16(fVec.val[2], o.fVec.val[2]), - vsubq_u16(fVec.val[3], o.fVec.val[3]), - }}; - } - - template Wide shl() const { - return (uint16x8x4_t) {{ - vshlq_n_u16(fVec.val[0], bits), - vshlq_n_u16(fVec.val[1], bits), - vshlq_n_u16(fVec.val[2], bits), - vshlq_n_u16(fVec.val[3], bits), - }}; - } - template Wide shr() const { - return (uint16x8x4_t) {{ - vshrq_n_u16(fVec.val[0], bits), - vshrq_n_u16(fVec.val[1], bits), - vshrq_n_u16(fVec.val[2], bits), - vshrq_n_u16(fVec.val[3], bits), - }}; - } - - SkPx addNarrowHi(const SkPx& o) const { - return (uint8x8x4_t) {{ - vshrn_n_u16(vaddw_u8(fVec.val[0], o.fVec.val[0]), 8), - vshrn_n_u16(vaddw_u8(fVec.val[1], o.fVec.val[1]), 8), - vshrn_n_u16(vaddw_u8(fVec.val[2], o.fVec.val[2]), 8), - vshrn_n_u16(vaddw_u8(fVec.val[3], o.fVec.val[3]), 8), - }}; - } - }; - - Alpha alpha() const { return fVec.val[3]; } - - Wide widenLo() const { - return (uint16x8x4_t) {{ - vmovl_u8(fVec.val[0]), - vmovl_u8(fVec.val[1]), - vmovl_u8(fVec.val[2]), - vmovl_u8(fVec.val[3]), - }}; - } - // TODO: these two can probably be done faster. - Wide widenHi() const { return this->widenLo().shl<8>(); } - Wide widenLoHi() const { return this->widenLo() + this->widenHi(); } - - SkPx operator+(const SkPx& o) const { - return (uint8x8x4_t) {{ - vadd_u8(fVec.val[0], o.fVec.val[0]), - vadd_u8(fVec.val[1], o.fVec.val[1]), - vadd_u8(fVec.val[2], o.fVec.val[2]), - vadd_u8(fVec.val[3], o.fVec.val[3]), - }}; - } - SkPx operator-(const SkPx& o) const { - return (uint8x8x4_t) {{ - vsub_u8(fVec.val[0], o.fVec.val[0]), - vsub_u8(fVec.val[1], o.fVec.val[1]), - vsub_u8(fVec.val[2], o.fVec.val[2]), - vsub_u8(fVec.val[3], o.fVec.val[3]), - }}; - } - SkPx saturatedAdd(const SkPx& o) const { - return (uint8x8x4_t) {{ - vqadd_u8(fVec.val[0], o.fVec.val[0]), - vqadd_u8(fVec.val[1], o.fVec.val[1]), - vqadd_u8(fVec.val[2], o.fVec.val[2]), - vqadd_u8(fVec.val[3], o.fVec.val[3]), - }}; - } - - Wide operator*(const Alpha& a) const { - return (uint16x8x4_t) {{ - vmull_u8(fVec.val[0], a.fA), - vmull_u8(fVec.val[1], a.fA), - vmull_u8(fVec.val[2], a.fA), - vmull_u8(fVec.val[3], a.fA), - }}; - } - SkPx approxMulDiv255(const Alpha& a) const { - return (*this * a).addNarrowHi(*this); - } - - SkPx addAlpha(const Alpha& a) const { - return (uint8x8x4_t) {{ - fVec.val[0], - fVec.val[1], - fVec.val[2], - vadd_u8(fVec.val[3], a.fA), - }}; - } -}; - -} // namespace neon - -typedef neon::SkPx SkPx; - -#endif//SkPx_neon_DEFINED diff --git a/gfx/skia/skia/src/opts/SkPx_none.h b/gfx/skia/skia/src/opts/SkPx_none.h deleted file mode 100644 index 8217eaef3c..0000000000 --- a/gfx/skia/skia/src/opts/SkPx_none.h +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright 2015 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#ifndef SkPx_none_DEFINED -#define SkPx_none_DEFINED - -// Nothing fancy here. We're the backup _none case after all. -// Our declared sweet spot is simply a single pixel at a time. - -namespace none { - -struct SkPx { - static const int N = 1; - uint8_t f8[4]; - - SkPx(uint32_t px) { memcpy(f8, &px, 4); } - SkPx(uint8_t x, uint8_t y, uint8_t z, uint8_t a) { - f8[0] = x; f8[1] = y; f8[2] = z; f8[3] = a; - } - - static SkPx Dup(uint32_t px) { return px; } - static SkPx Load(const uint32_t* px) { return *px; } - static SkPx Load(const uint32_t* px, int n) { - SkASSERT(false); // There are no 0 Wide shl() const { - return Wide(f16[0]< Wide shr() const { - return Wide(f16[0]>>bits, f16[1]>>bits, f16[2]>>bits, f16[3]>>bits); - } - - SkPx addNarrowHi(const SkPx& o) const { - Wide sum = (*this + o.widenLo()).shr<8>(); - return SkPx(sum.f16[0], sum.f16[1], sum.f16[2], sum.f16[3]); - } - }; - - Alpha alpha() const { return f8[3]; } - - Wide widenLo() const { return Wide(f8[0], f8[1], f8[2], f8[3]); } - Wide widenHi() const { return this->widenLo().shl<8>(); } - Wide widenLoHi() const { return this->widenLo() + this->widenHi(); } - - SkPx operator+(const SkPx& o) const { - return SkPx(f8[0]+o.f8[0], f8[1]+o.f8[1], f8[2]+o.f8[2], f8[3]+o.f8[3]); - } - SkPx operator-(const SkPx& o) const { - return SkPx(f8[0]-o.f8[0], f8[1]-o.f8[1], f8[2]-o.f8[2], f8[3]-o.f8[3]); - } - SkPx saturatedAdd(const SkPx& o) const { - return SkPx(SkTMax(0, SkTMin(255, f8[0]+o.f8[0])), - SkTMax(0, SkTMin(255, f8[1]+o.f8[1])), - SkTMax(0, SkTMin(255, f8[2]+o.f8[2])), - SkTMax(0, SkTMin(255, f8[3]+o.f8[3]))); - } - - Wide operator*(const Alpha& a) const { - return Wide(f8[0]*a.fA, f8[1]*a.fA, f8[2]*a.fA, f8[3]*a.fA); - } - SkPx approxMulDiv255(const Alpha& a) const { - return (*this * a).addNarrowHi(*this); - } - - SkPx addAlpha(const Alpha& a) const { - return SkPx(f8[0], f8[1], f8[2], f8[3]+a.fA); - } -}; - -} // namespace none - -typedef none::SkPx SkPx; - -#endif//SkPx_none_DEFINED diff --git a/gfx/skia/skia/src/opts/SkPx_sse.h b/gfx/skia/skia/src/opts/SkPx_sse.h deleted file mode 100644 index 2560946edb..0000000000 --- a/gfx/skia/skia/src/opts/SkPx_sse.h +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright 2015 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#ifndef SkPx_sse_DEFINED -#define SkPx_sse_DEFINED - -// sse::SkPx's sweet spot is to work with 4 pixels at a time, -// stored interlaced, just as they sit in memory: rgba rgba rgba rgba. - -// sse::SkPx's best way to work with alphas is similar, -// replicating the 4 alphas 4 times each across the pixel: aaaa aaaa aaaa aaaa. - -// When working with fewer than 4 pixels, we load the pixels in the low lanes, -// usually filling the top lanes with zeros (but who cares, might be junk). - -namespace sse { - -struct SkPx { - static const int N = 4; - - __m128i fVec; - SkPx(__m128i vec) : fVec(vec) {} - - static SkPx Dup(uint32_t px) { return _mm_set1_epi32(px); } - static SkPx Load(const uint32_t* px) { return _mm_loadu_si128((const __m128i*)px); } - static SkPx Load(const uint32_t* px, int n) { - SkASSERT(n > 0 && n < 4); - switch (n) { - case 1: return _mm_cvtsi32_si128(px[0]); - case 2: return _mm_loadl_epi64((const __m128i*)px); - case 3: return _mm_or_si128(_mm_loadl_epi64((const __m128i*)px), - _mm_slli_si128(_mm_cvtsi32_si128(px[2]), 8)); - } - return _mm_setzero_si128(); // Not actually reachable. - } - - void store(uint32_t* px) const { _mm_storeu_si128((__m128i*)px, fVec); } - void store(uint32_t* px, int n) const { - SkASSERT(n > 0 && n < 4); - __m128i v = fVec; - if (n & 1) { - *px++ = _mm_cvtsi128_si32(v); - v = _mm_srli_si128(v, 4); - } - if (n & 2) { - _mm_storel_epi64((__m128i*)px, v); - } - } - - struct Alpha { - __m128i fVec; - Alpha(__m128i vec) : fVec(vec) {} - - static Alpha Dup(uint8_t a) { return _mm_set1_epi8(a); } - static Alpha Load(const uint8_t* a) { - __m128i as = _mm_cvtsi32_si128(*(const uint32_t*)a); // ____ ____ ____ 3210 - #if SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSSE3 - return _mm_shuffle_epi8(as, _mm_set_epi8(3,3,3,3, 2,2,2,2, 1,1,1,1, 0,0,0,0)); - #else - as = _mm_unpacklo_epi8 (as, as); // ____ ____ 3322 1100 - as = _mm_unpacklo_epi16(as, as); // 3333 2222 1111 0000 - return as; - #endif - } - static Alpha Load(const uint8_t* a, int n) { - SkASSERT(n > 0 && n < 4); - uint8_t a4[] = { 0,0,0,0 }; - switch (n) { - case 3: a4[2] = a[2]; // fall through - case 2: a4[1] = a[1]; // fall through - case 1: a4[0] = a[0]; - } - return Load(a4); - } - - Alpha inv() const { return _mm_sub_epi8(_mm_set1_epi8(~0), fVec); } - }; - - struct Wide { - __m128i fLo, fHi; - Wide(__m128i lo, __m128i hi) : fLo(lo), fHi(hi) {} - - Wide operator+(const Wide& o) const { - return Wide(_mm_add_epi16(fLo, o.fLo), _mm_add_epi16(fHi, o.fHi)); - } - Wide operator-(const Wide& o) const { - return Wide(_mm_sub_epi16(fLo, o.fLo), _mm_sub_epi16(fHi, o.fHi)); - } - template Wide shl() const { - return Wide(_mm_slli_epi16(fLo, bits), _mm_slli_epi16(fHi, bits)); - } - template Wide shr() const { - return Wide(_mm_srli_epi16(fLo, bits), _mm_srli_epi16(fHi, bits)); - } - - SkPx addNarrowHi(const SkPx& o) const { - Wide sum = (*this + o.widenLo()).shr<8>(); - return _mm_packus_epi16(sum.fLo, sum.fHi); - } - }; - - Alpha alpha() const { - #if SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSSE3 - return _mm_shuffle_epi8(fVec, _mm_set_epi8(15,15,15,15, 11,11,11,11, 7,7,7,7, 3,3,3,3)); - #else - // We exploit that A >= rgb for any premul pixel. - __m128i as = fVec; // 3xxx 2xxx 1xxx 0xxx - as = _mm_max_epu8(as, _mm_srli_epi32(as, 8)); // 33xx 22xx 11xx 00xx - as = _mm_max_epu8(as, _mm_srli_epi32(as, 16)); // 3333 2222 1111 0000 - return as; - #endif - } - - Wide widenLo() const { - return Wide(_mm_unpacklo_epi8(fVec, _mm_setzero_si128()), - _mm_unpackhi_epi8(fVec, _mm_setzero_si128())); - } - Wide widenHi() const { - return Wide(_mm_unpacklo_epi8(_mm_setzero_si128(), fVec), - _mm_unpackhi_epi8(_mm_setzero_si128(), fVec)); - } - Wide widenLoHi() const { - return Wide(_mm_unpacklo_epi8(fVec, fVec), - _mm_unpackhi_epi8(fVec, fVec)); - } - - SkPx operator+(const SkPx& o) const { return _mm_add_epi8(fVec, o.fVec); } - SkPx operator-(const SkPx& o) const { return _mm_sub_epi8(fVec, o.fVec); } - SkPx saturatedAdd(const SkPx& o) const { return _mm_adds_epi8(fVec, o.fVec); } - - Wide operator*(const Alpha& a) const { - __m128i pLo = _mm_unpacklo_epi8( fVec, _mm_setzero_si128()), - aLo = _mm_unpacklo_epi8(a.fVec, _mm_setzero_si128()), - pHi = _mm_unpackhi_epi8( fVec, _mm_setzero_si128()), - aHi = _mm_unpackhi_epi8(a.fVec, _mm_setzero_si128()); - return Wide(_mm_mullo_epi16(pLo, aLo), _mm_mullo_epi16(pHi, aHi)); - } - SkPx approxMulDiv255(const Alpha& a) const { - return (*this * a).addNarrowHi(*this); - } - - SkPx addAlpha(const Alpha& a) const { - return _mm_add_epi8(fVec, _mm_and_si128(a.fVec, _mm_set1_epi32(0xFF000000))); - } -}; - -} // namespace sse - -typedef sse::SkPx SkPx; - -#endif//SkPx_sse_DEFINED diff --git a/gfx/skia/skia/src/opts/SkSwizzler_opts.h b/gfx/skia/skia/src/opts/SkSwizzler_opts.h new file mode 100644 index 0000000000..467e5d1940 --- /dev/null +++ b/gfx/skia/skia/src/opts/SkSwizzler_opts.h @@ -0,0 +1,153 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkSwizzler_opts_DEFINED +#define SkSwizzler_opts_DEFINED + +#include "SkColorPriv.h" + +namespace SK_OPTS_NS { + +// These variable names in these functions just pretend the input is BGRA. +// They work fine with both RGBA and BGRA. + +static void premul_xxxa_portable(uint32_t dst[], const uint32_t src[], int count) { + for (int i = 0; i < count; i++) { + uint8_t a = src[i] >> 24, + r = src[i] >> 16, + g = src[i] >> 8, + b = src[i] >> 0; + r = (r*a+127)/255; + g = (g*a+127)/255; + b = (b*a+127)/255; + dst[i] = (uint32_t)a << 24 + | (uint32_t)r << 16 + | (uint32_t)g << 8 + | (uint32_t)b << 0; + } +} + +static void premul_swaprb_xxxa_portable(uint32_t dst[], const uint32_t src[], int count) { + for (int i = 0; i < count; i++) { + uint8_t a = src[i] >> 24, + r = src[i] >> 16, + g = src[i] >> 8, + b = src[i] >> 0; + r = (r*a+127)/255; + g = (g*a+127)/255; + b = (b*a+127)/255; + dst[i] = (uint32_t)a << 24 + | (uint32_t)b << 16 + | (uint32_t)g << 8 + | (uint32_t)r << 0; + } +} + +#if defined(SK_ARM_HAS_NEON) + +// Rounded divide by 255, (x + 127) / 255 +static uint8x8_t div255_round(uint16x8_t x) { + // result = (x + 127) / 255 + // result = (x + 127) / 256 + error1 + // + // error1 = (x + 127) / (255 * 256) + // error1 = (x + 127) / (256 * 256) + error2 + // + // error2 = (x + 127) / (255 * 256 * 256) + // + // The maximum value of error2 is too small to matter. Thus: + // result = (x + 127) / 256 + (x + 127) / (256 * 256) + // result = ((x + 127) / 256 + x + 127) / 256 + // result = ((x + 127) >> 8 + x + 127) >> 8 + // + // Use >>> to represent "rounded right shift" which, conveniently, + // NEON supports in one instruction. + // result = ((x >>> 8) + x) >>> 8 + // + // Note that the second right shift is actually performed as an + // "add, round, and narrow back to 8-bits" instruction. + return vraddhn_u16(x, vrshrq_n_u16(x, 8)); +} + +// Scale a byte by another, (x * y + 127) / 255 +static uint8x8_t scale(uint8x8_t x, uint8x8_t y) { + return div255_round(vmull_u8(x, y)); +} + +template +static void premul_xxxa_should_swaprb(uint32_t dst[], const uint32_t src[], int count) { + while (count >= 8) { + // Load 8 pixels. + uint8x8x4_t bgra = vld4_u8((const uint8_t*) src); + + uint8x8_t a = bgra.val[3], + r = bgra.val[2], + g = bgra.val[1], + b = bgra.val[0]; + + // Premultiply. + r = scale(r, a); + g = scale(g, a); + b = scale(b, a); + + // Store 8 premultiplied pixels. + if (kSwapRB) { + bgra.val[2] = b; + bgra.val[1] = g; + bgra.val[0] = r; + } else { + bgra.val[2] = r; + bgra.val[1] = g; + bgra.val[0] = b; + } + vst4_u8((uint8_t*) dst, bgra); + src += 8; + dst += 8; + count -= 8; + } + + // Call portable code to finish up the tail of [0,8) pixels. + auto proc = kSwapRB ? premul_swaprb_xxxa_portable : premul_xxxa_portable; + proc(dst, src, count); +} + +static void premul_xxxa(uint32_t dst[], const uint32_t src[], int count) { + premul_xxxa_should_swaprb(dst, src, count); +} + +static void premul_swaprb_xxxa(uint32_t dst[], const uint32_t src[], int count) { + premul_xxxa_should_swaprb(dst, src, count); +} + +#else + +static void premul_xxxa(uint32_t dst[], const uint32_t src[], int count) { + premul_xxxa_portable(dst, src, count); +} + +static void premul_swaprb_xxxa(uint32_t dst[], const uint32_t src[], int count) { + premul_swaprb_xxxa_portable(dst, src, count); +} + +#endif + +static void swaprb_xxxa(uint32_t dst[], const uint32_t src[], int count) { + for (int i = 0; i < count; i++) { + uint8_t a = src[i] >> 24, + r = src[i] >> 16, + g = src[i] >> 8, + b = src[i] >> 0; + dst[i] = (uint32_t)a << 24 + | (uint32_t)b << 16 + | (uint32_t)g << 8 + | (uint32_t)r << 0; + } +} + +} + +#endif // SkSwizzler_opts_DEFINED diff --git a/gfx/skia/skia/src/opts/SkXfermode_opts.h b/gfx/skia/skia/src/opts/SkXfermode_opts.h index 5a037ceb03..7b72bc3f80 100644 --- a/gfx/skia/skia/src/opts/SkXfermode_opts.h +++ b/gfx/skia/skia/src/opts/SkXfermode_opts.h @@ -15,7 +15,9 @@ namespace { // Most xfermodes can be done most efficiently 4 pixels at a time in 8 or 16-bit fixed point. -#define XFERMODE(Name) inline Sk4px Name(const Sk4px& d, const Sk4px& s) +#define XFERMODE(Xfermode) \ + struct Xfermode { Sk4px operator()(const Sk4px&, const Sk4px&) const; }; \ + inline Sk4px Xfermode::operator()(const Sk4px& d, const Sk4px& s) const XFERMODE(Clear) { return Sk4px::DupPMColor(0); } XFERMODE(Src) { return s; } @@ -23,13 +25,13 @@ XFERMODE(Dst) { return d; } XFERMODE(SrcIn) { return s.approxMulDiv255(d.alphas() ); } XFERMODE(SrcOut) { return s.approxMulDiv255(d.alphas().inv()); } XFERMODE(SrcOver) { return s + d.approxMulDiv255(s.alphas().inv()); } -XFERMODE(DstIn) { return SrcIn (s,d); } -XFERMODE(DstOut) { return SrcOut (s,d); } -XFERMODE(DstOver) { return SrcOver(s,d); } +XFERMODE(DstIn) { return SrcIn ()(s,d); } +XFERMODE(DstOut) { return SrcOut ()(s,d); } +XFERMODE(DstOver) { return SrcOver()(s,d); } // [ S * Da + (1 - Sa) * D] XFERMODE(SrcATop) { return (s * d.alphas() + d * s.alphas().inv()).div255(); } -XFERMODE(DstATop) { return SrcATop(s,d); } +XFERMODE(DstATop) { return SrcATop()(s,d); } //[ S * (1 - Da) + (1 - Sa) * D ] XFERMODE(Xor) { return (s * d.alphas().inv() + d * s.alphas().inv()).div255(); } // [S + D ] @@ -79,7 +81,7 @@ XFERMODE(HardLight) { auto colors = (both + isLite.thenElse(lite, dark)).div255(); return alphas.zeroColors() + colors.zeroAlphas(); } -XFERMODE(Overlay) { return HardLight(s,d); } +XFERMODE(Overlay) { return HardLight()(s,d); } XFERMODE(Darken) { auto sa = s.alphas(), @@ -110,7 +112,9 @@ XFERMODE(Lighten) { #undef XFERMODE // Some xfermodes use math like divide or sqrt that's best done in floats 1 pixel at a time. -#define XFERMODE(Name) inline Sk4f Name(const Sk4f& d, const Sk4f& s) +#define XFERMODE(Xfermode) \ + struct Xfermode { Sk4f operator()(const Sk4f&, const Sk4f&) const; }; \ + inline Sk4f Xfermode::operator()(const Sk4f& d, const Sk4f& s) const static inline Sk4f a_rgb(const Sk4f& a, const Sk4f& rgb) { static_assert(SK_A32_SHIFT == 24, ""); @@ -181,15 +185,15 @@ XFERMODE(SoftLight) { // A reasonable fallback mode for doing AA is to simply apply the transfermode first, // then linearly interpolate the AA. -template -inline Sk4px xfer_aa(const Sk4px& d, const Sk4px& s, const Sk4px& aa) { - Sk4px bw = Mode(d, s); +template +static Sk4px xfer_aa(const Sk4px& d, const Sk4px& s, const Sk4px& aa) { + Sk4px bw = Xfermode()(d, s); return (bw * aa + d * aa.inv()).div255(); } // For some transfermodes we specialize AA, either for correctness or performance. -#define XFERMODE_AA(Name) \ - template <> inline Sk4px xfer_aa(const Sk4px& d, const Sk4px& s, const Sk4px& aa) +#define XFERMODE_AA(Xfermode) \ + template <> Sk4px xfer_aa(const Sk4px& d, const Sk4px& s, const Sk4px& aa) // Plus' clamp needs to happen after AA. skia:3852 XFERMODE_AA(Plus) { // [ clamp( (1-AA)D + (AA)(S+D) ) == clamp(D + AA*S) ] @@ -198,8 +202,7 @@ XFERMODE_AA(Plus) { // [ clamp( (1-AA)D + (AA)(S+D) ) == clamp(D + AA*S) ] #undef XFERMODE_AA -template +template class Sk4pxXfermode : public SkProcCoeffXfermode { public: Sk4pxXfermode(const ProcCoeff& rec, SkXfermode::Mode mode) @@ -207,17 +210,41 @@ public: void xfer32(SkPMColor dst[], const SkPMColor src[], int n, const SkAlpha aa[]) const override { if (nullptr == aa) { - Sk4px::MapDstSrc(n, dst, src, Proc4); + Sk4px::MapDstSrc(n, dst, src, Xfermode()); } else { - Sk4px::MapDstSrcAlpha(n, dst, src, aa, AAProc4); + Sk4px::MapDstSrcAlpha(n, dst, src, aa, xfer_aa); } } void xfer16(uint16_t dst[], const SkPMColor src[], int n, const SkAlpha aa[]) const override { - if (nullptr == aa) { - Sk4px::MapDstSrc(n, dst, src, Proc4); - } else { - Sk4px::MapDstSrcAlpha(n, dst, src, aa, AAProc4); + SkPMColor dst32[4]; + while (n >= 4) { + dst32[0] = SkPixel16ToPixel32(dst[0]); + dst32[1] = SkPixel16ToPixel32(dst[1]); + dst32[2] = SkPixel16ToPixel32(dst[2]); + dst32[3] = SkPixel16ToPixel32(dst[3]); + + this->xfer32(dst32, src, 4, aa); + + dst[0] = SkPixel32ToPixel16(dst32[0]); + dst[1] = SkPixel32ToPixel16(dst32[1]); + dst[2] = SkPixel32ToPixel16(dst32[2]); + dst[3] = SkPixel32ToPixel16(dst32[3]); + + dst += 4; + src += 4; + aa += aa ? 4 : 0; + n -= 4; + } + while (n) { + SkPMColor dst32 = SkPixel16ToPixel32(*dst); + this->xfer32(&dst32, src, 1, aa); + *dst = SkPixel32ToPixel16(dst32); + + dst += 1; + src += 1; + aa += aa ? 1 : 0; + n -= 1; } } @@ -225,7 +252,7 @@ private: typedef SkProcCoeffXfermode INHERITED; }; -template +template class Sk4fXfermode : public SkProcCoeffXfermode { public: Sk4fXfermode(const ProcCoeff& rec, SkXfermode::Mode mode) @@ -233,41 +260,38 @@ public: void xfer32(SkPMColor dst[], const SkPMColor src[], int n, const SkAlpha aa[]) const override { for (int i = 0; i < n; i++) { - dst[i] = aa ? this->xfer32(dst[i], src[i], aa[i]) - : this->xfer32(dst[i], src[i]); + dst[i] = Xfer32_1(dst[i], src[i], aa ? aa+i : nullptr); } } void xfer16(uint16_t dst[], const SkPMColor src[], int n, const SkAlpha aa[]) const override { for (int i = 0; i < n; i++) { SkPMColor dst32 = SkPixel16ToPixel32(dst[i]); - dst32 = aa ? this->xfer32(dst32, src[i], aa[i]) - : this->xfer32(dst32, src[i]); + dst32 = Xfer32_1(dst32, src[i], aa ? aa+i : nullptr); dst[i] = SkPixel32ToPixel16(dst32); } } private: - static Sk4f Load(SkPMColor c) { - return Sk4f::FromBytes((uint8_t*)&c) * Sk4f(1.0f/255); - } - static SkPMColor Round(const Sk4f& f) { - SkPMColor c; - (f * Sk4f(255) + Sk4f(0.5f)).toBytes((uint8_t*)&c); - return c; - } - inline SkPMColor xfer32(SkPMColor dst, SkPMColor src) const { - return Round(ProcF(Load(dst), Load(src))); + static SkPMColor Xfer32_1(SkPMColor dst, const SkPMColor src, const SkAlpha* aa) { + Sk4f d = Load(dst), + s = Load(src), + b = Xfermode()(d, s); + if (aa) { + Sk4f a = Sk4f(*aa) * Sk4f(1.0f/255); + b = b*a + d*(Sk4f(1)-a); + } + return Round(b); } - inline SkPMColor xfer32(SkPMColor dst, SkPMColor src, SkAlpha aa) const { - Sk4f s(Load(src)), - d(Load(dst)), - b(ProcF(d,s)); - // We do aa in full float precision before going back down to bytes, because we can! - Sk4f a = Sk4f(aa) * Sk4f(1.0f/255); - b = b*a + d*(Sk4f(1)-a); - return Round(b); + static Sk4f Load(SkPMColor c) { + return SkNx_cast(Sk4b::Load((uint8_t*)&c)) * Sk4f(1.0f/255); + } + + static SkPMColor Round(const Sk4f& f) { + SkPMColor c; + SkNx_cast(f * Sk4f(255) + Sk4f(0.5f)).store((uint8_t*)&c); + return c; } typedef SkProcCoeffXfermode INHERITED; @@ -279,8 +303,8 @@ namespace SK_OPTS_NS { static SkXfermode* create_xfermode(const ProcCoeff& rec, SkXfermode::Mode mode) { switch (mode) { -#define CASE(Mode) \ - case SkXfermode::k##Mode##_Mode: return new Sk4pxXfermode >(rec, mode) +#define CASE(Xfermode) \ + case SkXfermode::k##Xfermode##_Mode: return new Sk4pxXfermode(rec, mode) CASE(Clear); CASE(Src); CASE(Dst); @@ -305,8 +329,8 @@ static SkXfermode* create_xfermode(const ProcCoeff& rec, SkXfermode::Mode mode) CASE(Lighten); #undef CASE -#define CASE(Mode) \ - case SkXfermode::k##Mode##_Mode: return new Sk4fXfermode(rec, mode) +#define CASE(Xfermode) \ + case SkXfermode::k##Xfermode##_Mode: return new Sk4fXfermode(rec, mode) CASE(ColorDodge); CASE(ColorBurn); CASE(SoftLight); diff --git a/gfx/skia/skia/src/opts/opts_check_x86.cpp b/gfx/skia/skia/src/opts/opts_check_x86.cpp index 3c817e1f0a..9983eb56a8 100644 --- a/gfx/skia/skia/src/opts/opts_check_x86.cpp +++ b/gfx/skia/skia/src/opts/opts_check_x86.cpp @@ -161,17 +161,6 @@ void SkBitmapProcState::platformProcs() { } } - /* Check fSampleProc16 */ - if (fSampleProc16 == S32_D16_filter_DX) { - if (ssse3) { - fSampleProc16 = S32_D16_filter_DX_SSSE3; - } else { - fSampleProc16 = S32_D16_filter_DX_SSE2; - } - } else if (ssse3 && fSampleProc16 == S32_D16_filter_DXDY) { - fSampleProc16 = S32_D16_filter_DXDY_SSSE3; - } - /* Check fMatrixProc */ if (fMatrixProc == ClampX_ClampY_filter_scale) { fMatrixProc = ClampX_ClampY_filter_scale_SSE2; diff --git a/gfx/skia/skia/src/pathops/SkDLineIntersection.cpp b/gfx/skia/skia/src/pathops/SkDLineIntersection.cpp index 6fbac978b6..71e2a064d5 100644 --- a/gfx/skia/skia/src/pathops/SkDLineIntersection.cpp +++ b/gfx/skia/skia/src/pathops/SkDLineIntersection.cpp @@ -108,7 +108,7 @@ int SkIntersections::intersect(const SkDLine& a, const SkDLine& b) { double ayBxLen = ayLen * bxLen; // detect parallel lines the same way here and in SkOpAngle operator < // so that non-parallel means they are also sortable - bool unparallel = fAllowNear ? NotAlmostEqualUlps(axByLen, ayBxLen) + bool unparallel = fAllowNear ? NotAlmostEqualUlps_Pin(axByLen, ayBxLen) : NotAlmostDequalUlps(axByLen, ayBxLen); if (unparallel && fUsed == 0) { double ab0y = a[0].fY - b[0].fY; diff --git a/gfx/skia/skia/src/pathops/SkOpAngle.cpp b/gfx/skia/skia/src/pathops/SkOpAngle.cpp index 655df45b15..c2eb0c9326 100644 --- a/gfx/skia/skia/src/pathops/SkOpAngle.cpp +++ b/gfx/skia/skia/src/pathops/SkOpAngle.cpp @@ -1016,7 +1016,7 @@ deferTilLater: if (!crossesZero) { fSectorMask = (unsigned) -1 >> (31 - end + start) << start; } else { - fSectorMask = (unsigned) -1 >> (31 - start) | (-1 << end); + fSectorMask = (unsigned) -1 >> (31 - start) | ((unsigned) -1 << end); } } diff --git a/gfx/skia/skia/src/pathops/SkOpCoincidence.cpp b/gfx/skia/skia/src/pathops/SkOpCoincidence.cpp index 87bb913869..5687dd415f 100644 --- a/gfx/skia/skia/src/pathops/SkOpCoincidence.cpp +++ b/gfx/skia/skia/src/pathops/SkOpCoincidence.cpp @@ -110,11 +110,17 @@ bool SkOpCoincidence::addExpanded(SkChunkAlloc* allocator if (startPart < oStartPart) { double newT = oStartPtT->fT + oStartRange * startPart; newPt = oStart->segment()->addT(newT, SkOpSegment::kAllowAlias, allocator); + if (!newPt) { + return false; + } newPt->fPt = test->pt(); test->ptT()->addOpp(newPt); } else { double newT = startPtT->fT + startRange * oStartPart; newPt = start->segment()->addT(newT, SkOpSegment::kAllowAlias, allocator); + if (!newPt) { + return false; + } newPt->fPt = oTest->pt(); oTest->ptT()->addOpp(newPt); } @@ -384,6 +390,9 @@ bool SkOpCoincidence::apply() { SkOpSegment* oSegment = oStart->segment(); bool operandSwap = segment->operand() != oSegment->operand(); if (flipped) { + if (oEnd->deleted()) { + continue; + } do { SkOpSpanBase* oNext = oStart->next(); if (oNext == oEnd) { diff --git a/gfx/skia/skia/src/pathops/SkOpSegment.cpp b/gfx/skia/skia/src/pathops/SkOpSegment.cpp index 3b81cf2eed..67f172e58a 100644 --- a/gfx/skia/skia/src/pathops/SkOpSegment.cpp +++ b/gfx/skia/skia/src/pathops/SkOpSegment.cpp @@ -362,6 +362,9 @@ SkOpPtT* SkOpSegment::addT(double t, AllowAlias allowAlias, SkChunkAlloc* alloca } if (t < result->fT) { SkOpSpan* prev = result->span()->prev(); + if (!prev) { + return nullptr; + } SkOpSpan* span = insert(prev, allocator); span->init(this, prev, t, pt); this->debugValidate(); @@ -1680,7 +1683,8 @@ bool SkOpSegment::testForCoincidence(const SkOpPtT* priorPtT, const SkOpPtT* ptT coincident = false; SkIntersections i; SkVector dxdy = (*CurveSlopeAtT[fVerb])(this->pts(), this->weight(), midT); - SkDLine ray = {{{midPt.fX, midPt.fY}, {midPt.fX + dxdy.fY, midPt.fY - dxdy.fX}}}; + SkDLine ray = {{{midPt.fX, midPt.fY}, + {(double) midPt.fX + dxdy.fY, (double) midPt.fY - dxdy.fX}}}; (*CurveIntersectRay[opp->verb()])(opp->pts(), opp->weight(), ray, &i); // measure distance and see if it's small enough to denote coincidence for (int index = 0; index < i.used(); ++index) { diff --git a/gfx/skia/skia/src/pathops/SkPathOpsLine.cpp b/gfx/skia/skia/src/pathops/SkPathOpsLine.cpp index cad54eb263..6fa091db8e 100644 --- a/gfx/skia/skia/src/pathops/SkPathOpsLine.cpp +++ b/gfx/skia/skia/src/pathops/SkPathOpsLine.cpp @@ -41,6 +41,9 @@ double SkDLine::nearPoint(const SkDPoint& xy, bool* unequal) const { if (!between(0, numer, denom)) { return -1; } + if (!denom) { + return 0; + } double t = numer / denom; SkDPoint realPt = ptAtT(t); double dist = realPt.distance(xy); // OPTIMIZATION: can we compare against distSq instead ? @@ -48,7 +51,7 @@ double SkDLine::nearPoint(const SkDPoint& xy, bool* unequal) const { double tiniest = SkTMin(SkTMin(SkTMin(fPts[0].fX, fPts[0].fY), fPts[1].fX), fPts[1].fY); double largest = SkTMax(SkTMax(SkTMax(fPts[0].fX, fPts[0].fY), fPts[1].fX), fPts[1].fY); largest = SkTMax(largest, -tiniest); - if (!AlmostEqualUlps(largest, largest + dist)) { // is the dist within ULPS tolerance? + if (!AlmostEqualUlps_Pin(largest, largest + dist)) { // is the dist within ULPS tolerance? return -1; } if (unequal) { diff --git a/gfx/skia/skia/src/pathops/SkPathOpsQuad.cpp b/gfx/skia/skia/src/pathops/SkPathOpsQuad.cpp index 12b9658ce7..3deab21133 100644 --- a/gfx/skia/skia/src/pathops/SkPathOpsQuad.cpp +++ b/gfx/skia/skia/src/pathops/SkPathOpsQuad.cpp @@ -119,7 +119,8 @@ and using the roots int SkDQuad::RootsReal(const double A, const double B, const double C, double s[2]) { const double p = B / (2 * A); const double q = C / A; - if (approximately_zero(A) && (approximately_zero_inverse(p) || approximately_zero_inverse(q))) { + if (!A || (approximately_zero(A) && (approximately_zero_inverse(p) + || approximately_zero_inverse(q)))) { if (approximately_zero(B)) { s[0] = 0; return C == 0; diff --git a/gfx/skia/skia/src/pathops/SkPathOpsTSect.h b/gfx/skia/skia/src/pathops/SkPathOpsTSect.h index bebdf40208..074fe37654 100644 --- a/gfx/skia/skia/src/pathops/SkPathOpsTSect.h +++ b/gfx/skia/skia/src/pathops/SkPathOpsTSect.h @@ -1467,11 +1467,17 @@ int SkTSect::linesIntersect(SkTSpan* span, workT += tStep; workPt = fCurve.ptAtT(workT); coinW.setPerp(fCurve, workT, workPt, opp->fCurve); + if (coinW.perpT() < 0) { + continue; + } SkDVector perpW = workPt - coinW.perpPt(); if ((perpS.dot(perpW) >= 0) == (tStep < 0)) { tStep = -tStep; } - } while (!workPt.approximatelyEqual(coinW.perpPt())); + if (workPt.approximatelyEqual(coinW.perpPt())) { + break; + } + } while (true); double oppTTest = coinW.perpT(); if (!opp->fHead->contains(oppTTest)) { return 0; diff --git a/gfx/skia/skia/src/pathops/SkPathOpsTypes.cpp b/gfx/skia/skia/src/pathops/SkPathOpsTypes.cpp index ca84405aa7..1ed484afee 100644 --- a/gfx/skia/skia/src/pathops/SkPathOpsTypes.cpp +++ b/gfx/skia/skia/src/pathops/SkPathOpsTypes.cpp @@ -16,6 +16,16 @@ static bool arguments_denormalized(float a, float b, int epsilon) { // from http://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/ // FIXME: move to SkFloatBits.h static bool equal_ulps(float a, float b, int epsilon, int depsilon) { + if (arguments_denormalized(a, b, depsilon)) { + return true; + } + int aBits = SkFloatAs2sCompliment(a); + int bBits = SkFloatAs2sCompliment(b); + // Find the difference in ULPs. + return aBits < bBits + epsilon && bBits < aBits + epsilon; +} + +static bool equal_ulps_pin(float a, float b, int epsilon, int depsilon) { if (!SkScalarIsFinite(a) || !SkScalarIsFinite(b)) { return false; } @@ -29,9 +39,6 @@ static bool equal_ulps(float a, float b, int epsilon, int depsilon) { } static bool d_equal_ulps(float a, float b, int epsilon) { - if (!SkScalarIsFinite(a) || !SkScalarIsFinite(b)) { - return false; - } int aBits = SkFloatAs2sCompliment(a); int bBits = SkFloatAs2sCompliment(b); // Find the difference in ULPs. @@ -39,6 +46,16 @@ static bool d_equal_ulps(float a, float b, int epsilon) { } static bool not_equal_ulps(float a, float b, int epsilon) { + if (arguments_denormalized(a, b, epsilon)) { + return false; + } + int aBits = SkFloatAs2sCompliment(a); + int bBits = SkFloatAs2sCompliment(b); + // Find the difference in ULPs. + return aBits >= bBits + epsilon || bBits >= aBits + epsilon; +} + +static bool not_equal_ulps_pin(float a, float b, int epsilon) { if (!SkScalarIsFinite(a) || !SkScalarIsFinite(b)) { return false; } @@ -52,9 +69,6 @@ static bool not_equal_ulps(float a, float b, int epsilon) { } static bool d_not_equal_ulps(float a, float b, int epsilon) { - if (!SkScalarIsFinite(a) || !SkScalarIsFinite(b)) { - return false; - } int aBits = SkFloatAs2sCompliment(a); int bBits = SkFloatAs2sCompliment(b); // Find the difference in ULPs. @@ -62,9 +76,6 @@ static bool d_not_equal_ulps(float a, float b, int epsilon) { } static bool less_ulps(float a, float b, int epsilon) { - if (!SkScalarIsFinite(a) || !SkScalarIsFinite(b)) { - return false; - } if (arguments_denormalized(a, b, epsilon)) { return a <= b - FLT_EPSILON * epsilon; } @@ -75,9 +86,6 @@ static bool less_ulps(float a, float b, int epsilon) { } static bool less_or_equal_ulps(float a, float b, int epsilon) { - if (!SkScalarIsFinite(a) || !SkScalarIsFinite(b)) { - return false; - } if (arguments_denormalized(a, b, epsilon)) { return a < b + FLT_EPSILON * epsilon; } @@ -104,10 +112,7 @@ bool AlmostDequalUlps(float a, float b) { } bool AlmostDequalUlps(double a, double b) { - if (SkScalarIsFinite(a) || SkScalarIsFinite(b)) { - return AlmostDequalUlps(SkDoubleToScalar(a), SkDoubleToScalar(b)); - } - return fabs(a - b) / SkTMax(fabs(a), fabs(b)) < FLT_EPSILON * 16; + return AlmostDequalUlps(SkDoubleToScalar(a), SkDoubleToScalar(b)); } bool AlmostEqualUlps(float a, float b) { @@ -115,11 +120,21 @@ bool AlmostEqualUlps(float a, float b) { return equal_ulps(a, b, UlpsEpsilon, UlpsEpsilon); } +bool AlmostEqualUlps_Pin(float a, float b) { + const int UlpsEpsilon = 16; + return equal_ulps_pin(a, b, UlpsEpsilon, UlpsEpsilon); +} + bool NotAlmostEqualUlps(float a, float b) { const int UlpsEpsilon = 16; return not_equal_ulps(a, b, UlpsEpsilon); } +bool NotAlmostEqualUlps_Pin(float a, float b) { + const int UlpsEpsilon = 16; + return not_equal_ulps_pin(a, b, UlpsEpsilon); +} + bool NotAlmostDequalUlps(float a, float b) { const int UlpsEpsilon = 16; return d_not_equal_ulps(a, b, UlpsEpsilon); @@ -148,9 +163,6 @@ bool AlmostLessOrEqualUlps(float a, float b) { } int UlpsDistance(float a, float b) { - if (!SkScalarIsFinite(a) || !SkScalarIsFinite(b)) { - return SK_MaxS32; - } SkFloatIntUnion floatIntA, floatIntB; floatIntA.fFloat = a; floatIntB.fFloat = b; diff --git a/gfx/skia/skia/src/pathops/SkPathOpsTypes.h b/gfx/skia/skia/src/pathops/SkPathOpsTypes.h index f85c6653eb..b35c97ef06 100644 --- a/gfx/skia/skia/src/pathops/SkPathOpsTypes.h +++ b/gfx/skia/skia/src/pathops/SkPathOpsTypes.h @@ -167,6 +167,11 @@ inline bool AlmostEqualUlps(double a, double b) { return AlmostEqualUlps(SkDoubleToScalar(a), SkDoubleToScalar(b)); } +bool AlmostEqualUlps_Pin(float a, float b); +inline bool AlmostEqualUlps_Pin(double a, double b) { + return AlmostEqualUlps_Pin(SkDoubleToScalar(a), SkDoubleToScalar(b)); +} + // Use Almost Dequal when comparing should not special case denormalized values. bool AlmostDequalUlps(float a, float b); bool AlmostDequalUlps(double a, double b); @@ -176,6 +181,11 @@ inline bool NotAlmostEqualUlps(double a, double b) { return NotAlmostEqualUlps(SkDoubleToScalar(a), SkDoubleToScalar(b)); } +bool NotAlmostEqualUlps_Pin(float a, float b); +inline bool NotAlmostEqualUlps_Pin(double a, double b) { + return NotAlmostEqualUlps_Pin(SkDoubleToScalar(a), SkDoubleToScalar(b)); +} + bool NotAlmostDequalUlps(float a, float b); inline bool NotAlmostDequalUlps(double a, double b) { return NotAlmostDequalUlps(SkDoubleToScalar(a), SkDoubleToScalar(b)); diff --git a/gfx/skia/skia/src/pathops/SkReduceOrder.cpp b/gfx/skia/skia/src/pathops/SkReduceOrder.cpp index 43a91901f7..52a19d6138 100644 --- a/gfx/skia/skia/src/pathops/SkReduceOrder.cpp +++ b/gfx/skia/skia/src/pathops/SkReduceOrder.cpp @@ -126,7 +126,7 @@ static int check_quadratic(const SkDCubic& cubic, SkDCubic& reduction) { double sideAx = midX - cubic[3].fX; double sideBx = dx23 * 3 / 2; if (approximately_zero(sideAx) ? !approximately_equal(sideAx, sideBx) - : !AlmostEqualUlps(sideAx, sideBx)) { + : !AlmostEqualUlps_Pin(sideAx, sideBx)) { return 0; } double dy10 = cubic[1].fY - cubic[0].fY; @@ -135,7 +135,7 @@ static int check_quadratic(const SkDCubic& cubic, SkDCubic& reduction) { double sideAy = midY - cubic[3].fY; double sideBy = dy23 * 3 / 2; if (approximately_zero(sideAy) ? !approximately_equal(sideAy, sideBy) - : !AlmostEqualUlps(sideAy, sideBy)) { + : !AlmostEqualUlps_Pin(sideAy, sideBy)) { return 0; } reduction[0] = cubic[0]; diff --git a/gfx/skia/skia/src/pdf/SkPDFBitmap.cpp b/gfx/skia/skia/src/pdf/SkPDFBitmap.cpp index 0c53da6974..4e49db518d 100644 --- a/gfx/skia/skia/src/pdf/SkPDFBitmap.cpp +++ b/gfx/skia/skia/src/pdf/SkPDFBitmap.cpp @@ -468,7 +468,8 @@ void PDFJpegBitmap::emitObject(SkWStream* stream, //////////////////////////////////////////////////////////////////////////////// -SkPDFObject* SkPDFCreateBitmapObject(const SkImage* image) { +SkPDFObject* SkPDFCreateBitmapObject(const SkImage* image, + SkPixelSerializer* pixelSerializer) { SkAutoTUnref data(image->refEncoded()); SkJFIFInfo info; if (data && SkIsJFIF(data, &info)) { @@ -481,6 +482,21 @@ SkPDFObject* SkPDFCreateBitmapObject(const SkImage* image) { return new PDFJpegBitmap(info.fSize, data, yuv); } } + + if (pixelSerializer) { + SkBitmap bm; + SkAutoPixmapUnlock apu; + if (as_IB(image)->getROPixels(&bm) && bm.requestLock(&apu)) { + data.reset(pixelSerializer->encode(apu.pixmap())); + if (data && SkIsJFIF(data, &info)) { + bool yuv = info.fType == SkJFIFInfo::kYCbCr; + if (info.fSize == image->dimensions()) { // Sanity check. + return new PDFJpegBitmap(info.fSize, data, yuv); + } + } + } + } + SkPDFObject* smask = image_compute_is_opaque(image) ? nullptr : new PDFAlphaBitmap(image); #ifdef SK_PDF_IMAGE_STATS diff --git a/gfx/skia/skia/src/pdf/SkPDFBitmap.h b/gfx/skia/skia/src/pdf/SkPDFBitmap.h index d931331431..b0254c1f55 100644 --- a/gfx/skia/skia/src/pdf/SkPDFBitmap.h +++ b/gfx/skia/skia/src/pdf/SkPDFBitmap.h @@ -16,6 +16,6 @@ class SkImage; * It is designed to use a minimal amout of memory, aside from refing * the image, and its emitObject() does not cache any data. */ -SkPDFObject* SkPDFCreateBitmapObject(const SkImage*); +SkPDFObject* SkPDFCreateBitmapObject(const SkImage*, SkPixelSerializer*); #endif // SkPDFBitmap_DEFINED diff --git a/gfx/skia/skia/src/pdf/SkPDFCanon.h b/gfx/skia/skia/src/pdf/SkPDFCanon.h index 3d2ba6a77d..9ecb3a02d9 100644 --- a/gfx/skia/skia/src/pdf/SkPDFCanon.h +++ b/gfx/skia/skia/src/pdf/SkPDFCanon.h @@ -10,6 +10,7 @@ #include "SkBitmap.h" #include "SkPDFGraphicState.h" #include "SkPDFShader.h" +#include "SkPixelSerializer.h" #include "SkTDArray.h" #include "SkTHash.h" @@ -80,6 +81,8 @@ public: SkTHashMap fCanEmbedTypeface; + SkAutoTUnref fPixelSerializer; + private: struct FontRec { SkPDFFont* fFont; diff --git a/gfx/skia/skia/src/pdf/SkPDFDevice.cpp b/gfx/skia/skia/src/pdf/SkPDFDevice.cpp index 1c5b4da070..83408a55be 100644 --- a/gfx/skia/skia/src/pdf/SkPDFDevice.cpp +++ b/gfx/skia/skia/src/pdf/SkPDFDevice.cpp @@ -5,7 +5,6 @@ * found in the LICENSE file. */ -#include #include "SkPDFDevice.h" #include "SkAnnotation.h" @@ -48,15 +47,12 @@ static bool excessive_translation(const SkMatrix& m) { || SkScalarAbs(m.getTranslateY()) > kExcessiveTranslation; } -static std::tuple untranslate(const SkDraw& d) { +static SkMatrix untranslate(const SkMatrix& matrix, SkScalar x, SkScalar y) { // https://bug.skia.org/257 If the translation is too large, // PDF can't exactly represent the float values as numbers. - SkScalar translateX = d.fMatrix->getTranslateX() / d.fMatrix->getScaleX(); - SkScalar translateY = d.fMatrix->getTranslateY() / d.fMatrix->getScaleY(); - SkMatrix mat = *d.fMatrix; - mat.preTranslate(-translateX, -translateY); - SkASSERT(SkScalarAbs(mat.getTranslateX()) <= 1.0); - return std::make_tuple(mat, SkVector::Make(translateX, translateY)); + SkMatrix result(matrix); + result.preTranslate(x, y); + return result; } // If the paint will definitely draw opaquely, replace kSrc_Mode with @@ -802,7 +798,7 @@ void SkPDFDevice::internalDrawPaint(const SkPaint& paint, &contentEntry->fContent); } -void SkPDFDevice::drawPoints(const SkDraw& d, +void SkPDFDevice::drawPoints(const SkDraw& srcDraw, SkCanvas::PointMode mode, size_t count, const SkPoint* points, @@ -815,19 +811,20 @@ void SkPDFDevice::drawPoints(const SkDraw& d, } if (SkAnnotation* annotation = passedPaint.getAnnotation()) { - if (handlePointAnnotation(points, count, *d.fMatrix, annotation)) { + if (handlePointAnnotation(points, count, *srcDraw.fMatrix, annotation)) { return; } } + SkMatrix newMatrix; + SkDraw d(srcDraw); + SkTArray pointsCopy; if (excessive_translation(*d.fMatrix)) { - SkVector translate; SkMatrix translateMatrix; - std::tie(translateMatrix, translate) = untranslate(d); - SkDraw drawCopy(d); - drawCopy.fMatrix = &translateMatrix; - SkTArray pointsCopy(points, SkToInt(count)); - SkPoint::Offset(&pointsCopy[0], SkToInt(count), translate); - this->drawPoints(drawCopy, mode, count, &pointsCopy[0], srcPaint); - return; // NOTE: shader behavior will be off. + newMatrix = untranslate(*d.fMatrix, points[0].x(), points[0].y()); + d.fMatrix = &newMatrix; + pointsCopy.reset(points, SkToInt(count)); + SkPoint::Offset(&pointsCopy[0], SkToInt(count), + -points[0].x(), -points[0].y()); + points = &pointsCopy[0]; } // SkDraw::drawPoints converts to multiple calls to fDevice->drawPath. @@ -949,7 +946,7 @@ static SkPDFDict* create_link_named_dest(const SkData* nameData, return annotation.detach(); } -void SkPDFDevice::drawRect(const SkDraw& d, +void SkPDFDevice::drawRect(const SkDraw& srcDraw, const SkRect& rect, const SkPaint& srcPaint) { SkPaint paint = srcPaint; @@ -957,15 +954,12 @@ void SkPDFDevice::drawRect(const SkDraw& d, SkRect r = rect; r.sort(); + SkMatrix newMatrix; + SkDraw d(srcDraw); if (excessive_translation(*d.fMatrix)) { - SkVector translate; SkMatrix translateMatrix; - std::tie(translateMatrix, translate) = untranslate(d); - SkDraw drawCopy(d); - drawCopy.fMatrix = &translateMatrix; - SkRect rectCopy = rect; - rectCopy.offset(translate.x(), translate.y()); - this->drawRect(drawCopy, rectCopy, srcPaint); - return; // NOTE: shader behavior will be off. + newMatrix = untranslate(*d.fMatrix, r.x(), r.y()); + d.fMatrix = &newMatrix; + r.offsetTo(0, 0); } if (paint.getPathEffect()) { @@ -1015,35 +1009,40 @@ void SkPDFDevice::drawOval(const SkDraw& draw, this->drawPath(draw, path, paint, nullptr, true); } -void SkPDFDevice::drawPath(const SkDraw& d, +void SkPDFDevice::drawPath(const SkDraw& srcDraw, const SkPath& origPath, const SkPaint& srcPaint, const SkMatrix* prePathMatrix, bool pathIsMutable) { + SkMatrix newMatrix; + SkDraw d(srcDraw); + SkPath modifiedPath; + SkPath* pathPtr = const_cast(&origPath); if (excessive_translation(*d.fMatrix)) { - SkVector translate; SkMatrix translateMatrix; - std::tie(translateMatrix, translate) = untranslate(d); - SkDraw drawCopy(d); - drawCopy.fMatrix = &translateMatrix; - SkPath pathCopy(origPath); - pathCopy.offset(translate.x(), translate.y()); - this->drawPath(drawCopy, pathCopy, srcPaint, prePathMatrix, true); - return; // NOTE: shader behavior will be off. + SkPoint firstPt; + if (origPath.getPoints(&firstPt, 1) > 0) { + newMatrix = untranslate(*d.fMatrix, firstPt.x(), firstPt.y()); + d.fMatrix = &newMatrix; + modifiedPath = origPath; + modifiedPath.offset(-firstPt.x(), -firstPt.y()); + pathPtr = &modifiedPath; // NOTE: shader behavior will be off. + pathIsMutable = true; + } } SkPaint paint = srcPaint; replace_srcmode_on_opaque_paint(&paint); - SkPath modifiedPath; - SkPath* pathPtr = const_cast(&origPath); SkMatrix matrix = *d.fMatrix; if (prePathMatrix) { if (paint.getPathEffect() || paint.getStyle() != SkPaint::kFill_Style) { - if (!pathIsMutable) { + if (pathIsMutable) { + pathPtr->transform(*prePathMatrix); + } else { + pathPtr->transform(*prePathMatrix, &modifiedPath); pathPtr = &modifiedPath; pathIsMutable = true; } - origPath.transform(*prePathMatrix, pathPtr); } else { matrix.preConcat(*prePathMatrix); } @@ -1053,11 +1052,14 @@ void SkPDFDevice::drawPath(const SkDraw& d, if (d.fClip->isEmpty()) { return; } - if (!pathIsMutable) { + bool fill; + if (pathIsMutable) { + fill = paint.getFillPath(*pathPtr, pathPtr); + } else { + fill = paint.getFillPath(*pathPtr, &modifiedPath); pathPtr = &modifiedPath; pathIsMutable = true; } - bool fill = paint.getFillPath(origPath, pathPtr); SkPaint noEffectPaint(paint); noEffectPaint.setPathEffect(nullptr); @@ -1321,16 +1323,15 @@ static void draw_transparent_text(SkPDFDevice* device, } -void SkPDFDevice::drawText(const SkDraw& d, const void* text, size_t len, +void SkPDFDevice::drawText(const SkDraw& srcDraw, const void* text, size_t len, SkScalar x, SkScalar y, const SkPaint& srcPaint) { + SkMatrix newMatrix; + SkDraw d(srcDraw); if (excessive_translation(*d.fMatrix)) { - SkVector translate; SkMatrix translateMatrix; - std::tie(translateMatrix, translate) = untranslate(d); - SkDraw drawCopy(d); - drawCopy.fMatrix = &translateMatrix; - this->drawText(drawCopy, text, len, x + translate.x(), - y + translate.y(), srcPaint); - return; // NOTE: shader behavior will be off. + newMatrix = untranslate(*d.fMatrix, x, y); + d.fMatrix = &newMatrix; + x = 0; + y = 0; } if (!SkPDFFont::CanEmbedTypeface(srcPaint.getTypeface(), fCanon)) { @@ -1391,19 +1392,44 @@ void SkPDFDevice::drawText(const SkDraw& d, const void* text, size_t len, content.entry()->fContent.writeText("ET\n"); } -void SkPDFDevice::drawPosText(const SkDraw& d, const void* text, size_t len, +void SkPDFDevice::drawPosText(const SkDraw& srcDraw, const void* text, size_t len, const SkScalar pos[], int scalarsPerPos, - const SkPoint& offset, const SkPaint& srcPaint) { + const SkPoint& srcOffset, const SkPaint& srcPaint) { + if (len == 0) { + return; + } + SkMatrix newMatrix; + SkDraw d(srcDraw); + SkPoint offset(srcOffset); + SkAutoTMalloc scalarsBuffer; if (excessive_translation(*d.fMatrix)) { - SkVector translate; SkMatrix translateMatrix; - std::tie(translateMatrix, translate) = untranslate(d); - SkDraw drawCopy(d); - drawCopy.fMatrix = &translateMatrix; - SkPoint offsetCopy = offset; - SkPoint::Offset(&offsetCopy, 1, translate.x(), translate.y()); - this->drawPosText(drawCopy, text, len, pos, scalarsPerPos, offsetCopy, - srcPaint); - return; // NOTE: shader behavior will be off. + SkPoint first; + if (scalarsPerPos != 2) { + first.set(pos[0], 0); + } else { + first.set(pos[0], pos[1]); + } + newMatrix = untranslate(*d.fMatrix, + first.x() + offset.x(), + first.y() + offset.y()); + d.fMatrix = &newMatrix; + offset.set(0, 0); // offset -= offset; + if (first.x() != 0 || first.y() != 0) { + int glyphCount = srcPaint.textToGlyphs(text, len, NULL); + if (scalarsPerPos != 2) { + scalarsBuffer.reset(glyphCount); + for (int i = 0; i < glyphCount; ++i) { + scalarsBuffer[i] = pos[i] - first.x(); + } + } else { + scalarsBuffer.reset(2 * glyphCount); + for (int i = 0; i < glyphCount; ++i) { + scalarsBuffer[2 * i] = pos[2 * i] - first.x(); + scalarsBuffer[2 * i + 1] = pos[2 * i + 1] - first.y(); + } + } + pos = &scalarsBuffer[0]; + } } if (!SkPDFFont::CanEmbedTypeface(srcPaint.getTypeface(), fCanon)) { @@ -2433,7 +2459,8 @@ void SkPDFDevice::internalDrawImage(const SkMatrix& origMatrix, } SkAutoTUnref pdfimage(SkSafeRef(fCanon->findPDFBitmap(image))); if (!pdfimage) { - pdfimage.reset(SkPDFCreateBitmapObject(image)); + pdfimage.reset(SkPDFCreateBitmapObject( + image, fCanon->fPixelSerializer)); if (!pdfimage) { return; } diff --git a/gfx/skia/skia/src/pdf/SkPDFFont.cpp b/gfx/skia/skia/src/pdf/SkPDFFont.cpp index 0d6f318799..5c06bcc532 100644 --- a/gfx/skia/skia/src/pdf/SkPDFFont.cpp +++ b/gfx/skia/skia/src/pdf/SkPDFFont.cpp @@ -24,14 +24,13 @@ #include "SkTypes.h" #include "SkUtils.h" -#if defined (GOOGLE3) - #if !defined (SK_BUILD_FOR_ANDROID) +#if defined (SK_SFNTLY_SUBSETTER) + #if defined (GOOGLE3) // #including #defines doesn't work with this build system. #include "typography/font/sfntly/src/sample/chromium/font_subsetter.h" - #define SK_SFNTLY_SUBSETTER // For the benefit of #ifdefs below. + #else + #include SK_SFNTLY_SUBSETTER #endif -#elif defined (SK_SFNTLY_SUBSETTER) - #include SK_SFNTLY_SUBSETTER #endif // PDF's notion of symbolic vs non-symbolic is related to the character set, not diff --git a/gfx/skia/skia/src/pdf/SkPDFMetadata.cpp b/gfx/skia/skia/src/pdf/SkPDFMetadata.cpp index 3f181f5d73..51619c6748 100644 --- a/gfx/skia/skia/src/pdf/SkPDFMetadata.cpp +++ b/gfx/skia/skia/src/pdf/SkPDFMetadata.cpp @@ -7,6 +7,7 @@ #include "SkPDFMetadata.h" #include "SkPDFTypes.h" +#include #ifdef SK_PDF_GENERATE_PDFA #include "SkMD5.h" @@ -117,7 +118,7 @@ static SkString sk_string_printf(const char* format, ...) { va_end(args); SkASSERT(check == length); SkASSERT(string[length] == '\0'); - return skstd::move(string); + return std::move(string); #else // C99/C++11 standard vsnprintf // TODO: When all compilers support this, remove windows-specific code. va_list args; @@ -138,7 +139,7 @@ static SkString sk_string_printf(const char* format, ...) { va_end(args); SkASSERT(check == length); SkASSERT(string[length] == '\0'); - return skstd::move(string); + return std::move(string); #endif } @@ -185,7 +186,7 @@ static SkString uuid_to_string(const SkPDFMetadata::UUID& uuid) { namespace { class PDFXMLObject final : public SkPDFObject { public: - PDFXMLObject(SkString xml) : fXML(skstd::move(xml)) {} + PDFXMLObject(SkString xml) : fXML(std::move(xml)) {} void emitObject(SkWStream* stream, const SkPDFObjNumMap& omap, const SkPDFSubstituteMap& smap) const override { @@ -258,7 +259,7 @@ const SkString escape_xml(const SkString& input, // Validate that we haven't written outside of our string. SkASSERT(out == &output.writable_str()[output.size()]); *out = '\0'; - return skstd::move(output); + return std::move(output); } SkPDFObject* SkPDFMetadata::createXMPObject(const UUID& doc, diff --git a/gfx/skia/skia/src/pipe/SkGPipePriv.h b/gfx/skia/skia/src/pipe/SkGPipePriv.h deleted file mode 100644 index c4217f3bf9..0000000000 --- a/gfx/skia/skia/src/pipe/SkGPipePriv.h +++ /dev/null @@ -1,314 +0,0 @@ - -/* - * Copyright 2011 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - - - -#ifndef SkGPipePriv_DEFINED -#define SkGPipePriv_DEFINED - -#include "SkTypes.h" - -#define UNIMPLEMENTED - -// these must be contiguous, 0...N-1 -enum PaintFlats { - kColorFilter_PaintFlat, - kDrawLooper_PaintFlat, - kImageFilter_PaintFlat, - kMaskFilter_PaintFlat, - kPathEffect_PaintFlat, - kRasterizer_PaintFlat, - kShader_PaintFlat, - kXfermode_PaintFlat, - - kLast_PaintFlat = kXfermode_PaintFlat -}; -#define kCount_PaintFlats (kLast_PaintFlat + 1) - -enum DrawOps { - kSkip_DrawOp, // skip an addition N bytes (N == data) - - // these match Canvas apis - kClipPath_DrawOp, - kClipRegion_DrawOp, - kClipRect_DrawOp, - kClipRRect_DrawOp, - kConcat_DrawOp, - kDrawAtlas_DrawOp, - kDrawBitmap_DrawOp, - kDrawBitmapNine_DrawOp, - kDrawBitmapRect_DrawOp, - kDrawDRRect_DrawOp, - kDrawImage_DrawOp, - kDrawImageRect_DrawOp, - kDrawImageNine_DrawOp, - kDrawOval_DrawOp, - kDrawPaint_DrawOp, - kDrawPatch_DrawOp, - kDrawPath_DrawOp, - kDrawPicture_DrawOp, - kDrawPoints_DrawOp, - kDrawPosText_DrawOp, - kDrawPosTextH_DrawOp, - kDrawRect_DrawOp, - kDrawRRect_DrawOp, - kDrawSprite_DrawOp, - kDrawText_DrawOp, - kDrawTextBlob_DrawOp, - kDrawTextOnPath_DrawOp, - kDrawVertices_DrawOp, - kRestore_DrawOp, - kRotate_DrawOp, - kSave_DrawOp, - kSaveLayer_DrawOp, - kScale_DrawOp, - kSetMatrix_DrawOp, - kSkew_DrawOp, - kTranslate_DrawOp, - - kPaintOp_DrawOp, - kSetTypeface_DrawOp, - kSetAnnotation_DrawOp, - - kDef_Typeface_DrawOp, - kDef_Flattenable_DrawOp, - kDef_Bitmap_DrawOp, - kDef_Factory_DrawOp, - - // these are signals to playback, not drawing verbs - kReportFlags_DrawOp, - kShareBitmapHeap_DrawOp, - kShareImageHeap_DrawOp, - kDone_DrawOp, -}; - -/** - * DrawOp packs into a 32bit int as follows - * - * DrawOp:8 - Flags:4 - Data:20 - * - * Flags and Data are called out separately, so we can reuse Data between - * different Ops that might have different Flags. e.g. Data might be a Paint - * index for both drawRect (no flags) and saveLayer (does have flags). - * - * All Ops that take a SkPaint use their Data field to store the index to - * the paint (previously defined with kPaintOp_DrawOp). - */ - -#define DRAWOPS_OP_BITS 8 -#define DRAWOPS_FLAG_BITS 4 -#define DRAWOPS_DATA_BITS 20 - -#define DRAWOPS_OP_MASK ((1 << DRAWOPS_OP_BITS) - 1) -#define DRAWOPS_FLAG_MASK ((1 << DRAWOPS_FLAG_BITS) - 1) -#define DRAWOPS_DATA_MASK ((1 << DRAWOPS_DATA_BITS) - 1) - -static inline unsigned DrawOp_unpackOp(uint32_t op32) { - return (op32 >> (DRAWOPS_FLAG_BITS + DRAWOPS_DATA_BITS)); -} - -static inline unsigned DrawOp_unpackFlags(uint32_t op32) { - return (op32 >> DRAWOPS_DATA_BITS) & DRAWOPS_FLAG_MASK; -} - -static inline unsigned DrawOp_unpackData(uint32_t op32) { - return op32 & DRAWOPS_DATA_MASK; -} - -static inline uint32_t DrawOp_packOpFlagData(DrawOps op, unsigned flags, unsigned data) { - SkASSERT(0 == (op & ~DRAWOPS_OP_MASK)); - SkASSERT(0 == (flags & ~DRAWOPS_FLAG_MASK)); - SkASSERT(0 == (data & ~DRAWOPS_DATA_MASK)); - - return (op << (DRAWOPS_FLAG_BITS + DRAWOPS_DATA_BITS)) | - (flags << DRAWOPS_DATA_BITS) | - data; -} - -/** DrawOp specific flag bits - */ - -enum { - kSaveLayer_HasBounds_DrawOpFlag = 1 << 0, - kSaveLayer_HasPaint_DrawOpFlag = 1 << 1, -}; -enum { - kDrawTextOnPath_HasMatrix_DrawOpFlag = 1 << 0 -}; -enum { - kDrawVertices_HasTexs_DrawOpFlag = 1 << 0, - kDrawVertices_HasColors_DrawOpFlag = 1 << 1, - kDrawVertices_HasIndices_DrawOpFlag = 1 << 2, - kDrawVertices_HasXfermode_DrawOpFlag = 1 << 3, -}; -enum { - kDrawAtlas_HasPaint_DrawOpFlag = 1 << 0, - kDrawAtlas_HasColors_DrawOpFlag = 1 << 1, - kDrawAtlas_HasCull_DrawOpFlag = 1 << 2, -}; -// These are shared between drawbitmap and drawimage -enum { - kDrawBitmap_HasPaint_DrawOpFlag = 1 << 0, - // Specific to drawBitmapRect, but needs to be different from HasPaint, - // which is used for all drawBitmap calls, so include it here. - kDrawBitmap_HasSrcRect_DrawOpFlag = 1 << 1, - // SkCanvas::DrawBitmapRectFlags::kBleed_DrawBitmapRectFlag is - // converted into and out of this flag to save space - kDrawBitmap_Bleed_DrawOpFlag = 1 << 2, -}; -enum { - kClip_HasAntiAlias_DrawOpFlag = 1 << 0, -}; -/////////////////////////////////////////////////////////////////////////////// - -class BitmapInfo : SkNoncopyable { -public: - BitmapInfo(SkBitmap* bitmap, uint32_t genID, int toBeDrawnCount) - : fBitmap(bitmap) - , fGenID(genID) - , fBytesAllocated(0) - , fMoreRecentlyUsed(nullptr) - , fLessRecentlyUsed(nullptr) - , fToBeDrawnCount(toBeDrawnCount) - {} - - ~BitmapInfo() { - SkASSERT(0 == fToBeDrawnCount); - delete fBitmap; - } - - void addDraws(int drawsToAdd) { - if (0 == fToBeDrawnCount) { - // The readers will only ever decrement the count, so once the - // count is zero, the writer will be the only one modifying it, - // so it does not need to be an atomic operation. - fToBeDrawnCount = drawsToAdd; - } else { - sk_atomic_add(&fToBeDrawnCount, drawsToAdd); - } - } - - void decDraws() { - sk_atomic_dec(&fToBeDrawnCount); - } - - int drawCount() const { - return fToBeDrawnCount; - } - - SkBitmap* fBitmap; - // Store the generation ID of the original bitmap, since copying does - // not copy this field, so fBitmap's generation ID will not be useful - // for comparing. - // FIXME: Is it reasonable to make copying a bitmap/pixelref copy the - // generation ID? - uint32_t fGenID; - // Keep track of the bytes allocated for this bitmap. When replacing the - // bitmap or removing this BitmapInfo we know how much memory has been - // reclaimed. - size_t fBytesAllocated; - // TODO: Generalize the LRU caching mechanism - BitmapInfo* fMoreRecentlyUsed; - BitmapInfo* fLessRecentlyUsed; -private: - int fToBeDrawnCount; -}; - -static inline bool shouldFlattenBitmaps(uint32_t flags) { - return SkToBool(flags & SkGPipeWriter::kCrossProcess_Flag - && !(flags & SkGPipeWriter::kSharedAddressSpace_Flag)); -} - -class SkImageHeap : public SkRefCnt { -public: - SkImageHeap(); - virtual ~SkImageHeap(); - - size_t bytesInCache() const { return fBytesInCache; } - void reset(); - // slot must be "valid" -- 0 is never valid - const SkImage* get(int32_t slot) const; - // returns 0 if not found, else returns slot - int32_t find(const SkImage*) const; - // returns non-zero value for where the image was stored - int32_t insert(const SkImage*); - -private: - SkTDArray fArray; - size_t fBytesInCache; -}; - -/////////////////////////////////////////////////////////////////////////////// - -enum PaintOps { - kReset_PaintOp, // no arg - - kFlags_PaintOp, // arg inline - kColor_PaintOp, // arg 32 - kFilterLevel_PaintOp, // arg inline - kStyle_PaintOp, // arg inline - kJoin_PaintOp, // arg inline - kCap_PaintOp, // arg inline - kWidth_PaintOp, // arg scalar - kMiter_PaintOp, // arg scalar - - kEncoding_PaintOp, // arg inline - text - kHinting_PaintOp, // arg inline - text - kAlign_PaintOp, // arg inline - text - kTextSize_PaintOp, // arg scalar - text - kTextScaleX_PaintOp,// arg scalar - text - kTextSkewX_PaintOp, // arg scalar - text - kTypeface_PaintOp, // arg inline (index) - text - - kFlatIndex_PaintOp, // flags=paintflat, data=index -}; - -#define PAINTOPS_OP_BITS 8 -#define PAINTOPS_FLAG_BITS 4 -#define PAINTOPS_DATA_BITS 20 - -#define PAINTOPS_OP_MASK ((1 << PAINTOPS_OP_BITS) - 1) -#define PAINTOPS_FLAG_MASK ((1 << PAINTOPS_FLAG_BITS) - 1) -#define PAINTOPS_DATA_MASK ((1 << PAINTOPS_DATA_BITS) - 1) - -static inline unsigned PaintOp_unpackOp(uint32_t op32) { - return (op32 >> (PAINTOPS_FLAG_BITS + PAINTOPS_DATA_BITS)); -} - -static inline unsigned PaintOp_unpackFlags(uint32_t op32) { - return (op32 >> PAINTOPS_DATA_BITS) & PAINTOPS_FLAG_MASK; -} - -static inline unsigned PaintOp_unpackData(uint32_t op32) { - return op32 & PAINTOPS_DATA_MASK; -} - -static inline uint32_t PaintOp_packOp(PaintOps op) { - SkASSERT(0 == (op & ~PAINTOPS_OP_MASK)); - - return op << (PAINTOPS_FLAG_BITS + PAINTOPS_DATA_BITS); -} - -static inline uint32_t PaintOp_packOpData(PaintOps op, unsigned data) { - SkASSERT(0 == (op & ~PAINTOPS_OP_MASK)); - SkASSERT(0 == (data & ~PAINTOPS_DATA_MASK)); - - return (op << (PAINTOPS_FLAG_BITS + PAINTOPS_DATA_BITS)) | data; -} - -static inline uint32_t PaintOp_packOpFlagData(PaintOps op, unsigned flags, unsigned data) { - SkASSERT(0 == (op & ~PAINTOPS_OP_MASK)); - SkASSERT(0 == (flags & ~PAINTOPS_FLAG_MASK)); - SkASSERT(0 == (data & ~PAINTOPS_DATA_MASK)); - - return (op << (PAINTOPS_FLAG_BITS + PAINTOPS_DATA_BITS)) | - (flags << PAINTOPS_DATA_BITS) | - data; -} - -#endif diff --git a/gfx/skia/skia/src/pipe/SkGPipeRead.cpp b/gfx/skia/skia/src/pipe/SkGPipeRead.cpp deleted file mode 100644 index 4d57a6cca9..0000000000 --- a/gfx/skia/skia/src/pipe/SkGPipeRead.cpp +++ /dev/null @@ -1,1017 +0,0 @@ - -/* - * Copyright 2011 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - - -#include "SkBitmapHeap.h" -#include "SkCanvas.h" -#include "SkPaint.h" -#include "SkGPipe.h" -#include "SkGPipePriv.h" -#include "SkReader32.h" -#include "SkStream.h" - -#include "SkAnnotation.h" -#include "SkColorFilter.h" -#include "SkDrawLooper.h" -#include "SkImageFilter.h" -#include "SkMaskFilter.h" -#include "SkPatchUtils.h" -#include "SkPathEffect.h" -#include "SkRasterizer.h" -#include "SkReadBuffer.h" -#include "SkRRect.h" -#include "SkRSXform.h" -#include "SkShader.h" -#include "SkTextBlob.h" -#include "SkTypeface.h" -#include "SkXfermode.h" - -static SkFlattenable::Type paintflat_to_flattype(PaintFlats pf) { - static const uint8_t gEffectTypesInPaintFlatsOrder[] = { - SkFlattenable::kSkColorFilter_Type, - SkFlattenable::kSkDrawLooper_Type, - SkFlattenable::kSkImageFilter_Type, - SkFlattenable::kSkMaskFilter_Type, - SkFlattenable::kSkPathEffect_Type, - SkFlattenable::kSkRasterizer_Type, - SkFlattenable::kSkShader_Type, - SkFlattenable::kSkXfermode_Type, - }; - - SkASSERT((size_t)pf < SK_ARRAY_COUNT(gEffectTypesInPaintFlatsOrder)); - return (SkFlattenable::Type)gEffectTypesInPaintFlatsOrder[pf]; -} - -static void set_paintflat(SkPaint* paint, SkFlattenable* obj, unsigned paintFlat) { - SkASSERT(paintFlat < kCount_PaintFlats); - switch (paintFlat) { - case kColorFilter_PaintFlat: - paint->setColorFilter((SkColorFilter*)obj); - break; - case kDrawLooper_PaintFlat: - paint->setLooper((SkDrawLooper*)obj); - break; - case kMaskFilter_PaintFlat: - paint->setMaskFilter((SkMaskFilter*)obj); - break; - case kPathEffect_PaintFlat: - paint->setPathEffect((SkPathEffect*)obj); - break; - case kRasterizer_PaintFlat: - paint->setRasterizer((SkRasterizer*)obj); - break; - case kShader_PaintFlat: - paint->setShader((SkShader*)obj); - break; - case kImageFilter_PaintFlat: - paint->setImageFilter((SkImageFilter*)obj); - break; - case kXfermode_PaintFlat: - paint->setXfermode((SkXfermode*)obj); - break; - default: - SkDEBUGFAIL("never gets here"); - } -} - -template class SkRefCntTDArray : public SkTDArray { -public: - ~SkRefCntTDArray() { this->unrefAll(); } -}; - -class SkGPipeState : public SkBitmapHeapReader { -public: - SkGPipeState(); - ~SkGPipeState(); - - void setSilent(bool silent) { - fSilent = silent; - } - - bool shouldDraw() { - return !fSilent; - } - - void setFlags(unsigned flags) { - if (fFlags != flags) { - fFlags = flags; - this->updateReader(); - } - } - - unsigned getFlags() const { - return fFlags; - } - - void setReader(SkReadBuffer* reader) { - fReader = reader; - this->updateReader(); - } - - const SkPaint& paint() const { return fPaint; } - SkPaint* editPaint() { return &fPaint; } - - SkFlattenable* getFlat(unsigned index) const { - if (0 == index) { - return nullptr; - } - return fFlatArray[index - 1]; - } - - void defFlattenable(PaintFlats pf, int index) { - index--; - SkFlattenable* obj = fReader->readFlattenable(paintflat_to_flattype(pf)); - if (fFlatArray.count() == index) { - *fFlatArray.append() = obj; - } else { - SkSafeUnref(fFlatArray[index]); - fFlatArray[index] = obj; - } - } - - void defFactory(const char* name) { - SkFlattenable::Factory factory = SkFlattenable::NameToFactory(name); - if (factory) { - SkASSERT(fFactoryArray.find(factory) < 0); - *fFactoryArray.append() = factory; - } - } - - /** - * Add a bitmap to the array of bitmaps, or replace an existing one. - * This is only used when in cross process mode without a shared heap. - */ - void addBitmap(int index) { - SkASSERT(shouldFlattenBitmaps(fFlags)); - SkBitmap* bm; - if(fBitmaps.count() == index) { - bm = new SkBitmap; - *fBitmaps.append() = bm; - } else { - bm = fBitmaps[index]; - } - fReader->readBitmap(bm); - } - - /** - * Override of SkBitmapHeapReader, so that SkReadBuffer can use - * these SkBitmaps for bitmap shaders. Used only in cross process mode - * without a shared heap. - */ - SkBitmap* getBitmap(int32_t index) const override { - SkASSERT(shouldFlattenBitmaps(fFlags)); - return fBitmaps[index]; - } - - /** - * Needed to be a non-abstract subclass of SkBitmapHeapReader. - */ - void releaseRef(int32_t) override {} - - void setSharedHeap(SkBitmapHeap* heap) { - SkASSERT(!shouldFlattenBitmaps(fFlags) || nullptr == heap); - SkRefCnt_SafeAssign(fSharedHeap, heap); - this->updateReader(); - } - - void setImageHeap(SkImageHeap* heap) { - fImageHeap.reset(SkRef(heap)); - } - - /** - * Access the shared heap. Only used in the case when bitmaps are not - * flattened. - */ - SkBitmapHeap* getSharedHeap() const { - SkASSERT(!shouldFlattenBitmaps(fFlags)); - return fSharedHeap; - } - - void addTypeface() { - size_t size = fReader->read32(); - const void* data = fReader->skip(SkAlign4(size)); - SkMemoryStream stream(data, size, false); - *fTypefaces.append() = SkTypeface::Deserialize(&stream); - } - - SkTypeface* getTypeface(unsigned id) const { - return id ? fTypefaces[id - 1] : nullptr; - } - - const SkImage* getImage(int32_t slot) const { - return fImageHeap->get(slot); - } - -private: - void updateReader() { - if (nullptr == fReader) { - return; - } - bool crossProcess = SkToBool(fFlags & SkGPipeWriter::kCrossProcess_Flag); - fReader->setFlags(SkSetClearMask(fReader->getFlags(), crossProcess, - SkReadBuffer::kCrossProcess_Flag)); - if (crossProcess) { - fReader->setFactoryArray(&fFactoryArray); - } else { - fReader->setFactoryArray(nullptr); - } - - if (shouldFlattenBitmaps(fFlags)) { - fReader->setBitmapStorage(this); - } else { - fReader->setBitmapStorage(fSharedHeap); - } - } - SkReadBuffer* fReader; - SkPaint fPaint; - SkTDArray fFlatArray; - SkTDArray fTypefaces; - SkTDArray fFactoryArray; - SkTDArray fBitmaps; - bool fSilent; - // Only used when sharing bitmaps with the writer. - SkBitmapHeap* fSharedHeap; - SkAutoTUnref fImageHeap; - unsigned fFlags; -}; - -/////////////////////////////////////////////////////////////////////////////// - -template const T* skip(SkReader32* reader, size_t count = 1) { - size_t size = sizeof(T) * count; - SkASSERT(SkAlign4(size) == size); - return reinterpret_cast(reader->skip(size)); -} - -template const T* skipAlign(SkReader32* reader, size_t count = 1) { - size_t size = SkAlign4(sizeof(T) * count); - return reinterpret_cast(reader->skip(size)); -} - -/////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////// - -static void clipPath_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32, - SkGPipeState* state) { - SkPath path; - reader->readPath(&path); - bool doAA = SkToBool(DrawOp_unpackFlags(op32) & kClip_HasAntiAlias_DrawOpFlag); - canvas->clipPath(path, (SkRegion::Op)DrawOp_unpackData(op32), doAA); -} - -static void clipRegion_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32, - SkGPipeState* state) { - SkRegion rgn; - reader->readRegion(&rgn); - canvas->clipRegion(rgn, (SkRegion::Op)DrawOp_unpackData(op32)); -} - -static void clipRect_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32, - SkGPipeState* state) { - const SkRect* rect = skip(reader); - bool doAA = SkToBool(DrawOp_unpackFlags(op32) & kClip_HasAntiAlias_DrawOpFlag); - canvas->clipRect(*rect, (SkRegion::Op)DrawOp_unpackData(op32), doAA); -} - -static void clipRRect_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32, - SkGPipeState* state) { - SkRRect rrect; - reader->readRRect(&rrect); - bool doAA = SkToBool(DrawOp_unpackFlags(op32) & kClip_HasAntiAlias_DrawOpFlag); - canvas->clipRRect(rrect, (SkRegion::Op)DrawOp_unpackData(op32), doAA); -} - -/////////////////////////////////////////////////////////////////////////////// - -static void setMatrix_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32, - SkGPipeState* state) { - SkMatrix matrix; - reader->readMatrix(&matrix); - canvas->setMatrix(matrix); -} - -static void concat_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32, - SkGPipeState* state) { - SkMatrix matrix; - reader->readMatrix(&matrix); - canvas->concat(matrix); -} - -static void scale_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32, - SkGPipeState* state) { - const SkScalar* param = skip(reader, 2); - canvas->scale(param[0], param[1]); -} - -static void skew_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32, - SkGPipeState* state) { - const SkScalar* param = skip(reader, 2); - canvas->skew(param[0], param[1]); -} - -static void rotate_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32, - SkGPipeState* state) { - canvas->rotate(reader->readScalar()); -} - -static void translate_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32, - SkGPipeState* state) { - const SkScalar* param = skip(reader, 2); - canvas->translate(param[0], param[1]); -} - -/////////////////////////////////////////////////////////////////////////////// - -static void save_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32, - SkGPipeState* state) { - canvas->save(); -} - -static void saveLayer_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32, - SkGPipeState* state) { - unsigned flags = DrawOp_unpackFlags(op32); - SkCanvas::SaveFlags saveFlags = (SkCanvas::SaveFlags)DrawOp_unpackData(op32); - - const SkRect* bounds = nullptr; - if (flags & kSaveLayer_HasBounds_DrawOpFlag) { - bounds = skip(reader); - } - const SkPaint* paint = nullptr; - if (flags & kSaveLayer_HasPaint_DrawOpFlag) { - paint = &state->paint(); - } - canvas->saveLayer(bounds, paint, saveFlags); -} - -static void restore_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32, - SkGPipeState* state) { - canvas->restore(); -} - -/////////////////////////////////////////////////////////////////////////////// - -static void drawPaint_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32, - SkGPipeState* state) { - if (state->shouldDraw()) { - canvas->drawPaint(state->paint()); - } -} - -static void drawPoints_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32, - SkGPipeState* state) { - SkCanvas::PointMode mode = (SkCanvas::PointMode)DrawOp_unpackFlags(op32); - size_t count = reader->readU32(); - const SkPoint* pts = skip(reader, count); - if (state->shouldDraw()) { - canvas->drawPoints(mode, count, pts, state->paint()); - } -} - -static void drawOval_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32, - SkGPipeState* state) { - const SkRect* rect = skip(reader); - if (state->shouldDraw()) { - canvas->drawOval(*rect, state->paint()); - } -} - -static void drawRect_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32, - SkGPipeState* state) { - const SkRect* rect = skip(reader); - if (state->shouldDraw()) { - canvas->drawRect(*rect, state->paint()); - } -} - -static void drawRRect_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32, - SkGPipeState* state) { - SkRRect rrect; - reader->readRRect(&rrect); - if (state->shouldDraw()) { - canvas->drawRRect(rrect, state->paint()); - } -} - -static void drawDRRect_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32, - SkGPipeState* state) { - SkRRect outer, inner; - reader->readRRect(&outer); - reader->readRRect(&inner); - if (state->shouldDraw()) { - canvas->drawDRRect(outer, inner, state->paint()); - } -} - -static void drawPatch_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32, - SkGPipeState* state) { - - unsigned flags = DrawOp_unpackFlags(op32); - - const SkPoint* cubics = skip(reader, SkPatchUtils::kNumCtrlPts); - - const SkColor* colors = nullptr; - if (flags & kDrawVertices_HasColors_DrawOpFlag) { - colors = skip(reader, SkPatchUtils::kNumCorners); - } - const SkPoint* texCoords = nullptr; - if (flags & kDrawVertices_HasTexs_DrawOpFlag) { - texCoords = skip(reader, SkPatchUtils::kNumCorners); - } - SkAutoTUnref xfer; - if (flags & kDrawVertices_HasXfermode_DrawOpFlag) { - int mode = reader->readInt(); - if (mode < 0 || mode > SkXfermode::kLastMode) { - mode = SkXfermode::kModulate_Mode; - } - xfer.reset(SkXfermode::Create((SkXfermode::Mode)mode)); - } - if (state->shouldDraw()) { - canvas->drawPatch(cubics, colors, texCoords, xfer, state->paint()); - } -} - -static void drawPath_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32, - SkGPipeState* state) { - SkPath path; - reader->readPath(&path); - if (state->shouldDraw()) { - canvas->drawPath(path, state->paint()); - } -} - -static void drawVertices_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32, - SkGPipeState* state) { - unsigned flags = DrawOp_unpackFlags(op32); - - SkCanvas::VertexMode vmode = (SkCanvas::VertexMode)reader->readU32(); - int vertexCount = reader->readU32(); - const SkPoint* verts = skip(reader, vertexCount); - - const SkPoint* texs = nullptr; - if (flags & kDrawVertices_HasTexs_DrawOpFlag) { - texs = skip(reader, vertexCount); - } - - const SkColor* colors = nullptr; - if (flags & kDrawVertices_HasColors_DrawOpFlag) { - colors = skip(reader, vertexCount); - } - - SkAutoTUnref xfer; - if (flags & kDrawVertices_HasXfermode_DrawOpFlag) { - SkXfermode::Mode mode = (SkXfermode::Mode)reader->readU32(); - xfer.reset(SkXfermode::Create(mode)); - } - - int indexCount = 0; - const uint16_t* indices = nullptr; - if (flags & kDrawVertices_HasIndices_DrawOpFlag) { - indexCount = reader->readU32(); - indices = skipAlign(reader, indexCount); - } - if (state->shouldDraw()) { - canvas->drawVertices(vmode, vertexCount, verts, texs, colors, xfer, - indices, indexCount, state->paint()); - } -} - -static void drawAtlas_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32, SkGPipeState* state) { - unsigned flags = DrawOp_unpackFlags(op32); - - const SkPaint* paint = nullptr; - if (flags & kDrawAtlas_HasPaint_DrawOpFlag) { - paint = &state->paint(); - } - const int slot = reader->readU32(); - const SkImage* atlas = state->getImage(slot); - const int count = reader->readU32(); - SkXfermode::Mode mode = (SkXfermode::Mode)reader->readU32(); - const SkRSXform* xform = skip(reader, count); - const SkRect* tex = skip(reader, count); - const SkColor* colors = nullptr; - if (flags & kDrawAtlas_HasColors_DrawOpFlag) { - colors = skip(reader, count); - } - const SkRect* cull = nullptr; - if (flags & kDrawAtlas_HasCull_DrawOpFlag) { - cull = skip(reader, 1); - } - - if (state->shouldDraw()) { - canvas->drawAtlas(atlas, xform, tex, colors, count, mode, cull, paint); - } -} - -/////////////////////////////////////////////////////////////////////////////// - -static void drawText_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32, - SkGPipeState* state) { - size_t len = reader->readU32(); - const void* text = reader->skip(SkAlign4(len)); - const SkScalar* xy = skip(reader, 2); - if (state->shouldDraw()) { - canvas->drawText(text, len, xy[0], xy[1], state->paint()); - } -} - -static void drawPosText_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32, - SkGPipeState* state) { - size_t len = reader->readU32(); - const void* text = reader->skip(SkAlign4(len)); - size_t posCount = reader->readU32(); // compute by our writer - const SkPoint* pos = skip(reader, posCount); - if (state->shouldDraw()) { - canvas->drawPosText(text, len, pos, state->paint()); - } -} - -static void drawPosTextH_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32, - SkGPipeState* state) { - size_t len = reader->readU32(); - const void* text = reader->skip(SkAlign4(len)); - size_t posCount = reader->readU32(); // compute by our writer - const SkScalar* xpos = skip(reader, posCount); - SkScalar constY = reader->readScalar(); - if (state->shouldDraw()) { - canvas->drawPosTextH(text, len, xpos, constY, state->paint()); - } -} - -static void drawTextOnPath_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32, - SkGPipeState* state) { - size_t len = reader->readU32(); - const void* text = reader->skip(SkAlign4(len)); - - SkPath path; - reader->readPath(&path); - - SkMatrix matrixStorage; - const SkMatrix* matrix = nullptr; - if (DrawOp_unpackFlags(op32) & kDrawTextOnPath_HasMatrix_DrawOpFlag) { - reader->readMatrix(&matrixStorage); - matrix = &matrixStorage; - } - if (state->shouldDraw()) { - canvas->drawTextOnPath(text, len, path, matrix, state->paint()); - } -} - -/////////////////////////////////////////////////////////////////////////////// - -class BitmapHolder : SkNoncopyable { -public: - BitmapHolder(SkReader32* reader, uint32_t op32, SkGPipeState* state); - ~BitmapHolder() { - if (fHeapEntry != nullptr) { - fHeapEntry->releaseRef(); - } - } - const SkBitmap* getBitmap() { - return fBitmap; - } -private: - SkBitmapHeapEntry* fHeapEntry; - const SkBitmap* fBitmap; - SkBitmap fBitmapStorage; -}; - -BitmapHolder::BitmapHolder(SkReader32* reader, uint32_t op32, - SkGPipeState* state) { - const unsigned flags = state->getFlags(); - const unsigned index = DrawOp_unpackData(op32); - if (shouldFlattenBitmaps(flags)) { - fHeapEntry = nullptr; - fBitmap = state->getBitmap(index); - } else { - SkBitmapHeapEntry* entry = state->getSharedHeap()->getEntry(index); - if (SkToBool(flags & SkGPipeWriter::kSimultaneousReaders_Flag)) { - // Make a shallow copy for thread safety. Each thread will point to the same SkPixelRef, - // which is thread safe. - fBitmapStorage = *entry->getBitmap(); - fBitmap = &fBitmapStorage; - // Release the ref on the bitmap now, since we made our own copy. - entry->releaseRef(); - fHeapEntry = nullptr; - } else { - SkASSERT(!shouldFlattenBitmaps(flags)); - SkASSERT(!SkToBool(flags & SkGPipeWriter::kSimultaneousReaders_Flag)); - fHeapEntry = entry; - fBitmap = fHeapEntry->getBitmap(); - } - } -} - -static void drawBitmap_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32, - SkGPipeState* state) { - BitmapHolder holder(reader, op32, state); - bool hasPaint = SkToBool(DrawOp_unpackFlags(op32) & kDrawBitmap_HasPaint_DrawOpFlag); - SkScalar left = reader->readScalar(); - SkScalar top = reader->readScalar(); - const SkBitmap* bitmap = holder.getBitmap(); - if (state->shouldDraw()) { - canvas->drawBitmap(*bitmap, left, top, hasPaint ? &state->paint() : nullptr); - } -} - -static void drawBitmapNine_rp(SkCanvas* canvas, SkReader32* reader, - uint32_t op32, SkGPipeState* state) { - BitmapHolder holder(reader, op32, state); - bool hasPaint = SkToBool(DrawOp_unpackFlags(op32) & kDrawBitmap_HasPaint_DrawOpFlag); - const SkIRect* center = skip(reader); - const SkRect* dst = skip(reader); - const SkBitmap* bitmap = holder.getBitmap(); - if (state->shouldDraw()) { - canvas->drawBitmapNine(*bitmap, *center, *dst, - hasPaint ? &state->paint() : nullptr); - } -} - -static void drawBitmapRect_rp(SkCanvas* canvas, SkReader32* reader, - uint32_t op32, SkGPipeState* state) { - BitmapHolder holder(reader, op32, state); - unsigned flags = DrawOp_unpackFlags(op32); - bool hasPaint = SkToBool(flags & kDrawBitmap_HasPaint_DrawOpFlag); - bool hasSrc = SkToBool(flags & kDrawBitmap_HasSrcRect_DrawOpFlag); - const SkRect* src; - if (hasSrc) { - src = skip(reader); - } else { - src = nullptr; - } - SkCanvas::SrcRectConstraint constraint = SkCanvas::kStrict_SrcRectConstraint; - if (flags & kDrawBitmap_Bleed_DrawOpFlag) { - constraint = SkCanvas::kFast_SrcRectConstraint; - } - const SkRect* dst = skip(reader); - const SkBitmap* bitmap = holder.getBitmap(); - if (state->shouldDraw()) { - canvas->legacy_drawBitmapRect(*bitmap, src, *dst, hasPaint ? &state->paint() : nullptr, constraint); - } -} - -static void drawSprite_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32, - SkGPipeState* state) { - BitmapHolder holder(reader, op32, state); - bool hasPaint = SkToBool(DrawOp_unpackFlags(op32) & kDrawBitmap_HasPaint_DrawOpFlag); - const SkIPoint* point = skip(reader); - const SkBitmap* bitmap = holder.getBitmap(); - if (state->shouldDraw()) { - canvas->drawSprite(*bitmap, point->fX, point->fY, hasPaint ? &state->paint() : nullptr); - } -} - -static void drawImage_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32, SkGPipeState* state) { - unsigned slot = DrawOp_unpackData(op32); - unsigned flags = DrawOp_unpackFlags(op32); - bool hasPaint = SkToBool(flags & kDrawBitmap_HasPaint_DrawOpFlag); - SkScalar x = reader->readScalar(); - SkScalar y = reader->readScalar(); - const SkImage* image = state->getImage(slot); - if (state->shouldDraw()) { - canvas->drawImage(image, x, y, hasPaint ? &state->paint() : nullptr); - } -} - -static void drawImageRect_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32, - SkGPipeState* state) { - unsigned slot = DrawOp_unpackData(op32); - unsigned flags = DrawOp_unpackFlags(op32); - bool hasPaint = SkToBool(flags & kDrawBitmap_HasPaint_DrawOpFlag); - bool hasSrc = SkToBool(flags & kDrawBitmap_HasSrcRect_DrawOpFlag); - const SkRect* src = nullptr; - if (hasSrc) { - src = skip(reader); - } - const SkRect* dst = skip(reader); - SkCanvas::SrcRectConstraint constraint = (SkCanvas::SrcRectConstraint)reader->readInt(); - - const SkImage* image = state->getImage(slot); - if (state->shouldDraw()) { - canvas->legacy_drawImageRect(image, src, *dst, hasPaint ? &state->paint() : nullptr, constraint); - } -} - -static void drawImageNine_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32, - SkGPipeState* state) { - unsigned slot = DrawOp_unpackData(op32); - unsigned flags = DrawOp_unpackFlags(op32); - bool hasPaint = SkToBool(flags & kDrawBitmap_HasPaint_DrawOpFlag); - const SkIRect* center = skip(reader); - const SkRect* dst = skip(reader); - const SkImage* image = state->getImage(slot); - if (state->shouldDraw()) { - canvas->drawImageNine(image, *center, *dst, hasPaint ? &state->paint() : nullptr); - } -} - -/////////////////////////////////////////////////////////////////////////////// - -static void drawPicture_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32, - SkGPipeState* state) { - UNIMPLEMENTED -} - -static void drawTextBlob_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32, - SkGPipeState* state) { - SkScalar x = reader->readScalar(); - SkScalar y = reader->readScalar(); - - int typefaceCount = reader->readU32(); - SkAutoSTMalloc<16, SkTypeface*> typefaceArray(typefaceCount); - if (state->getFlags() & SkGPipeWriter::kCrossProcess_Flag) { - for (int i = 0; i < typefaceCount; ++i) { - typefaceArray[i] = state->getTypeface(reader->readU32()); - } - } else { - reader->read(typefaceArray.get(), typefaceCount * sizeof(SkTypeface*)); - } - - size_t blobSize = reader->readU32(); - const void* data = reader->skip(SkAlign4(blobSize)); - - if (state->shouldDraw()) { - SkReadBuffer blobBuffer(data, blobSize); - blobBuffer.setTypefaceArray(typefaceArray.get(), typefaceCount); - SkAutoTUnref blob(SkTextBlob::CreateFromBuffer(blobBuffer)); - SkASSERT(blob.get()); - - canvas->drawTextBlob(blob, x, y, state->paint()); - } -} -/////////////////////////////////////////////////////////////////////////////// - -static void paintOp_rp(SkCanvas*, SkReader32* reader, uint32_t op32, - SkGPipeState* state) { - size_t offset = reader->offset(); - size_t stop = offset + PaintOp_unpackData(op32); - SkPaint* p = state->editPaint(); - - do { - uint32_t p32 = reader->readU32(); - unsigned op = PaintOp_unpackOp(p32); - unsigned data = PaintOp_unpackData(p32); - -// SkDebugf(" read %08X op=%d flags=%d data=%d\n", p32, op, done, data); - - switch (op) { - case kReset_PaintOp: p->reset(); break; - case kFlags_PaintOp: p->setFlags(data); break; - case kColor_PaintOp: p->setColor(reader->readU32()); break; - case kFilterLevel_PaintOp: p->setFilterQuality((SkFilterQuality)data); break; - case kStyle_PaintOp: p->setStyle((SkPaint::Style)data); break; - case kJoin_PaintOp: p->setStrokeJoin((SkPaint::Join)data); break; - case kCap_PaintOp: p->setStrokeCap((SkPaint::Cap)data); break; - case kWidth_PaintOp: p->setStrokeWidth(reader->readScalar()); break; - case kMiter_PaintOp: p->setStrokeMiter(reader->readScalar()); break; - case kEncoding_PaintOp: - p->setTextEncoding((SkPaint::TextEncoding)data); - break; - case kHinting_PaintOp: p->setHinting((SkPaint::Hinting)data); break; - case kAlign_PaintOp: p->setTextAlign((SkPaint::Align)data); break; - case kTextSize_PaintOp: p->setTextSize(reader->readScalar()); break; - case kTextScaleX_PaintOp: p->setTextScaleX(reader->readScalar()); break; - case kTextSkewX_PaintOp: p->setTextSkewX(reader->readScalar()); break; - - case kFlatIndex_PaintOp: { - PaintFlats pf = (PaintFlats)PaintOp_unpackFlags(p32); - unsigned index = data; - set_paintflat(p, state->getFlat(index), pf); - break; - } - - case kTypeface_PaintOp: - SkASSERT(SkToBool(state->getFlags() & - SkGPipeWriter::kCrossProcess_Flag)); - p->setTypeface(state->getTypeface(data)); - break; - default: SkDEBUGFAIL("bad paintop"); return; - } - SkASSERT(reader->offset() <= stop); - } while (reader->offset() < stop); -} - -static void typeface_rp(SkCanvas*, SkReader32* reader, uint32_t, - SkGPipeState* state) { - SkASSERT(!SkToBool(state->getFlags() & SkGPipeWriter::kCrossProcess_Flag)); - SkPaint* p = state->editPaint(); - p->setTypeface(static_cast(reader->readPtr())); -} - -static void annotation_rp(SkCanvas*, SkReader32* reader, uint32_t op32, - SkGPipeState* state) { - SkPaint* p = state->editPaint(); - - const size_t size = DrawOp_unpackData(op32); - if (size > 0) { - SkReadBuffer buffer(reader->skip(size), size); - p->setAnnotation(SkAnnotation::Create(buffer))->unref(); - SkASSERT(buffer.offset() == size); - } else { - p->setAnnotation(nullptr); - } -} - -/////////////////////////////////////////////////////////////////////////////// - -static void def_Typeface_rp(SkCanvas*, SkReader32*, uint32_t, SkGPipeState* state) { - state->addTypeface(); -} - -static void def_PaintFlat_rp(SkCanvas*, SkReader32*, uint32_t op32, - SkGPipeState* state) { - PaintFlats pf = (PaintFlats)DrawOp_unpackFlags(op32); - unsigned index = DrawOp_unpackData(op32); - state->defFlattenable(pf, index); -} - -static void def_Bitmap_rp(SkCanvas*, SkReader32*, uint32_t op32, - SkGPipeState* state) { - unsigned index = DrawOp_unpackData(op32); - state->addBitmap(index); -} - -static void def_Factory_rp(SkCanvas*, SkReader32* reader, uint32_t, - SkGPipeState* state) { - state->defFactory(reader->readString()); -} - -/////////////////////////////////////////////////////////////////////////////// - -static void skip_rp(SkCanvas*, SkReader32* reader, uint32_t op32, SkGPipeState*) { - size_t bytes = DrawOp_unpackData(op32); - (void)reader->skip(bytes); -} - -static void reportFlags_rp(SkCanvas*, SkReader32*, uint32_t op32, - SkGPipeState* state) { - unsigned flags = DrawOp_unpackFlags(op32); - state->setFlags(flags); -} - -static void shareBitmapHeap_rp(SkCanvas*, SkReader32* reader, uint32_t, - SkGPipeState* state) { - state->setSharedHeap(static_cast(reader->readPtr())); -} - -static void shareImageHeap_rp(SkCanvas*, SkReader32* reader, uint32_t, SkGPipeState* state) { - state->setImageHeap(static_cast(reader->readPtr())); -} - -static void done_rp(SkCanvas*, SkReader32*, uint32_t, SkGPipeState*) {} - -typedef void (*ReadProc)(SkCanvas*, SkReader32*, uint32_t op32, SkGPipeState*); - -static const ReadProc gReadTable[] = { - skip_rp, - clipPath_rp, - clipRegion_rp, - clipRect_rp, - clipRRect_rp, - concat_rp, - drawAtlas_rp, - drawBitmap_rp, - drawBitmapNine_rp, - drawBitmapRect_rp, - drawDRRect_rp, - drawImage_rp, - drawImageRect_rp, - drawImageNine_rp, - drawOval_rp, - drawPaint_rp, - drawPatch_rp, - drawPath_rp, - drawPicture_rp, - drawPoints_rp, - drawPosText_rp, - drawPosTextH_rp, - drawRect_rp, - drawRRect_rp, - drawSprite_rp, - drawText_rp, - drawTextBlob_rp, - drawTextOnPath_rp, - drawVertices_rp, - restore_rp, - rotate_rp, - save_rp, - saveLayer_rp, - scale_rp, - setMatrix_rp, - skew_rp, - translate_rp, - - paintOp_rp, - typeface_rp, - annotation_rp, - - def_Typeface_rp, - def_PaintFlat_rp, - def_Bitmap_rp, - def_Factory_rp, - - reportFlags_rp, - shareBitmapHeap_rp, - shareImageHeap_rp, - done_rp -}; - -/////////////////////////////////////////////////////////////////////////////// - -SkGPipeState::SkGPipeState() - : fReader(0) - , fSilent(false) - , fSharedHeap(nullptr) - , fFlags(0) { - -} - -SkGPipeState::~SkGPipeState() { - fTypefaces.safeUnrefAll(); - fFlatArray.safeUnrefAll(); - fBitmaps.deleteAll(); - SkSafeUnref(fSharedHeap); -} - -/////////////////////////////////////////////////////////////////////////////// - -#include "SkGPipe.h" - -SkGPipeReader::SkGPipeReader() { - fCanvas = nullptr; - fState = nullptr; - fProc = nullptr; -} - -SkGPipeReader::SkGPipeReader(SkCanvas* target) { - fCanvas = nullptr; - this->setCanvas(target); - fState = nullptr; - fProc = nullptr; -} - -void SkGPipeReader::setCanvas(SkCanvas *target) { - SkRefCnt_SafeAssign(fCanvas, target); -} - -SkGPipeReader::~SkGPipeReader() { - SkSafeUnref(fCanvas); - delete fState; -} - -SkGPipeReader::Status SkGPipeReader::playback(const void* data, size_t length, - uint32_t playbackFlags, size_t* bytesRead) { - if (nullptr == fCanvas) { - return kError_Status; - } - - if (nullptr == fState) { - fState = new SkGPipeState; - } - - fState->setSilent(playbackFlags & kSilent_PlaybackFlag); - - SkASSERT(SK_ARRAY_COUNT(gReadTable) == (kDone_DrawOp + 1)); - - const ReadProc* table = gReadTable; - SkReadBuffer reader(data, length); - reader.setBitmapDecoder(fProc); - SkCanvas* canvas = fCanvas; - Status status = kEOF_Status; - - fState->setReader(&reader); - while (!reader.eof()) { - uint32_t op32 = reader.readUInt(); - unsigned op = DrawOp_unpackOp(op32); - // SkDEBUGCODE(DrawOps drawOp = (DrawOps)op;) - - if (op >= SK_ARRAY_COUNT(gReadTable)) { - SkDebugf("---- bad op during GPipeState::playback\n"); - status = kError_Status; - break; - } - if (kDone_DrawOp == op) { - status = kDone_Status; - break; - } - table[op](canvas, reader.getReader32(), op32, fState); - if ((playbackFlags & kReadAtom_PlaybackFlag) && - (table[op] != paintOp_rp && - table[op] != def_Typeface_rp && - table[op] != def_PaintFlat_rp && - table[op] != def_Bitmap_rp - )) { - status = kReadAtom_Status; - break; - } - } - - if (bytesRead) { - *bytesRead = reader.offset(); - } - return status; -} diff --git a/gfx/skia/skia/src/pipe/SkGPipeWrite.cpp b/gfx/skia/skia/src/pipe/SkGPipeWrite.cpp deleted file mode 100644 index 0fccb80d0a..0000000000 --- a/gfx/skia/skia/src/pipe/SkGPipeWrite.cpp +++ /dev/null @@ -1,1495 +0,0 @@ - -/* - * Copyright 2011 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "SkAnnotation.h" -#include "SkBitmapHeap.h" -#include "SkCanvas.h" -#include "SkColorFilter.h" -#include "SkData.h" -#include "SkDrawLooper.h" -#include "SkGPipe.h" -#include "SkGPipePriv.h" -#include "SkImageFilter.h" -#include "SkMaskFilter.h" -#include "SkRSXform.h" -#include "SkWriteBuffer.h" -#include "SkPaint.h" -#include "SkPatchUtils.h" -#include "SkPathEffect.h" -#include "SkPictureFlat.h" -#include "SkPtrRecorder.h" -#include "SkRasterizer.h" -#include "SkRRect.h" -#include "SkShader.h" -#include "SkStream.h" -#include "SkTextBlob.h" -#include "SkTSearch.h" -#include "SkTypeface.h" -#include "SkWriter32.h" - -enum { - kSizeOfFlatRRect = sizeof(SkRect) + 4 * sizeof(SkVector) -}; - -static bool is_cross_process(uint32_t flags) { - return SkToBool(flags & SkGPipeWriter::kCrossProcess_Flag); -} - -static SkFlattenable* get_paintflat(const SkPaint& paint, unsigned paintFlat) { - SkASSERT(paintFlat < kCount_PaintFlats); - switch (paintFlat) { - case kColorFilter_PaintFlat: return paint.getColorFilter(); - case kDrawLooper_PaintFlat: return paint.getLooper(); - case kMaskFilter_PaintFlat: return paint.getMaskFilter(); - case kPathEffect_PaintFlat: return paint.getPathEffect(); - case kRasterizer_PaintFlat: return paint.getRasterizer(); - case kShader_PaintFlat: return paint.getShader(); - case kImageFilter_PaintFlat: return paint.getImageFilter(); - case kXfermode_PaintFlat: return paint.getXfermode(); - } - SkDEBUGFAIL("never gets here"); - return nullptr; -} - -static size_t writeTypeface(SkWriter32* writer, SkTypeface* typeface) { - SkASSERT(typeface); - SkDynamicMemoryWStream stream; - typeface->serialize(&stream); - size_t size = stream.getOffset(); - if (writer) { - writer->write32(SkToU32(size)); - SkAutoDataUnref data(stream.copyToData()); - writer->writePad(data->data(), size); - } - return 4 + SkAlign4(size); -} - -/////////////////////////////////////////////////////////////////////////////// - -class FlattenableHeap : public SkFlatController { -public: - FlattenableHeap(int numFlatsToKeep, SkNamedFactorySet* fset, bool isCrossProcess) - : INHERITED(isCrossProcess ? SkWriteBuffer::kCrossProcess_Flag : 0) - , fNumFlatsToKeep(numFlatsToKeep) { - SkASSERT((isCrossProcess && fset != nullptr) || (!isCrossProcess && nullptr == fset)); - if (isCrossProcess) { - this->setNamedFactorySet(fset); - } - } - - ~FlattenableHeap() { - fPointers.freeAll(); - } - - void* allocThrow(size_t bytes) override; - - void unalloc(void* ptr) override; - - void setBitmapStorage(SkBitmapHeap* heap) { - this->setBitmapHeap(heap); - } - - const SkFlatData* flatToReplace() const; - - // Mark an SkFlatData as one that should not be returned by flatToReplace. - // Takes the result of SkFlatData::index() as its parameter. - void markFlatForKeeping(int index) { - *fFlatsThatMustBeKept.append() = index; - } - - void markAllFlatsSafeToDelete() { - fFlatsThatMustBeKept.reset(); - } - -private: - // Keep track of the indices (i.e. the result of SkFlatData::index()) of - // flats that must be kept, since they are on the current paint. - SkTDArray fFlatsThatMustBeKept; - SkTDArray fPointers; - const int fNumFlatsToKeep; - - typedef SkFlatController INHERITED; -}; - -void FlattenableHeap::unalloc(void* ptr) { - int indexToRemove = fPointers.rfind(ptr); - if (indexToRemove >= 0) { - sk_free(ptr); - fPointers.remove(indexToRemove); - } -} - -void* FlattenableHeap::allocThrow(size_t bytes) { - void* ptr = sk_malloc_throw(bytes); - *fPointers.append() = ptr; - return ptr; -} - -const SkFlatData* FlattenableHeap::flatToReplace() const { - // First, determine whether we should replace one. - if (fPointers.count() > fNumFlatsToKeep) { - // Look through the flattenable heap. - // TODO: Return the LRU flat. - for (int i = 0; i < fPointers.count(); i++) { - SkFlatData* potential = (SkFlatData*)fPointers[i]; - // Make sure that it is not one that must be kept. - bool mustKeep = false; - for (int j = 0; j < fFlatsThatMustBeKept.count(); j++) { - if (potential->index() == fFlatsThatMustBeKept[j]) { - mustKeep = true; - break; - } - } - if (!mustKeep) { - return potential; - } - } - } - return nullptr; -} - -/////////////////////////////////////////////////////////////////////////////// - -struct SkFlattenableTraits { - static void Flatten(SkWriteBuffer& buffer, const SkFlattenable& flattenable) { - buffer.writeFlattenable(&flattenable); - } - // No need to define unflatten if we never call it. -}; -typedef SkFlatDictionary FlatDictionary; - -/////////////////////////////////////////////////////////////////////////////// - -/** - * If SkBitmaps are to be flattened to send to the reader, this class is - * provided to the SkBitmapHeap to tell the SkGPipeCanvas to do so. - */ -class BitmapShuttle : public SkBitmapHeap::ExternalStorage { -public: - BitmapShuttle(SkGPipeCanvas*); - - ~BitmapShuttle(); - - bool insert(const SkBitmap& bitmap, int32_t slot) override; - - /** - * Remove the SkGPipeCanvas used for insertion. After this, calls to - * insert will crash. - */ - void removeCanvas(); - -private: - SkGPipeCanvas* fCanvas; -}; - -/////////////////////////////////////////////////////////////////////////////// - -class SkGPipeCanvas : public SkCanvas { -public: - SkGPipeCanvas(SkGPipeController*, SkWriter32*, uint32_t flags, - uint32_t width, uint32_t height); - virtual ~SkGPipeCanvas(); - - /** - * Called when nothing else is to be written to the stream. Any repeated - * calls are ignored. - * - * @param notifyReaders Whether to send a message to the reader(s) that - * the writer is through sending commands. Should generally be true, - * unless there is an error which prevents further messages from - * being sent. - */ - void finish(bool notifyReaders) { - if (fDone) { - return; - } - if (notifyReaders && this->needOpBytes()) { - this->writeOp(kDone_DrawOp); - this->doNotify(); - } - if (shouldFlattenBitmaps(fFlags)) { - // The following circular references exist: - // fFlattenableHeap -> fWriteBuffer -> fBitmapStorage -> fExternalStorage -> fCanvas - // fBitmapHeap -> fExternalStorage -> fCanvas - // fFlattenableHeap -> fBitmapStorage -> fExternalStorage -> fCanvas - - // Break them all by destroying the final link to this SkGPipeCanvas. - fBitmapShuttle->removeCanvas(); - } - fDone = true; - } - - void flushRecording(bool detachCurrentBlock); - size_t freeMemoryIfPossible(size_t bytesToFree); - - size_t storageAllocatedForRecording() { - size_t bytesAllocated = 0; - if (nullptr != fBitmapHeap) { - bytesAllocated += fBitmapHeap->bytesAllocated(); - } - if (nullptr != fImageHeap) { - bytesAllocated += fImageHeap->bytesInCache(); - } - return bytesAllocated; - } - - /** - * Flatten an SkBitmap to send to the reader, where it will be referenced - * according to slot. - */ - bool shuttleBitmap(const SkBitmap&, int32_t slot); - - void resetImageHeap(); - -protected: - void willSave() override; - SaveLayerStrategy willSaveLayer(const SkRect*, const SkPaint*, SaveFlags) override; - void willRestore() override; - - void didConcat(const SkMatrix&) override; - void didSetMatrix(const SkMatrix&) override; - - void onDrawDRRect(const SkRRect&, const SkRRect&, const SkPaint&) override; - void onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y, - const SkPaint&) override; - void onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[], - const SkPaint&) override; - void onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[], - SkScalar constY, const SkPaint&) override; - void onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path, - const SkMatrix* matrix, const SkPaint&) override; - void onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y, - const SkPaint& paint) override; - void onDrawPatch(const SkPoint cubics[12], const SkColor colors[4], - const SkPoint texCoords[4], SkXfermode* xmode, - const SkPaint& paint) override; - void onDrawPaint(const SkPaint&) override; - void onDrawPoints(PointMode, size_t count, const SkPoint pts[], const SkPaint&) override; - void onDrawRect(const SkRect&, const SkPaint&) override; - void onDrawOval(const SkRect&, const SkPaint&) override; - void onDrawRRect(const SkRRect&, const SkPaint&) override; - void onDrawPath(const SkPath&, const SkPaint&) override; - void onDrawBitmap(const SkBitmap&, SkScalar left, SkScalar top, const SkPaint*) override; - void onDrawBitmapRect(const SkBitmap&, const SkRect* src, const SkRect& dst, const SkPaint*, - SrcRectConstraint) override; - void onDrawImage(const SkImage*, SkScalar left, SkScalar top, const SkPaint*) override; - void onDrawImageRect(const SkImage*, const SkRect* src, const SkRect& dst, - const SkPaint*, SrcRectConstraint) override; - void onDrawImageNine(const SkImage*, const SkIRect& center, const SkRect& dst, - const SkPaint*) override; - void onDrawBitmapNine(const SkBitmap&, const SkIRect& center, const SkRect& dst, - const SkPaint*) override; - void onDrawSprite(const SkBitmap&, int left, int top, const SkPaint*) override; - void onDrawVertices(VertexMode vmode, int vertexCount, - const SkPoint vertices[], const SkPoint texs[], - const SkColor colors[], SkXfermode* xmode, - const uint16_t indices[], int indexCount, - const SkPaint&) override; - void onDrawAtlas(const SkImage*, const SkRSXform[], const SkRect[], const SkColor[], - int count, SkXfermode::Mode, const SkRect* cull, const SkPaint*) override; - void onClipRect(const SkRect&, SkRegion::Op, ClipEdgeStyle) override; - void onClipRRect(const SkRRect&, SkRegion::Op, ClipEdgeStyle) override; - void onClipPath(const SkPath&, SkRegion::Op, ClipEdgeStyle) override; - void onClipRegion(const SkRegion&, SkRegion::Op) override; - - void onDrawPicture(const SkPicture*, const SkMatrix*, const SkPaint*) override; - -private: - void recordTranslate(const SkMatrix&); - void recordScale(const SkMatrix&); - void recordConcat(const SkMatrix&); - - SkNamedFactorySet* fFactorySet; - SkBitmapHeap* fBitmapHeap; - SkImageHeap* fImageHeap; - SkGPipeController* fController; - SkWriter32& fWriter; - size_t fBlockSize; // amount allocated for writer - size_t fBytesNotified; - bool fDone; - const uint32_t fFlags; - - SkRefCntSet fTypefaceSet; - - uint32_t getTypefaceID(SkTypeface*); - - inline void writeOp(DrawOps op, unsigned flags, unsigned data) { - fWriter.write32(DrawOp_packOpFlagData(op, flags, data)); - } - - inline void writeOp(DrawOps op) { - fWriter.write32(DrawOp_packOpFlagData(op, 0, 0)); - } - - bool needOpBytes(size_t size = 0); - - inline void doNotify() { - if (!fDone) { - size_t bytes = fWriter.bytesWritten() - fBytesNotified; - if (bytes > 0) { - fController->notifyWritten(bytes); - fBytesNotified += bytes; - } - } - } - - typedef SkAutoSTMalloc<128, uint8_t> TypefaceBuffer; - size_t getInProcessTypefaces(const SkRefCntSet& typefaceSet, TypefaceBuffer*); - size_t getCrossProcessTypefaces(const SkRefCntSet& typefaceSet, TypefaceBuffer*); - - // Should be called after any calls to an SkFlatDictionary::findAndReplace - // if a new SkFlatData was added when in cross process mode - void flattenFactoryNames(); - - FlattenableHeap fFlattenableHeap; - FlatDictionary fFlatDictionary; - SkAutoTUnref fBitmapShuttle; - int fCurrFlatIndex[kCount_PaintFlats]; - - int flattenToIndex(SkFlattenable* obj, PaintFlats); - - // Common code used by drawBitmap*. Behaves differently depending on the - // type of SkBitmapHeap being used, which is determined by the flags used. - bool commonDrawBitmap(const SkBitmap&, DrawOps, unsigned flags, size_t bytes, const SkPaint*); - bool commonDrawImage(const SkImage*, DrawOps, unsigned flags, size_t bytes, const SkPaint*); - - SkPaint fPaint; - void writePaint(const SkPaint&); - - class AutoPipeNotify { - public: - AutoPipeNotify(SkGPipeCanvas* canvas) : fCanvas(canvas) {} - ~AutoPipeNotify() { fCanvas->doNotify(); } - private: - SkGPipeCanvas* fCanvas; - }; - friend class AutoPipeNotify; - - typedef SkCanvas INHERITED; -}; - -void SkGPipeCanvas::flattenFactoryNames() { - const char* name; - while ((name = fFactorySet->getNextAddedFactoryName()) != nullptr) { - size_t len = strlen(name); - if (this->needOpBytes(SkWriter32::WriteStringSize(name, len))) { - this->writeOp(kDef_Factory_DrawOp); - fWriter.writeString(name, len); - } - } -} - -bool SkGPipeCanvas::shuttleBitmap(const SkBitmap& bm, int32_t slot) { - SkASSERT(shouldFlattenBitmaps(fFlags)); - SkWriteBuffer buffer; - buffer.setNamedFactoryRecorder(fFactorySet); - buffer.writeBitmap(bm); - this->flattenFactoryNames(); - size_t size = buffer.bytesWritten(); - if (this->needOpBytes(size)) { - this->writeOp(kDef_Bitmap_DrawOp, 0, slot); - void* dst = static_cast(fWriter.reserve(size)); - buffer.writeToMemory(dst); - return true; - } - return false; -} - -// return 0 for nullptr (or unflattenable obj), or index-base-1 -// return ~(index-base-1) if an old flattenable was replaced -int SkGPipeCanvas::flattenToIndex(SkFlattenable* obj, PaintFlats paintflat) { - SkASSERT(!fDone && fBitmapHeap != nullptr); - if (nullptr == obj) { - return 0; - } - - fBitmapHeap->deferAddingOwners(); - bool added, replaced; - const SkFlatData* flat = fFlatDictionary.findAndReplace(*obj, fFlattenableHeap.flatToReplace(), - &added, &replaced); - fBitmapHeap->endAddingOwnersDeferral(added); - int index = flat->index(); - if (added) { - if (is_cross_process(fFlags)) { - this->flattenFactoryNames(); - } - size_t flatSize = flat->flatSize(); - if (this->needOpBytes(flatSize)) { - this->writeOp(kDef_Flattenable_DrawOp, paintflat, index); - fWriter.write(flat->data(), flatSize); - } - } - if (replaced) { - index = ~index; - } - return index; -} - -/////////////////////////////////////////////////////////////////////////////// - -#define MIN_BLOCK_SIZE (16 * 1024) -#define BITMAPS_TO_KEEP 5 -#define FLATTENABLES_TO_KEEP 10 - -SkGPipeCanvas::SkGPipeCanvas(SkGPipeController* controller, - SkWriter32* writer, - uint32_t flags, - uint32_t width, - uint32_t height) - : SkCanvas(width, height) - , fFactorySet(is_cross_process(flags) ? new SkNamedFactorySet : nullptr) - , fWriter(*writer) - , fFlags(flags) - , fFlattenableHeap(FLATTENABLES_TO_KEEP, fFactorySet, is_cross_process(flags)) - , fFlatDictionary(&fFlattenableHeap) { - fController = controller; - fDone = false; - fBlockSize = 0; // need first block from controller - fBytesNotified = 0; - sk_bzero(fCurrFlatIndex, sizeof(fCurrFlatIndex)); - - // Tell the reader the appropriate flags to use. - if (this->needOpBytes()) { - this->writeOp(kReportFlags_DrawOp, fFlags, 0); - } - - if (shouldFlattenBitmaps(flags)) { - fBitmapShuttle.reset(new BitmapShuttle(this)); - fBitmapHeap = new SkBitmapHeap(fBitmapShuttle.get(), BITMAPS_TO_KEEP); - } else { - fBitmapHeap = new SkBitmapHeap(BITMAPS_TO_KEEP, controller->numberOfReaders()); - if (this->needOpBytes(sizeof(void*))) { - this->writeOp(kShareBitmapHeap_DrawOp); - fWriter.writePtr(static_cast(fBitmapHeap)); - } - } - fFlattenableHeap.setBitmapStorage(fBitmapHeap); - - fImageHeap = new SkImageHeap; - if (this->needOpBytes(sizeof(void*))) { - this->writeOp(kShareImageHeap_DrawOp); - fWriter.writePtr(static_cast(fImageHeap)); - } - - this->doNotify(); -} - -SkGPipeCanvas::~SkGPipeCanvas() { - this->finish(true); - SkSafeUnref(fFactorySet); - SkSafeUnref(fBitmapHeap); - SkSafeUnref(fImageHeap); -} - -bool SkGPipeCanvas::needOpBytes(size_t needed) { - if (fDone) { - return false; - } - - needed += 4; // size of DrawOp atom - needed = SkAlign4(needed); - if (fWriter.bytesWritten() + needed > fBlockSize) { - // Before we wipe out any data that has already been written, read it out. - this->doNotify(); - - // If we're going to allocate a new block, allocate enough to make it worthwhile. - needed = SkTMax(MIN_BLOCK_SIZE, needed); - - void* block = fController->requestBlock(needed, &fBlockSize); - if (nullptr == block) { - // Do not notify the readers, which would call this function again. - this->finish(false); - return false; - } - SkASSERT(SkIsAlign4(fBlockSize)); - fWriter.reset(block, fBlockSize); - fBytesNotified = 0; - } - return true; -} - -uint32_t SkGPipeCanvas::getTypefaceID(SkTypeface* face) { - uint32_t id = 0; // 0 means default/null typeface - if (face) { - id = fTypefaceSet.find(face); - if (0 == id) { - id = fTypefaceSet.add(face); - size_t size = writeTypeface(nullptr, face); - if (this->needOpBytes(size)) { - this->writeOp(kDef_Typeface_DrawOp); - writeTypeface(&fWriter, face); - } - } - } - return id; -} - -/////////////////////////////////////////////////////////////////////////////// - -#define NOTIFY_SETUP(canvas) \ - AutoPipeNotify apn(canvas) - -void SkGPipeCanvas::willSave() { - NOTIFY_SETUP(this); - if (this->needOpBytes()) { - this->writeOp(kSave_DrawOp); - } - - this->INHERITED::willSave(); -} - -SkCanvas::SaveLayerStrategy SkGPipeCanvas::willSaveLayer(const SkRect* bounds, const SkPaint* paint, - SaveFlags saveFlags) { - NOTIFY_SETUP(this); - size_t size = 0; - unsigned opFlags = 0; - - if (bounds) { - opFlags |= kSaveLayer_HasBounds_DrawOpFlag; - size += sizeof(SkRect); - } - if (paint) { - opFlags |= kSaveLayer_HasPaint_DrawOpFlag; - this->writePaint(*paint); - } - - if (this->needOpBytes(size)) { - this->writeOp(kSaveLayer_DrawOp, opFlags, saveFlags); - if (bounds) { - fWriter.writeRect(*bounds); - } - } - - this->INHERITED::willSaveLayer(bounds, paint, saveFlags); - // we don't create a layer - return kNoLayer_SaveLayerStrategy; -} - -void SkGPipeCanvas::willRestore() { - NOTIFY_SETUP(this); - if (this->needOpBytes()) { - this->writeOp(kRestore_DrawOp); - } - - this->INHERITED::willRestore(); -} - -void SkGPipeCanvas::recordTranslate(const SkMatrix& m) { - if (this->needOpBytes(2 * sizeof(SkScalar))) { - this->writeOp(kTranslate_DrawOp); - fWriter.writeScalar(m.getTranslateX()); - fWriter.writeScalar(m.getTranslateY()); - } -} - -void SkGPipeCanvas::recordScale(const SkMatrix& m) { - if (this->needOpBytes(2 * sizeof(SkScalar))) { - this->writeOp(kScale_DrawOp); - fWriter.writeScalar(m.getScaleX()); - fWriter.writeScalar(m.getScaleY()); - } -} - -void SkGPipeCanvas::recordConcat(const SkMatrix& m) { - if (this->needOpBytes(m.writeToMemory(nullptr))) { - this->writeOp(kConcat_DrawOp); - fWriter.writeMatrix(m); - } -} - -void SkGPipeCanvas::didConcat(const SkMatrix& matrix) { - if (!matrix.isIdentity()) { - NOTIFY_SETUP(this); - switch (matrix.getType()) { - case SkMatrix::kTranslate_Mask: - this->recordTranslate(matrix); - break; - case SkMatrix::kScale_Mask: - this->recordScale(matrix); - break; - default: - this->recordConcat(matrix); - break; - } - } - - this->INHERITED::didConcat(matrix); -} - -void SkGPipeCanvas::didSetMatrix(const SkMatrix& matrix) { - NOTIFY_SETUP(this); - if (this->needOpBytes(matrix.writeToMemory(nullptr))) { - this->writeOp(kSetMatrix_DrawOp); - fWriter.writeMatrix(matrix); - } - this->INHERITED::didSetMatrix(matrix); -} - -void SkGPipeCanvas::onClipRect(const SkRect& rect, SkRegion::Op rgnOp, - ClipEdgeStyle edgeStyle) { - NOTIFY_SETUP(this); - if (this->needOpBytes(sizeof(SkRect))) { - unsigned flags = 0; - if (kSoft_ClipEdgeStyle == edgeStyle) { - flags = kClip_HasAntiAlias_DrawOpFlag; - } - this->writeOp(kClipRect_DrawOp, flags, rgnOp); - fWriter.writeRect(rect); - } - this->INHERITED::onClipRect(rect, rgnOp, edgeStyle); -} - -void SkGPipeCanvas::onClipRRect(const SkRRect& rrect, SkRegion::Op rgnOp, - ClipEdgeStyle edgeStyle) { - NOTIFY_SETUP(this); - if (this->needOpBytes(kSizeOfFlatRRect)) { - unsigned flags = 0; - if (kSoft_ClipEdgeStyle == edgeStyle) { - flags = kClip_HasAntiAlias_DrawOpFlag; - } - this->writeOp(kClipRRect_DrawOp, flags, rgnOp); - fWriter.writeRRect(rrect); - } - this->INHERITED::onClipRRect(rrect, rgnOp, edgeStyle); -} - -void SkGPipeCanvas::onClipPath(const SkPath& path, SkRegion::Op rgnOp, - ClipEdgeStyle edgeStyle) { - NOTIFY_SETUP(this); - if (this->needOpBytes(path.writeToMemory(nullptr))) { - unsigned flags = 0; - if (kSoft_ClipEdgeStyle == edgeStyle) { - flags = kClip_HasAntiAlias_DrawOpFlag; - } - this->writeOp(kClipPath_DrawOp, flags, rgnOp); - fWriter.writePath(path); - } - // we just pass on the bounds of the path - this->INHERITED::onClipRect(path.getBounds(), rgnOp, edgeStyle); -} - -void SkGPipeCanvas::onClipRegion(const SkRegion& region, SkRegion::Op rgnOp) { - NOTIFY_SETUP(this); - if (this->needOpBytes(region.writeToMemory(nullptr))) { - this->writeOp(kClipRegion_DrawOp, 0, rgnOp); - fWriter.writeRegion(region); - } - this->INHERITED::onClipRegion(region, rgnOp); -} - -/////////////////////////////////////////////////////////////////////////////// - -void SkGPipeCanvas::onDrawPaint(const SkPaint& paint) { - NOTIFY_SETUP(this); - this->writePaint(paint); - if (this->needOpBytes()) { - this->writeOp(kDrawPaint_DrawOp); - } -} - -void SkGPipeCanvas::onDrawPoints(PointMode mode, size_t count, - const SkPoint pts[], const SkPaint& paint) { - if (count) { - NOTIFY_SETUP(this); - this->writePaint(paint); - if (this->needOpBytes(4 + count * sizeof(SkPoint))) { - this->writeOp(kDrawPoints_DrawOp, mode, 0); - fWriter.write32(SkToU32(count)); - fWriter.write(pts, count * sizeof(SkPoint)); - } - } -} - -void SkGPipeCanvas::onDrawOval(const SkRect& rect, const SkPaint& paint) { - NOTIFY_SETUP(this); - this->writePaint(paint); - if (this->needOpBytes(sizeof(SkRect))) { - this->writeOp(kDrawOval_DrawOp); - fWriter.writeRect(rect); - } -} - -void SkGPipeCanvas::onDrawRect(const SkRect& rect, const SkPaint& paint) { - NOTIFY_SETUP(this); - this->writePaint(paint); - if (this->needOpBytes(sizeof(SkRect))) { - this->writeOp(kDrawRect_DrawOp); - fWriter.writeRect(rect); - } -} - -void SkGPipeCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) { - NOTIFY_SETUP(this); - this->writePaint(paint); - if (this->needOpBytes(kSizeOfFlatRRect)) { - this->writeOp(kDrawRRect_DrawOp); - fWriter.writeRRect(rrect); - } -} - -void SkGPipeCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner, - const SkPaint& paint) { - NOTIFY_SETUP(this); - this->writePaint(paint); - if (this->needOpBytes(kSizeOfFlatRRect * 2)) { - this->writeOp(kDrawDRRect_DrawOp); - fWriter.writeRRect(outer); - fWriter.writeRRect(inner); - } -} - -void SkGPipeCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) { - NOTIFY_SETUP(this); - this->writePaint(paint); - if (this->needOpBytes(path.writeToMemory(nullptr))) { - this->writeOp(kDrawPath_DrawOp); - fWriter.writePath(path); - } -} - -bool SkGPipeCanvas::commonDrawBitmap(const SkBitmap& bm, DrawOps op, - unsigned flags, - size_t opBytesNeeded, - const SkPaint* paint) { - if (fDone) { - return false; - } - - if (paint != nullptr) { - flags |= kDrawBitmap_HasPaint_DrawOpFlag; - this->writePaint(*paint); - } - // This needs to run first so its calls to needOpBytes() and its writes - // don't interlace with the needOpBytes() and write below. - SkASSERT(fBitmapHeap != nullptr); - int32_t bitmapIndex = fBitmapHeap->insert(bm); - if (SkBitmapHeap::INVALID_SLOT == bitmapIndex) { - return false; - } - - if (this->needOpBytes(opBytesNeeded)) { - this->writeOp(op, flags, bitmapIndex); - return true; - } - return false; -} - -void SkGPipeCanvas::onDrawBitmap(const SkBitmap& bm, SkScalar left, SkScalar top, - const SkPaint* paint) { - NOTIFY_SETUP(this); - size_t opBytesNeeded = sizeof(SkScalar) * 2; - - if (this->commonDrawBitmap(bm, kDrawBitmap_DrawOp, 0, opBytesNeeded, paint)) { - fWriter.writeScalar(left); - fWriter.writeScalar(top); - } -} - -void SkGPipeCanvas::onDrawBitmapRect(const SkBitmap& bm, const SkRect* src, const SkRect& dst, - const SkPaint* paint, SrcRectConstraint constraint) { - NOTIFY_SETUP(this); - size_t opBytesNeeded = sizeof(SkRect); - bool hasSrc = src != nullptr; - unsigned flags; - if (hasSrc) { - flags = kDrawBitmap_HasSrcRect_DrawOpFlag; - opBytesNeeded += sizeof(int32_t) * 4; - } else { - flags = 0; - } - if (kFast_SrcRectConstraint == constraint) { - flags |= kDrawBitmap_Bleed_DrawOpFlag; - } - - if (this->commonDrawBitmap(bm, kDrawBitmapRect_DrawOp, flags, opBytesNeeded, paint)) { - if (hasSrc) { - fWriter.writeRect(*src); - } - fWriter.writeRect(dst); - } -} - -void SkGPipeCanvas::onDrawBitmapNine(const SkBitmap& bm, const SkIRect& center, - const SkRect& dst, const SkPaint* paint) { - NOTIFY_SETUP(this); - size_t opBytesNeeded = sizeof(int32_t) * 4 + sizeof(SkRect); - - if (this->commonDrawBitmap(bm, kDrawBitmapNine_DrawOp, 0, opBytesNeeded, paint)) { - fWriter.write32(center.fLeft); - fWriter.write32(center.fTop); - fWriter.write32(center.fRight); - fWriter.write32(center.fBottom); - fWriter.writeRect(dst); - } -} - -void SkGPipeCanvas::onDrawSprite(const SkBitmap& bm, int left, int top, const SkPaint* paint) { - NOTIFY_SETUP(this); - size_t opBytesNeeded = sizeof(int32_t) * 2; - - if (this->commonDrawBitmap(bm, kDrawSprite_DrawOp, 0, opBytesNeeded, paint)) { - fWriter.write32(left); - fWriter.write32(top); - } -} - -bool SkGPipeCanvas::commonDrawImage(const SkImage* image, DrawOps op, unsigned flags, - size_t opBytesNeeded, const SkPaint* paint) { - if (fDone) { - return false; - } - - if (paint != nullptr) { - flags |= kDrawBitmap_HasPaint_DrawOpFlag; - this->writePaint(*paint); - } - // This needs to run first so its calls to needOpBytes() and its writes - // don't interlace with the needOpBytes() and write below. - int32_t slot = fImageHeap->insert(image); - SkASSERT(slot != 0); - if (this->needOpBytes(opBytesNeeded)) { - this->writeOp(op, flags, slot); - return true; - } - return false; -} - -void SkGPipeCanvas::onDrawImage(const SkImage* image, SkScalar x, SkScalar y, - const SkPaint* paint) { - NOTIFY_SETUP(this); - if (this->commonDrawImage(image, kDrawImage_DrawOp, 0, sizeof(SkScalar) * 2, paint)) { - fWriter.writeScalar(x); - fWriter.writeScalar(y); - } -} - -void SkGPipeCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst, - const SkPaint* paint, SrcRectConstraint constraint) { - NOTIFY_SETUP(this); - unsigned flags = 0; - size_t opBytesNeeded = sizeof(SkRect); // dst - if (src) { - flags |= kDrawBitmap_HasSrcRect_DrawOpFlag; - opBytesNeeded += sizeof(SkRect); // src - } - if (this->commonDrawImage(image, kDrawImageRect_DrawOp, flags, opBytesNeeded, paint)) { - if (src) { - fWriter.writeRect(*src); - } - fWriter.writeRect(dst); - fWriter.writeInt(constraint); - } -} - -void SkGPipeCanvas::onDrawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst, - const SkPaint* paint) { - NOTIFY_SETUP(this); - size_t opBytesNeeded = sizeof(SkIRect) + sizeof(SkRect); // center + dst - if (this->commonDrawImage(image, kDrawImageNine_DrawOp, 0, opBytesNeeded, paint)) { - fWriter.writeIRect(center); - fWriter.writeRect(dst); - } -} - -void SkGPipeCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y, - const SkPaint& paint) { - if (byteLength) { - NOTIFY_SETUP(this); - this->writePaint(paint); - if (this->needOpBytes(4 + SkAlign4(byteLength) + 2 * sizeof(SkScalar))) { - this->writeOp(kDrawText_DrawOp); - fWriter.write32(SkToU32(byteLength)); - fWriter.writePad(text, byteLength); - fWriter.writeScalar(x); - fWriter.writeScalar(y); - } - } -} - -void SkGPipeCanvas::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[], - const SkPaint& paint) { - if (byteLength) { - NOTIFY_SETUP(this); - this->writePaint(paint); - int count = paint.textToGlyphs(text, byteLength, nullptr); - if (this->needOpBytes(4 + SkAlign4(byteLength) + 4 + count * sizeof(SkPoint))) { - this->writeOp(kDrawPosText_DrawOp); - fWriter.write32(SkToU32(byteLength)); - fWriter.writePad(text, byteLength); - fWriter.write32(count); - fWriter.write(pos, count * sizeof(SkPoint)); - } - } -} - -void SkGPipeCanvas::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[], - SkScalar constY, const SkPaint& paint) { - if (byteLength) { - NOTIFY_SETUP(this); - this->writePaint(paint); - int count = paint.textToGlyphs(text, byteLength, nullptr); - if (this->needOpBytes(4 + SkAlign4(byteLength) + 4 + count * sizeof(SkScalar) + 4)) { - this->writeOp(kDrawPosTextH_DrawOp); - fWriter.write32(SkToU32(byteLength)); - fWriter.writePad(text, byteLength); - fWriter.write32(count); - fWriter.write(xpos, count * sizeof(SkScalar)); - fWriter.writeScalar(constY); - } - } -} - -void SkGPipeCanvas::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path, - const SkMatrix* matrix, const SkPaint& paint) { - if (byteLength) { - NOTIFY_SETUP(this); - unsigned flags = 0; - size_t size = 4 + SkAlign4(byteLength) + path.writeToMemory(nullptr); - if (matrix) { - flags |= kDrawTextOnPath_HasMatrix_DrawOpFlag; - size += matrix->writeToMemory(nullptr); - } - this->writePaint(paint); - if (this->needOpBytes(size)) { - this->writeOp(kDrawTextOnPath_DrawOp, flags, 0); - - fWriter.write32(SkToU32(byteLength)); - fWriter.writePad(text, byteLength); - - fWriter.writePath(path); - if (matrix) { - fWriter.writeMatrix(*matrix); - } - } - } -} - -size_t SkGPipeCanvas::getInProcessTypefaces(const SkRefCntSet& typefaceSet, - TypefaceBuffer* buffer) { - // When in-process, we simply write out the typeface pointers. - size_t size = typefaceSet.count() * sizeof(SkTypeface*); - buffer->reset(size); - typefaceSet.copyToArray(reinterpret_cast(buffer->get())); - - return size; -} - -size_t SkGPipeCanvas::getCrossProcessTypefaces(const SkRefCntSet& typefaceSet, - TypefaceBuffer* buffer) { - // For cross-process we use typeface IDs. - size_t size = typefaceSet.count() * sizeof(uint32_t); - buffer->reset(size); - - uint32_t* idBuffer = reinterpret_cast(buffer->get()); - SkRefCntSet::Iter iter(typefaceSet); - int i = 0; - - for (void* setPtr = iter.next(); setPtr; setPtr = iter.next()) { - idBuffer[i++] = this->getTypefaceID(reinterpret_cast(setPtr)); - } - - SkASSERT(i == typefaceSet.count()); - - return size; -} - -void SkGPipeCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y, - const SkPaint& paint) { - NOTIFY_SETUP(this); - this->writePaint(paint); - - // FIXME: this is inefficient but avoids duplicating the blob serialization logic. - SkRefCntSet typefaceSet; - SkWriteBuffer blobBuffer; - blobBuffer.setTypefaceRecorder(&typefaceSet); - blob->flatten(blobBuffer); - - // Unlike most draw ops (which only use one paint/typeface), text blobs may reference - // an arbitrary number of typefaces. Since the one-paint-per-op model is not applicable, - // we need to serialize these explicitly. - TypefaceBuffer typefaceBuffer; - size_t typefaceSize = is_cross_process(fFlags) - ? this->getCrossProcessTypefaces(typefaceSet, &typefaceBuffer) - : this->getInProcessTypefaces(typefaceSet, &typefaceBuffer); - - // blob byte count + typeface count + x + y + blob data + an index (cross-process) - // or pointer (in-process) for each typeface - size_t size = 2 * sizeof(uint32_t) - + 2 * sizeof(SkScalar) - + blobBuffer.bytesWritten() - + typefaceSize; - - if (this->needOpBytes(size)) { - this->writeOp(kDrawTextBlob_DrawOp); - SkDEBUGCODE(size_t initialOffset = fWriter.bytesWritten();) - - fWriter.writeScalar(x); - fWriter.writeScalar(y); - - fWriter.write32(typefaceSet.count()); - fWriter.write(typefaceBuffer.get(), typefaceSize); - - fWriter.write32(SkToU32(blobBuffer.bytesWritten())); - uint32_t* pad = fWriter.reservePad(blobBuffer.bytesWritten()); - blobBuffer.writeToMemory(pad); - - SkASSERT(initialOffset + size == fWriter.bytesWritten()); - } -} - -void SkGPipeCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix, - const SkPaint* paint) { - // we want to playback the picture into individual draw calls - // - // todo: do we always have to unroll? If the pipe is not cross-process, seems like - // we could just ref the picture and move on...? - // - this->INHERITED::onDrawPicture(picture, matrix, paint); -} - -void SkGPipeCanvas::onDrawVertices(VertexMode vmode, int vertexCount, - const SkPoint vertices[], const SkPoint texs[], - const SkColor colors[], SkXfermode* xfer, - const uint16_t indices[], int indexCount, - const SkPaint& paint) { - if (0 == vertexCount) { - return; - } - - NOTIFY_SETUP(this); - this->writePaint(paint); - - unsigned flags = 0; // packs with the op, so needs no extra space - - size_t size = 0; - size += 4; // vmode - size += 4; // vertex count - size += vertexCount * sizeof(SkPoint); // vertices - - if (texs) { - flags |= kDrawVertices_HasTexs_DrawOpFlag; - size += vertexCount * sizeof(SkPoint); - } - if (colors) { - flags |= kDrawVertices_HasColors_DrawOpFlag; - size += vertexCount * sizeof(SkColor); - } - if (xfer && !SkXfermode::IsMode(xfer, SkXfermode::kModulate_Mode)) { - flags |= kDrawVertices_HasXfermode_DrawOpFlag; - size += sizeof(int32_t); // SkXfermode::Mode - } - if (indices && indexCount > 0) { - flags |= kDrawVertices_HasIndices_DrawOpFlag; - size += 4; // index count - size += SkAlign4(indexCount * sizeof(uint16_t)); // indices - } - - if (this->needOpBytes(size)) { - this->writeOp(kDrawVertices_DrawOp, flags, 0); - fWriter.write32(vmode); - fWriter.write32(vertexCount); - fWriter.write(vertices, vertexCount * sizeof(SkPoint)); - if (flags & kDrawVertices_HasTexs_DrawOpFlag) { - fWriter.write(texs, vertexCount * sizeof(SkPoint)); - } - if (flags & kDrawVertices_HasColors_DrawOpFlag) { - fWriter.write(colors, vertexCount * sizeof(SkColor)); - } - if (flags & kDrawVertices_HasXfermode_DrawOpFlag) { - SkXfermode::Mode mode = SkXfermode::kModulate_Mode; - SkAssertResult(xfer->asMode(&mode)); - fWriter.write32(mode); - } - if (flags & kDrawVertices_HasIndices_DrawOpFlag) { - fWriter.write32(indexCount); - fWriter.writePad(indices, indexCount * sizeof(uint16_t)); - } - } -} - -void SkGPipeCanvas::onDrawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[], - const SkColor colors[], int count, SkXfermode::Mode mode, - const SkRect* cull, const SkPaint* paint) { - NOTIFY_SETUP(this); - unsigned flags = 0; // packs with the op, so needs no extra space - - if (paint) { - flags |= kDrawAtlas_HasPaint_DrawOpFlag; - this->writePaint(*paint); - } - - size_t size = 4; // image-slot - size += 4; // count - size += 4; // mode - size += count * sizeof(SkRSXform); // xform - size += count * sizeof(SkRect); // tex - if (colors) { - flags |= kDrawAtlas_HasColors_DrawOpFlag; - size += count * sizeof(SkColor); // colors - } - if (cull) { - flags |= kDrawAtlas_HasCull_DrawOpFlag; - size += sizeof(SkRect); // cull - } - - if (this->needOpBytes(size)) { - this->writeOp(kDrawAtlas_DrawOp, flags, 0); - int32_t slot = fImageHeap->insert(atlas); - fWriter.write32(slot); - fWriter.write32(count); - fWriter.write32(mode); - fWriter.write(xform, count * sizeof(SkRSXform)); - fWriter.write(tex, count * sizeof(SkRect)); - if (colors) { - fWriter.write(colors, count * sizeof(SkColor)); - } - if (cull) { - fWriter.writeRect(*cull); - } - } -} - -void SkGPipeCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4], - const SkPoint texCoords[4], SkXfermode* xmode, - const SkPaint& paint) { - NOTIFY_SETUP(this); - - size_t size = SkPatchUtils::kNumCtrlPts * sizeof(SkPoint); - unsigned flags = 0; - if (colors) { - flags |= kDrawVertices_HasColors_DrawOpFlag; - size += SkPatchUtils::kNumCorners * sizeof(SkColor); - } - if (texCoords) { - flags |= kDrawVertices_HasTexs_DrawOpFlag; - size += SkPatchUtils::kNumCorners * sizeof(SkPoint); - } - if (xmode) { - SkXfermode::Mode mode; - if (xmode->asMode(&mode) && SkXfermode::kModulate_Mode != mode) { - flags |= kDrawVertices_HasXfermode_DrawOpFlag; - size += sizeof(int32_t); - } - } - - this->writePaint(paint); - if (this->needOpBytes(size)) { - this->writeOp(kDrawPatch_DrawOp, flags, 0); - - fWriter.write(cubics, SkPatchUtils::kNumCtrlPts * sizeof(SkPoint)); - - if (colors) { - fWriter.write(colors, SkPatchUtils::kNumCorners * sizeof(SkColor)); - } - - if (texCoords) { - fWriter.write(texCoords, SkPatchUtils::kNumCorners * sizeof(SkPoint)); - } - - if (flags & kDrawVertices_HasXfermode_DrawOpFlag) { - SkXfermode::Mode mode = SkXfermode::kModulate_Mode; - SkAssertResult(xmode->asMode(&mode)); - fWriter.write32(mode); - } - } -} - -void SkGPipeCanvas::flushRecording(bool detachCurrentBlock) { - this->doNotify(); - if (detachCurrentBlock) { - // force a new block to be requested for the next recorded command - fBlockSize = 0; - } -} - -void SkGPipeCanvas::resetImageHeap() { - if (fImageHeap) { - fImageHeap->reset(); - } -} - -size_t SkGPipeCanvas::freeMemoryIfPossible(size_t bytesToFree) { - return (nullptr == fBitmapHeap) ? 0 : fBitmapHeap->freeMemoryIfPossible(bytesToFree); -} - -/////////////////////////////////////////////////////////////////////////////// - -template uint32_t castToU32(T value) { - union { - T fSrc; - uint32_t fDst; - } data; - data.fSrc = value; - return data.fDst; -} - -void SkGPipeCanvas::writePaint(const SkPaint& paint) { - if (fDone) { - return; - } - SkPaint& base = fPaint; - uint32_t storage[32]; - uint32_t* ptr = storage; - - if (base.getFlags() != paint.getFlags()) { - *ptr++ = PaintOp_packOpData(kFlags_PaintOp, paint.getFlags()); - base.setFlags(paint.getFlags()); - } - if (base.getColor() != paint.getColor()) { - *ptr++ = PaintOp_packOp(kColor_PaintOp); - *ptr++ = paint.getColor(); - base.setColor(paint.getColor()); - } - if (base.getFilterQuality() != paint.getFilterQuality()) { - *ptr++ = PaintOp_packOpData(kFilterLevel_PaintOp, paint.getFilterQuality()); - base.setFilterQuality(paint.getFilterQuality()); - } - if (base.getStyle() != paint.getStyle()) { - *ptr++ = PaintOp_packOpData(kStyle_PaintOp, paint.getStyle()); - base.setStyle(paint.getStyle()); - } - if (base.getStrokeJoin() != paint.getStrokeJoin()) { - *ptr++ = PaintOp_packOpData(kJoin_PaintOp, paint.getStrokeJoin()); - base.setStrokeJoin(paint.getStrokeJoin()); - } - if (base.getStrokeCap() != paint.getStrokeCap()) { - *ptr++ = PaintOp_packOpData(kCap_PaintOp, paint.getStrokeCap()); - base.setStrokeCap(paint.getStrokeCap()); - } - if (base.getStrokeWidth() != paint.getStrokeWidth()) { - *ptr++ = PaintOp_packOp(kWidth_PaintOp); - *ptr++ = castToU32(paint.getStrokeWidth()); - base.setStrokeWidth(paint.getStrokeWidth()); - } - if (base.getStrokeMiter() != paint.getStrokeMiter()) { - *ptr++ = PaintOp_packOp(kMiter_PaintOp); - *ptr++ = castToU32(paint.getStrokeMiter()); - base.setStrokeMiter(paint.getStrokeMiter()); - } - if (base.getTextEncoding() != paint.getTextEncoding()) { - *ptr++ = PaintOp_packOpData(kEncoding_PaintOp, paint.getTextEncoding()); - base.setTextEncoding(paint.getTextEncoding()); - } - if (base.getHinting() != paint.getHinting()) { - *ptr++ = PaintOp_packOpData(kHinting_PaintOp, paint.getHinting()); - base.setHinting(paint.getHinting()); - } - if (base.getTextAlign() != paint.getTextAlign()) { - *ptr++ = PaintOp_packOpData(kAlign_PaintOp, paint.getTextAlign()); - base.setTextAlign(paint.getTextAlign()); - } - if (base.getTextSize() != paint.getTextSize()) { - *ptr++ = PaintOp_packOp(kTextSize_PaintOp); - *ptr++ = castToU32(paint.getTextSize()); - base.setTextSize(paint.getTextSize()); - } - if (base.getTextScaleX() != paint.getTextScaleX()) { - *ptr++ = PaintOp_packOp(kTextScaleX_PaintOp); - *ptr++ = castToU32(paint.getTextScaleX()); - base.setTextScaleX(paint.getTextScaleX()); - } - if (base.getTextSkewX() != paint.getTextSkewX()) { - *ptr++ = PaintOp_packOp(kTextSkewX_PaintOp); - *ptr++ = castToU32(paint.getTextSkewX()); - base.setTextSkewX(paint.getTextSkewX()); - } - - if (!SkTypeface::Equal(base.getTypeface(), paint.getTypeface())) { - if (is_cross_process(fFlags)) { - uint32_t id = this->getTypefaceID(paint.getTypeface()); - *ptr++ = PaintOp_packOpData(kTypeface_PaintOp, id); - } else if (this->needOpBytes(sizeof(void*))) { - // Add to the set for ref counting. - fTypefaceSet.add(paint.getTypeface()); - // It is safe to write the typeface to the stream before the rest - // of the paint unless we ever send a kReset_PaintOp, which we - // currently never do. - this->writeOp(kSetTypeface_DrawOp); - fWriter.writePtr(paint.getTypeface()); - } - base.setTypeface(paint.getTypeface()); - } - - // This is a new paint, so all old flats can be safely purged, if necessary. - fFlattenableHeap.markAllFlatsSafeToDelete(); - for (int i = 0; i < kCount_PaintFlats; i++) { - int index = this->flattenToIndex(get_paintflat(paint, i), (PaintFlats)i); - bool replaced = index < 0; - if (replaced) { - index = ~index; - } - // Store the index of any flat that needs to be kept. 0 means no flat. - if (index > 0) { - fFlattenableHeap.markFlatForKeeping(index); - } - SkASSERT(index >= 0 && index <= fFlatDictionary.count()); - if (index != fCurrFlatIndex[i] || replaced) { - *ptr++ = PaintOp_packOpFlagData(kFlatIndex_PaintOp, i, index); - fCurrFlatIndex[i] = index; - } - } - - size_t size = (char*)ptr - (char*)storage; - if (size && this->needOpBytes(size)) { - this->writeOp(kPaintOp_DrawOp, 0, SkToU32(size)); - fWriter.write(storage, size); - for (size_t i = 0; i < size/4; i++) { -// SkDebugf("[%d] %08X\n", i, storage[i]); - } - } - - // - // Do these after we've written kPaintOp_DrawOp - - if (base.getAnnotation() != paint.getAnnotation()) { - if (nullptr == paint.getAnnotation()) { - if (this->needOpBytes()) { - this->writeOp(kSetAnnotation_DrawOp, 0, 0); - } - } else { - SkWriteBuffer buffer; - paint.getAnnotation()->writeToBuffer(buffer); - const size_t size = buffer.bytesWritten(); - if (this->needOpBytes(size)) { - this->writeOp(kSetAnnotation_DrawOp, 0, SkToU32(size)); - buffer.writeToMemory(fWriter.reserve(size)); - } - } - base.setAnnotation(paint.getAnnotation()); - } -} - -/////////////////////////////////////////////////////////////////////////////// - -#include "SkGPipe.h" - -SkGPipeController::~SkGPipeController() { - SkSafeUnref(fCanvas); -} - -void SkGPipeController::setCanvas(SkGPipeCanvas* canvas) { - SkRefCnt_SafeAssign(fCanvas, canvas); -} - -void SkGPipeController::purgeCaches() -{ - fCanvas->resetImageHeap(); - // Other caches are self-purging with a small MRU pool - // We could purge them as well, but it is not clear whether - // that would be a win. -} - -/////////////////////////////////////////////////////////////////////////////// - -SkGPipeWriter::SkGPipeWriter() -: fWriter(0) { - fCanvas = nullptr; -} - -SkGPipeWriter::~SkGPipeWriter() { - this->endRecording(); -} - -SkCanvas* SkGPipeWriter::startRecording(SkGPipeController* controller, uint32_t flags, - uint32_t width, uint32_t height) { - if (nullptr == fCanvas) { - fWriter.reset(nullptr, 0); - fCanvas = new SkGPipeCanvas(controller, &fWriter, flags, width, height); - } - controller->setCanvas(fCanvas); - return fCanvas; -} - -void SkGPipeWriter::endRecording() { - if (fCanvas) { - fCanvas->finish(true); - fCanvas->unref(); - fCanvas = nullptr; - } -} - -void SkGPipeWriter::flushRecording(bool detachCurrentBlock) { - if (fCanvas) { - fCanvas->flushRecording(detachCurrentBlock); - } -} - -size_t SkGPipeWriter::freeMemoryIfPossible(size_t bytesToFree) { - if (fCanvas) { - return fCanvas->freeMemoryIfPossible(bytesToFree); - } - return 0; -} - -size_t SkGPipeWriter::storageAllocatedForRecording() const { - return nullptr == fCanvas ? 0 : fCanvas->storageAllocatedForRecording(); -} - -/////////////////////////////////////////////////////////////////////////////// - -BitmapShuttle::BitmapShuttle(SkGPipeCanvas* canvas) { - SkASSERT(canvas != nullptr); - fCanvas = canvas; - fCanvas->ref(); -} - -BitmapShuttle::~BitmapShuttle() { - this->removeCanvas(); -} - -bool BitmapShuttle::insert(const SkBitmap& bitmap, int32_t slot) { - SkASSERT(fCanvas != nullptr); - return fCanvas->shuttleBitmap(bitmap, slot); -} - -void BitmapShuttle::removeCanvas() { - if (nullptr == fCanvas) { - return; - } - fCanvas->unref(); - fCanvas = nullptr; -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// - -SkImageHeap::SkImageHeap() : fBytesInCache (0) {} - -SkImageHeap::~SkImageHeap() { - fArray.unrefAll(); -} - -void SkImageHeap::reset() { - fArray.unrefAll(); - fArray.rewind(); - fBytesInCache = 0; -} - -const SkImage* SkImageHeap::get(int32_t slot) const { - SkASSERT(slot > 0); - return fArray[slot - 1]; -} - -int32_t SkImageHeap::find(const SkImage* img) const { - int index = fArray.find(img); - if (index >= 0) { - return index + 1; // found - } - return 0; // not found -} - -int32_t SkImageHeap::insert(const SkImage* img) { - int32_t slot = this->find(img); - if (slot) { - return slot; - } - // TODO: SkImage does not expose bytes per pixel, 4 is just a best guess. - fBytesInCache += img->width() * img->height() * 4; - *fArray.append() = SkRef(img); - return fArray.count(); // slot is always index+1 -} - diff --git a/gfx/skia/skia/src/pipe/utils/SamplePipeControllers.cpp b/gfx/skia/skia/src/pipe/utils/SamplePipeControllers.cpp deleted file mode 100644 index fea9d6842b..0000000000 --- a/gfx/skia/skia/src/pipe/utils/SamplePipeControllers.cpp +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright 2012 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "SamplePipeControllers.h" - -#include "SkCanvas.h" -#include "SkGPipe.h" -#include "SkMatrix.h" - -PipeController::PipeController(SkCanvas* target, SkPicture::InstallPixelRefProc proc) -:fReader(target) { - fBlock = nullptr; - fBlockSize = fBytesWritten = 0; - fReader.setBitmapDecoder(proc); -} - -PipeController::~PipeController() { - sk_free(fBlock); -} - -void* PipeController::requestBlock(size_t minRequest, size_t *actual) { - sk_free(fBlock); - fBlockSize = minRequest; - fBlock = sk_malloc_throw(fBlockSize); - fBytesWritten = 0; - *actual = fBlockSize; - return fBlock; -} - -void PipeController::notifyWritten(size_t bytes) { - fStatus = fReader.playback(this->getData(), bytes); - SkASSERT(SkGPipeReader::kError_Status != fStatus); - fBytesWritten += bytes; -} - -//////////////////////////////////////////////////////////////////////////////// - -TiledPipeController::TiledPipeController(const SkBitmap& bitmap, - SkPicture::InstallPixelRefProc proc, - const SkMatrix* initial) -: INHERITED(nullptr, proc) { - int32_t top = 0; - int32_t bottom; - int32_t height = bitmap.height() / NumberOfTiles; - SkIRect rect; - for (int i = 0; i < NumberOfTiles; i++) { - bottom = i + 1 == NumberOfTiles ? bitmap.height() : top + height; - rect.setLTRB(0, top, bitmap.width(), bottom); - top = bottom; - - SkDEBUGCODE(bool extracted = )bitmap.extractSubset(&fBitmaps[i], rect); - SkASSERT(extracted); - SkCanvas* canvas = new SkCanvas(fBitmaps[i]); - if (initial != nullptr) { - canvas->setMatrix(*initial); - } - canvas->translate(SkIntToScalar(-rect.left()), - SkIntToScalar(-rect.top())); - if (0 == i) { - fReader.setCanvas(canvas); - } else { - fReaders[i - 1].setCanvas(canvas); - fReaders[i - 1].setBitmapDecoder(proc); - } - canvas->unref(); - } -} - -void TiledPipeController::notifyWritten(size_t bytes) { - for (int i = 0; i < NumberOfTiles - 1; i++) { - fReaders[i].playback(this->getData(), bytes); - } - this->INHERITED::notifyWritten(bytes); -} - -//////////////////////////////////////////////////////////////////////////////// - -ThreadSafePipeController::ThreadSafePipeController(int numberOfReaders) -: fAllocator(kMinBlockSize) -, fNumberOfReaders(numberOfReaders) { - fBlock = nullptr; - fBytesWritten = 0; -} - -void* ThreadSafePipeController::requestBlock(size_t minRequest, size_t *actual) { - if (fBlock) { - // Save the previous block for later - PipeBlock previousBloc(fBlock, fBytesWritten); - fBlockList.push(previousBloc); - } - int32_t blockSize = SkMax32(SkToS32(minRequest), kMinBlockSize); - fBlock = fAllocator.allocThrow(blockSize); - fBytesWritten = 0; - *actual = blockSize; - return fBlock; -} - -void ThreadSafePipeController::notifyWritten(size_t bytes) { - fBytesWritten += bytes; -} - -void ThreadSafePipeController::draw(SkCanvas* target) { - SkGPipeReader reader(target); - for (int currentBlock = 0; currentBlock < fBlockList.count(); currentBlock++ ) { - reader.playback(fBlockList[currentBlock].fBlock, fBlockList[currentBlock].fBytes); - } - - if (fBlock) { - reader.playback(fBlock, fBytesWritten); - } -} diff --git a/gfx/skia/skia/src/pipe/utils/SamplePipeControllers.h b/gfx/skia/skia/src/pipe/utils/SamplePipeControllers.h deleted file mode 100644 index b3f057fa8c..0000000000 --- a/gfx/skia/skia/src/pipe/utils/SamplePipeControllers.h +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright 2012 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "SkBitmap.h" -#include "SkChunkAlloc.h" -#include "SkGPipe.h" -#include "SkPicture.h" -#include "SkTDArray.h" - -class SkCanvas; -class SkMatrix; - -class PipeController : public SkGPipeController { -public: - PipeController(SkCanvas* target, SkPicture::InstallPixelRefProc proc = nullptr); - virtual ~PipeController(); - void* requestBlock(size_t minRequest, size_t* actual) override; - void notifyWritten(size_t bytes) override; -protected: - const void* getData() { return (const char*) fBlock + fBytesWritten; } - SkGPipeReader fReader; -private: - void* fBlock; - size_t fBlockSize; - size_t fBytesWritten; - SkGPipeReader::Status fStatus; -}; - -//////////////////////////////////////////////////////////////////////////////// - -class TiledPipeController : public PipeController { -public: - TiledPipeController(const SkBitmap&, SkPicture::InstallPixelRefProc proc = nullptr, - const SkMatrix* initialMatrix = nullptr); - virtual ~TiledPipeController() {}; - void notifyWritten(size_t bytes) override; - int numberOfReaders() const override { return NumberOfTiles; } -private: - enum { - NumberOfTiles = 10 - }; - SkGPipeReader fReaders[NumberOfTiles - 1]; - SkBitmap fBitmaps[NumberOfTiles]; - typedef PipeController INHERITED; -}; - -//////////////////////////////////////////////////////////////////////////////// - -/** - * Borrowed (and modified) from SkDeferredCanvas.cpp::DeferredPipeController. - * Allows playing back from multiple threads, but does not do the threading itself. - */ -class ThreadSafePipeController : public SkGPipeController { -public: - ThreadSafePipeController(int numberOfReaders); - void* requestBlock(size_t minRequest, size_t* actual) override; - void notifyWritten(size_t bytes) override; - int numberOfReaders() const override { return fNumberOfReaders; } - - /** - * Play the stored drawing commands to the specified canvas. If SkGPipeWriter::startRecording - * used the flag SkGPipeWriter::kSimultaneousReaders_Flag, this can be called from different - * threads simultaneously. - */ - void draw(SkCanvas*); -private: - enum { - kMinBlockSize = 4096 - }; - struct PipeBlock { - PipeBlock(void* block, size_t bytes) { fBlock = block, fBytes = bytes; } - // Stream of draw commands written by the SkGPipeWriter. Allocated by fAllocator, which will - // handle freeing it. - void* fBlock; - // Number of bytes that were written to fBlock. - size_t fBytes; - }; - void* fBlock; - size_t fBytesWritten; - SkChunkAlloc fAllocator; - SkTDArray fBlockList; - int fNumberOfReaders; -}; diff --git a/gfx/skia/skia/src/ports/SkFontMgr_android_parser.cpp b/gfx/skia/skia/src/ports/SkFontMgr_android_parser.cpp index ba2296bffa..1d1a3b7ddb 100644 --- a/gfx/skia/skia/src/ports/SkFontMgr_android_parser.cpp +++ b/gfx/skia/skia/src/ports/SkFontMgr_android_parser.cpp @@ -12,6 +12,7 @@ #include "SkTDArray.h" #include "SkTSearch.h" #include "SkTemplates.h" +#include "SkTLogic.h" #include #include @@ -579,9 +580,6 @@ static const XML_Memory_Handling_Suite sk_XML_alloc = { sk_free }; -template struct remove_ptr {typedef T type;}; -template struct remove_ptr {typedef T type;}; - /** * This function parses the given filename and stores the results in the given * families array. Returns the version of the file, negative if the file does not exist. @@ -598,7 +596,7 @@ static int parse_config_file(const char* filename, SkTDArray& famil return -1; } - SkAutoTCallVProc::type, XML_ParserFree> parser( + SkAutoTCallVProc, XML_ParserFree> parser( XML_ParserCreate_MM(nullptr, &sk_XML_alloc, nullptr)); if (!parser) { SkDebugf(SK_FONTMGR_ANDROID_PARSER_PREFIX "could not create XML parser\n"); @@ -665,11 +663,6 @@ static void append_fallback_font_families_for_locale(SkTDArray& fal const char* dir, const SkString& basePath) { -#if defined(SK_BUILD_FOR_ANDROID_FRAMEWORK) - // The framework is beyond Android 4.2 and can therefore skip this function - return; -#endif - SkAutoTCallIProc fontDirectory(opendir(dir)); if (nullptr == fontDirectory) { return; diff --git a/gfx/skia/skia/src/ports/SkFontMgr_android_parser.h b/gfx/skia/skia/src/ports/SkFontMgr_android_parser.h index aca1573952..b841bc6d18 100644 --- a/gfx/skia/skia/src/ports/SkFontMgr_android_parser.h +++ b/gfx/skia/skia/src/ports/SkFontMgr_android_parser.h @@ -211,7 +211,7 @@ template static bool parse_fixed(const char* s, T* value) { n = -n; frac = -frac; } - *value = (n << N) + frac; + *value = SkLeftShift(n, N) + frac; return true; } diff --git a/gfx/skia/skia/src/ports/SkGlobalInitialization_chromium.cpp b/gfx/skia/skia/src/ports/SkGlobalInitialization_chromium.cpp deleted file mode 100644 index 8ff43f78e8..0000000000 --- a/gfx/skia/skia/src/ports/SkGlobalInitialization_chromium.cpp +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright 2011 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "SkTypes.h" - -#include "SkBitmapProcShader.h" -#include "SkMallocPixelRef.h" -#include "SkPathEffect.h" -#include "SkPixelRef.h" -#include "SkXfermode.h" - -#include "Sk1DPathEffect.h" -#include "Sk2DPathEffect.h" -#include "SkArithmeticMode.h" -#include "SkArcToPathEffect.h" -#include "SkBitmapSourceDeserializer.h" -#include "SkBlurDrawLooper.h" -#include "SkBlurImageFilter.h" -#include "SkBlurMaskFilter.h" -#include "SkColorCubeFilter.h" -#include "SkColorFilter.h" -#include "SkColorFilterImageFilter.h" -#include "SkColorMatrixFilter.h" -#include "SkColorShader.h" -#include "SkComposeImageFilter.h" -#include "SkComposeShader.h" -#include "SkCornerPathEffect.h" -#include "SkDashPathEffect.h" -#include "SkDiscretePathEffect.h" -#include "SkDisplacementMapEffect.h" -#include "SkDropShadowImageFilter.h" -#include "SkEmptyShader.h" -#include "SkEmbossMaskFilter.h" -#include "SkFlattenable.h" -#include "SkGradientShader.h" -#include "SkImageSource.h" -#include "SkLayerDrawLooper.h" -#include "SkLayerRasterizer.h" -#include "SkLerpXfermode.h" -#include "SkLightingImageFilter.h" -#include "SkLightingShader.h" -#include "SkLocalMatrixShader.h" -#include "SkLumaColorFilter.h" -#include "SkMagnifierImageFilter.h" -#include "SkMatrixConvolutionImageFilter.h" -#include "SkMergeImageFilter.h" -#include "SkModeColorFilter.h" -#include "SkMorphologyImageFilter.h" -#include "SkOffsetImageFilter.h" -#include "SkOnce.h" -#include "SkPerlinNoiseShader.h" -#include "SkPictureImageFilter.h" -#include "SkPictureShader.h" -#include "SkPixelXorXfermode.h" -#include "SkRectShaderImageFilter.h" -#include "SkTableColorFilter.h" -#include "SkTestImageFilters.h" -#include "SkTileImageFilter.h" -#include "SkMatrixImageFilter.h" -#include "SkXfermodeImageFilter.h" - -// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -// -// Adding new classes to Init() below has security consequences in Chrome. -// -// In particular, it is important that we don't create code paths that -// deserialize untrusted data as SkImageFilters; SkImageFilters are sent from -// Chrome renderers (untrusted) to the main (trusted) process. -// -// If you add a new SkImageFilter here _or_ other effect that can be part of -// an SkImageFilter, it's a good idea to have chrome-security@google.com sign -// off on the CL, and at minimum extend SampleFilterFuzz.cpp to fuzz it. -// -// SkPictures are untrusted data. Please be extremely careful not to allow -// SkPictures created in a Chrome renderer to be deserialized in the main process. -// -// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - -class SkPrivateEffectInitializer { -public: - static void Init() { - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkArcToPathEffect) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkBitmapProcShader) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkBitmapSourceDeserializer) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkBlurDrawLooper) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkBlurImageFilter) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkColorCubeFilter) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkColorMatrixFilter) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkColorShader) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkComposePathEffect) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkComposeShader) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkCornerPathEffect) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkDashPathEffect) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkDilateImageFilter) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkDiscretePathEffect) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkDisplacementMapEffect) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkDropShadowImageFilter) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkEmbossMaskFilter) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkEmptyShader) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkErodeImageFilter) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkImageSource) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkLayerDrawLooper) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkLayerRasterizer) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkLerpXfermode) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkLocalMatrixShader) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkLumaColorFilter) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkPath1DPathEffect) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkLine2DPathEffect) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkModeColorFilter) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkPath2DPathEffect) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkPerlinNoiseShader) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkPictureImageFilter) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkPictureShader) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkPixelXorXfermode) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkRectShaderImageFilter) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkSumPathEffect) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkTileImageFilter) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkMatrixImageFilter) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkXfermodeImageFilter) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkMagnifierImageFilter) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkMatrixConvolutionImageFilter) - - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkOffsetImageFilter) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkComposeImageFilter) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkMergeImageFilter) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkColorFilterImageFilter) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkDownSampleImageFilter) - - SkArithmeticMode::InitializeFlattenables(); - SkBlurMaskFilter::InitializeFlattenables(); - SkColorFilter::InitializeFlattenables(); - SkGradientShader::InitializeFlattenables(); - SkLightingImageFilter::InitializeFlattenables(); - SkLightingShader::InitializeFlattenables(); - SkTableColorFilter::InitializeFlattenables(); - SkXfermode::InitializeFlattenables(); - } -}; - -SK_DECLARE_STATIC_ONCE(once); -void SkFlattenable::InitializeFlattenablesIfNeeded() { - SkOnce(&once, SkPrivateEffectInitializer::Init); -} diff --git a/gfx/skia/skia/src/ports/SkGlobalInitialization_default.cpp b/gfx/skia/skia/src/ports/SkGlobalInitialization_default.cpp index 3038e25c5d..9471f01c88 100644 --- a/gfx/skia/skia/src/ports/SkGlobalInitialization_default.cpp +++ b/gfx/skia/skia/src/ports/SkGlobalInitialization_default.cpp @@ -5,12 +5,9 @@ * found in the LICENSE file. */ -#include "SkBitmapProcShader.h" -#include "SkPathEffect.h" -#include "SkXfermode.h" - #include "Sk1DPathEffect.h" #include "Sk2DPathEffect.h" +#include "SkAlphaThresholdFilter.h" #include "SkArithmeticMode.h" #include "SkArcToPathEffect.h" #include "SkBitmapSourceDeserializer.h" @@ -18,20 +15,15 @@ #include "SkBlurImageFilter.h" #include "SkBlurMaskFilter.h" #include "SkColorCubeFilter.h" -#include "SkColorFilter.h" #include "SkColorFilterImageFilter.h" #include "SkColorMatrixFilter.h" -#include "SkColorShader.h" #include "SkComposeImageFilter.h" -#include "SkComposeShader.h" #include "SkCornerPathEffect.h" #include "SkDashPathEffect.h" #include "SkDiscretePathEffect.h" #include "SkDisplacementMapEffect.h" #include "SkDropShadowImageFilter.h" -#include "SkEmptyShader.h" #include "SkEmbossMaskFilter.h" -#include "SkFlattenable.h" #include "SkGradientShader.h" #include "SkImageSource.h" #include "SkLayerDrawLooper.h" @@ -39,88 +31,87 @@ #include "SkLerpXfermode.h" #include "SkLightingImageFilter.h" #include "SkLightingShader.h" -#include "SkLocalMatrixShader.h" #include "SkLumaColorFilter.h" #include "SkMagnifierImageFilter.h" #include "SkMatrixConvolutionImageFilter.h" #include "SkMergeImageFilter.h" -#include "SkModeColorFilter.h" #include "SkMorphologyImageFilter.h" #include "SkOffsetImageFilter.h" -#include "SkOnce.h" +#include "SkPaintImageFilter.h" #include "SkPerlinNoiseShader.h" #include "SkPictureImageFilter.h" -#include "SkPictureShader.h" #include "SkPixelXorXfermode.h" -#include "SkRectShaderImageFilter.h" #include "SkTableColorFilter.h" #include "SkTestImageFilters.h" #include "SkTileImageFilter.h" -#include "SkMatrixImageFilter.h" #include "SkXfermodeImageFilter.h" -class SkPrivateEffectInitializer { -public: - static void Init() { - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkArcToPathEffect) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkBitmapProcShader) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkBitmapSourceDeserializer) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkBlurDrawLooper) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkBlurImageFilter) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkColorCubeFilter) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkColorMatrixFilter) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkColorShader) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkComposePathEffect) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkComposeShader) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkCornerPathEffect) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkDashPathEffect) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkDilateImageFilter) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkDiscretePathEffect) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkDisplacementMapEffect) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkDropShadowImageFilter) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkEmbossMaskFilter) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkEmptyShader) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkErodeImageFilter) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkImageSource) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkLayerDrawLooper) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkLayerRasterizer) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkLerpXfermode) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkLocalMatrixShader) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkLumaColorFilter) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkPath1DPathEffect) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkLine2DPathEffect) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkModeColorFilter) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkPath2DPathEffect) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkPerlinNoiseShader) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkPictureImageFilter) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkPictureShader) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkPixelXorXfermode) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkRectShaderImageFilter) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkSumPathEffect) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkTileImageFilter) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkMatrixImageFilter) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkXfermodeImageFilter) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkMagnifierImageFilter) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkMatrixConvolutionImageFilter) +/* + * None of these are strictly "required" for Skia to operate. + * + * These are the bulk of our "effects" -- subclasses of various effects on SkPaint. + * + * Clients should feel free to dup this file and modify it as needed. This function "InitEffects" + * will automatically be called before any of skia's effects are asked to be deserialized. + */ +void SkFlattenable::PrivateInitializer::InitEffects() { + SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkBitmapSourceDeserializer) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkOffsetImageFilter) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkComposeImageFilter) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkMergeImageFilter) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkColorFilterImageFilter) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkDownSampleImageFilter) + // MaskFilter + SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkEmbossMaskFilter) + SkBlurMaskFilter::InitializeFlattenables(); - SkArithmeticMode::InitializeFlattenables(); - SkBlurMaskFilter::InitializeFlattenables(); - SkColorFilter::InitializeFlattenables(); - SkGradientShader::InitializeFlattenables(); - SkLightingImageFilter::InitializeFlattenables(); - SkLightingShader::InitializeFlattenables(); - SkTableColorFilter::InitializeFlattenables(); - SkXfermode::InitializeFlattenables(); - } -}; + // DrawLooper + SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkBlurDrawLooper) + SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkLayerDrawLooper) -SK_DECLARE_STATIC_ONCE(once); -void SkFlattenable::InitializeFlattenablesIfNeeded() { - SkOnce(&once, SkPrivateEffectInitializer::Init); + // Rasterizer + SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkLayerRasterizer) + + // ColorFilter + SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkColorCubeFilter) + SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkColorMatrixFilter) + SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkLumaColorFilter) + SkAlphaThresholdFilter::InitializeFlattenables(); + SkArithmeticMode::InitializeFlattenables(); + SkTableColorFilter::InitializeFlattenables(); + + // Shader + SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkPerlinNoiseShader) + SkGradientShader::InitializeFlattenables(); + SkLightingShader::InitializeFlattenables(); + + // Xfermode + SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkLerpXfermode) + SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkPixelXorXfermode) + + // PathEffect + SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkArcToPathEffect) + SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkCornerPathEffect) + SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkDashPathEffect) + SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkDiscretePathEffect) + SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkPath1DPathEffect) + SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkLine2DPathEffect) + SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkPath2DPathEffect) + SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkSumPathEffect) + + // ImageFilter + SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkBlurImageFilter) + SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkDilateImageFilter) + SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkDisplacementMapEffect) + SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkDropShadowImageFilter) + SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkErodeImageFilter) + SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkImageSource) + SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkPaintImageFilter) + SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkPictureImageFilter) + SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkTileImageFilter) + SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkXfermodeImageFilter) + SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkMagnifierImageFilter) + SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkMatrixConvolutionImageFilter) + SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkOffsetImageFilter) + SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkComposeImageFilter) + SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkMergeImageFilter) + SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkColorFilterImageFilter) + SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkDownSampleImageFilter) + SkLightingImageFilter::InitializeFlattenables(); } diff --git a/gfx/skia/skia/src/ports/SkImageDecoder_empty.cpp b/gfx/skia/skia/src/ports/SkImageDecoder_empty.cpp index 11c3db1afc..f52dada73b 100644 --- a/gfx/skia/skia/src/ports/SkImageDecoder_empty.cpp +++ b/gfx/skia/skia/src/ports/SkImageDecoder_empty.cpp @@ -11,6 +11,7 @@ #include "SkImageDecoder.h" #include "SkImageEncoder.h" #include "SkMovie.h" +#include "SkPixelSerializer.h" #include "SkStream.h" class SkColorTable; @@ -108,6 +109,10 @@ SkData* SkImageEncoder::EncodeData(const SkImageInfo&, const void* pixels, size_ return nullptr; } +SkData* SkImageEncoder::EncodeData(const SkPixmap&, Type, int) { + return nullptr; +} + bool SkImageEncoder::encodeStream(SkWStream*, const SkBitmap&, int) { return false; } @@ -119,4 +124,17 @@ SkData* SkImageEncoder::encodeData(const SkBitmap&, int) { bool SkImageEncoder::encodeFile(const char file[], const SkBitmap& bm, int quality) { return false; } + +namespace { +class ImageEncoderPixelSerializer final : public SkPixelSerializer { +protected: + bool onUseEncodedData(const void*, size_t) override { return true; } + SkData* onEncode(const SkPixmap&) override { return nullptr; } +}; +} // namespace + +SkPixelSerializer* SkImageEncoder::CreatePixelSerializer() { + return new ImageEncoderPixelSerializer; +} + ///////////////////////////////////////////////////////////////////////// diff --git a/gfx/skia/skia/src/ports/SkImageGenerator_skia.cpp b/gfx/skia/skia/src/ports/SkImageGenerator_skia.cpp index afcd4b4a92..8dbad555dd 100644 --- a/gfx/skia/skia/src/ports/SkImageGenerator_skia.cpp +++ b/gfx/skia/skia/src/ports/SkImageGenerator_skia.cpp @@ -43,7 +43,7 @@ public: {} protected: - SkData* onRefEncodedData() override { + SkData* onRefEncodedData(SK_REFENCODEDDATA_CTXPARAM) override { return SkRef(fData.get()); } bool onGetPixels(const SkImageInfo& info, void* pixels, size_t rowBytes, diff --git a/gfx/skia/skia/src/ports/SkOSEnvironment.cpp b/gfx/skia/skia/src/ports/SkOSEnvironment.cpp new file mode 100644 index 0000000000..cc7aa18cff --- /dev/null +++ b/gfx/skia/skia/src/ports/SkOSEnvironment.cpp @@ -0,0 +1,19 @@ + +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkOSEnvironment.h" +#include + +void sk_setenv(const char* key, const char* value) { +#ifdef SK_BUILD_FOR_WIN32 + _putenv_s(key, value); +#else + setenv(key, value, 1); +#endif +} + diff --git a/gfx/skia/skia/src/ports/SkOSEnvironment.h b/gfx/skia/skia/src/ports/SkOSEnvironment.h new file mode 100644 index 0000000000..fdbce7a826 --- /dev/null +++ b/gfx/skia/skia/src/ports/SkOSEnvironment.h @@ -0,0 +1,15 @@ + +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkOSEnvironment_DEFINED +#define SkOSEnvironment_DEFINED + +void sk_setenv(const char* key, const char* value); + +#endif + diff --git a/gfx/skia/skia/src/ports/SkOSFile_stdio.cpp b/gfx/skia/skia/src/ports/SkOSFile_stdio.cpp index 3371bb7031..915b87b67b 100644 --- a/gfx/skia/skia/src/ports/SkOSFile_stdio.cpp +++ b/gfx/skia/skia/src/ports/SkOSFile_stdio.cpp @@ -23,23 +23,23 @@ static FILE* ios_open_from_bundle(const char path[], const char* perm) { // Get a reference to the main bundle CFBundleRef mainBundle = CFBundleGetMainBundle(); - + // Get a reference to the file's URL CFStringRef pathRef = CFStringCreateWithCString(NULL, path, kCFStringEncodingUTF8); CFURLRef imageURL = CFBundleCopyResourceURL(mainBundle, pathRef, NULL, NULL); if (!imageURL) { return nullptr; } - + // Convert the URL reference into a string reference CFStringRef imagePath = CFURLCopyFileSystemPath(imageURL, kCFURLPOSIXPathStyle); - + // Get the system encoding method CFStringEncoding encodingMethod = CFStringGetSystemEncoding(); - + // Convert the string reference into a C string const char *finalPath = CFStringGetCStringPtr(imagePath, encodingMethod); - + return fopen(finalPath, perm); } #endif @@ -57,7 +57,7 @@ FILE* sk_fopen(const char path[], SkFILE_Flags flags) { } *p++ = 'b'; *p = 0; - + //TODO: on Windows fopen is just ASCII or the current code page, //convert to utf16 and use _wfopen FILE* file = nullptr; @@ -69,7 +69,7 @@ FILE* sk_fopen(const char path[], SkFILE_Flags flags) { // otherwise just read from the Documents directory (default) if (!file) { #endif - file = ::fopen(path, perm); + file = fopen(path, perm); #ifdef SK_BUILD_FOR_IOS } #endif @@ -81,7 +81,7 @@ FILE* sk_fopen(const char path[], SkFILE_Flags flags) { } char* sk_fgets(char* str, int size, FILE* f) { - return ::fgets(str, size, (FILE *)f); + return fgets(str, size, (FILE *)f); } int sk_feof(FILE *f) { @@ -92,18 +92,18 @@ int sk_feof(FILE *f) { size_t sk_fgetsize(FILE* f) { SkASSERT(f); - long curr = ::ftell(f); // remember where we are + long curr = ftell(f); // remember where we are if (curr < 0) { return 0; } - ::fseek(f, 0, SEEK_END); // go to the end - long size = ::ftell(f); // record the size + fseek(f, 0, SEEK_END); // go to the end + long size = ftell(f); // record the size if (size < 0) { size = 0; } - ::fseek(f, curr, SEEK_SET); // go back to our prev location + fseek(f, curr, SEEK_SET); // go back to our prev location return size; } @@ -116,12 +116,12 @@ bool sk_frewind(FILE* f) { size_t sk_fread(void* buffer, size_t byteCount, FILE* f) { SkASSERT(f); if (buffer == nullptr) { - size_t curr = ::ftell(f); + size_t curr = ftell(f); if ((long)curr == -1) { SkDEBUGF(("sk_fread: ftell(%p) returned -1 feof:%d ferror:%d\n", f, feof(f), ferror(f))); return 0; } - int err = ::fseek(f, (long)byteCount, SEEK_CUR); + int err = fseek(f, (long)byteCount, SEEK_CUR); if (err != 0) { SkDEBUGF(("sk_fread: fseek(%d) tell:%d failed with feof:%d ferror:%d returned:%d\n", byteCount, curr, feof(f), ferror(f), err)); @@ -130,31 +130,39 @@ size_t sk_fread(void* buffer, size_t byteCount, FILE* f) { return byteCount; } else - return ::fread(buffer, 1, byteCount, f); + return fread(buffer, 1, byteCount, f); } size_t sk_fwrite(const void* buffer, size_t byteCount, FILE* f) { SkASSERT(f); - return ::fwrite(buffer, 1, byteCount, f); + return fwrite(buffer, 1, byteCount, f); } void sk_fflush(FILE* f) { SkASSERT(f); - ::fflush(f); + fflush(f); +} + +void sk_fsync(FILE* f) { +#if !defined(_WIN32) && !defined(SK_BUILD_FOR_ANDROID) && !defined(__UCLIBC__) \ + && !defined(_NEWLIB_VERSION) + int fd = fileno(f); + fsync(fd); +#endif } bool sk_fseek(FILE* f, size_t byteCount) { - int err = ::fseek(f, (long)byteCount, SEEK_SET); + int err = fseek(f, (long)byteCount, SEEK_SET); return err == 0; } bool sk_fmove(FILE* f, long byteCount) { - int err = ::fseek(f, byteCount, SEEK_CUR); + int err = fseek(f, byteCount, SEEK_CUR); return err == 0; } size_t sk_ftell(FILE* f) { - long curr = ::ftell(f); + long curr = ftell(f); if (curr < 0) { return 0; } @@ -163,7 +171,7 @@ size_t sk_ftell(FILE* f) { void sk_fclose(FILE* f) { SkASSERT(f); - ::fclose(f); + fclose(f); } bool sk_isdir(const char *path) { diff --git a/gfx/skia/skia/src/ports/SkOSLibrary_win.cpp b/gfx/skia/skia/src/ports/SkOSLibrary_win.cpp index a1249f893b..9defb7ef15 100644 --- a/gfx/skia/skia/src/ports/SkOSLibrary_win.cpp +++ b/gfx/skia/skia/src/ports/SkOSLibrary_win.cpp @@ -16,7 +16,7 @@ void* DynamicLoadLibrary(const char* libraryName) { } void* GetProcedureAddress(void* library, const char* functionName) { - return ::GetProcAddress((HMODULE)library, functionName); + return reinterpret_cast(::GetProcAddress((HMODULE)library, functionName)); } #endif//defined(SK_BUILD_FOR_WIN32) diff --git a/gfx/skia/skia/src/ports/SkTime_Unix.cpp b/gfx/skia/skia/src/ports/SkTime_Unix.cpp index 396abc0436..2123a40619 100644 --- a/gfx/skia/skia/src/ports/SkTime_Unix.cpp +++ b/gfx/skia/skia/src/ports/SkTime_Unix.cpp @@ -5,36 +5,3 @@ * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ - -#include "SkTime.h" -#include "SkTypes.h" - -#include -#include - -void SkTime::GetDateTime(DateTime* dt) -{ - if (dt) - { - tzset(); // initialize timezone variable; - time_t m_time; - time(&m_time); - struct tm* tstruct; - tstruct = localtime(&m_time); - int offset = tstruct->tm_isdst == 1 ? 60 : 0; - - // http://pubs.opengroup.org/onlinepubs/009695399/basedefs/time.h.html -#if defined(__FreeBSD__) - dt->fTimeZoneMinutes = SkToS16(offset - tstruct->tm_gmtoff / 60); -#else - dt->fTimeZoneMinutes = SkToS16(offset - timezone / 60); -#endif - dt->fYear = tstruct->tm_year + 1900; - dt->fMonth = SkToU8(tstruct->tm_mon + 1); - dt->fDayOfWeek = SkToU8(tstruct->tm_wday); - dt->fDay = SkToU8(tstruct->tm_mday); - dt->fHour = SkToU8(tstruct->tm_hour); - dt->fMinute = SkToU8(tstruct->tm_min); - dt->fSecond = SkToU8(tstruct->tm_sec); - } -} diff --git a/gfx/skia/skia/src/ports/SkTime_win.cpp b/gfx/skia/skia/src/ports/SkTime_win.cpp index 2589d43d44..f55ff90ea3 100644 --- a/gfx/skia/skia/src/ports/SkTime_win.cpp +++ b/gfx/skia/skia/src/ports/SkTime_win.cpp @@ -5,40 +5,4 @@ * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ -#include "SkTypes.h" -#if defined(SK_BUILD_FOR_WIN32) - -#include "SkTime.h" - -void SkTime::GetDateTime(DateTime* dt) -{ - if (dt) - { - SYSTEMTIME st; - TIME_ZONE_INFORMATION timeZoneInfo; - int tz_bias; - GetLocalTime(&st); - // https://gist.github.com/wrl/8924636 - switch (GetTimeZoneInformation(&timeZoneInfo)) { - case TIME_ZONE_ID_STANDARD: - tz_bias = -timeZoneInfo.Bias - timeZoneInfo.StandardBias; - break; - case TIME_ZONE_ID_DAYLIGHT: - tz_bias = -timeZoneInfo.Bias - timeZoneInfo.DaylightBias; - break; - default: - tz_bias = -timeZoneInfo.Bias; - break; - } - dt->fTimeZoneMinutes = SkToS16(tz_bias); - dt->fYear = st.wYear; - dt->fMonth = SkToU8(st.wMonth); - dt->fDayOfWeek = SkToU8(st.wDayOfWeek); - dt->fDay = SkToU8(st.wDay); - dt->fHour = SkToU8(st.wHour); - dt->fMinute = SkToU8(st.wMinute); - dt->fSecond = SkToU8(st.wSecond); - } -} -#endif//defined(SK_BUILD_FOR_WIN32) diff --git a/gfx/skia/skia/src/svg/parser/SkSVG.cpp b/gfx/skia/skia/src/svg/parser/SkSVG.cpp index fdfc13a6d5..8ee7d0286a 100644 --- a/gfx/skia/skia/src/svg/parser/SkSVG.cpp +++ b/gfx/skia/skia/src/svg/parser/SkSVG.cpp @@ -8,7 +8,8 @@ #include "SkSVG.h" -#include 'SkSVGParser.h" +#include "SkSVGParser.h" +#include "SkTemplates.h" SkSVG::SkSVG() { } @@ -19,8 +20,8 @@ SkSVG::~SkSVG() { bool SkSVG::decodeStream(SkStream* stream); { size_t size = stream->read(nil, 0); - SkAutoMalloc storage(size); - char* data = (char*)storage.get(); + SkAutoTMalloc storage(size); + char* data = storage.get(); size_t actual = stream->read(data, size); SkASSERT(size == actual); SkSVGParser parser(*fMaker); diff --git a/gfx/skia/skia/src/utils/SkCubicInterval.cpp b/gfx/skia/skia/src/utils/SkCubicInterval.cpp deleted file mode 100644 index 566023a244..0000000000 --- a/gfx/skia/skia/src/utils/SkCubicInterval.cpp +++ /dev/null @@ -1,67 +0,0 @@ - -/* - * Copyright 2011 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ -#include "SkCubicInterval.h" - -static SkScalar eval_cubic(SkScalar c1, SkScalar c2, SkScalar c3, - SkScalar t) { - return SkScalarMul(SkScalarMul(SkScalarMul(c3, t) + c2, t) + c1, t); -} - -static SkScalar find_cubic_t(SkScalar c1, SkScalar c2, SkScalar c3, - SkScalar targetX) { - SkScalar minT = 0; - SkScalar maxT = SK_Scalar1; - SkScalar t; - - for (;;) { - t = SkScalarAve(minT, maxT); - SkScalar x = eval_cubic(c1, c2, c3, t); - if (SkScalarNearlyZero(x - targetX)) { - break; - } - // subdivide the range and try again - if (x < targetX) { - minT = t; - } else { - maxT = t; - } - } - return t; -} - -/* - a(1-t)^3 + 3bt(1-t)^2 + 3ct^2(1-t) + dt^3 - a: [0, 0] - d: [1, 1] - - 3bt - 6bt^2 + 3bt^3 + 3ct^2 - 3ct^3 + t^3 - C1 = t^1: 3b - C2 = t^2: 3c - 6b - C3 = t^3: 3b - 3c + 1 - - ((C3*t + C2)*t + C1)*t - */ -SkScalar SkEvalCubicInterval(SkScalar x1, SkScalar y1, - SkScalar x2, SkScalar y2, - SkScalar unitX) { - x1 = SkScalarPin(x1, 0, SK_Scalar1); - x2 = SkScalarPin(x2, 0, SK_Scalar1); - unitX = SkScalarPin(unitX, 0, SK_Scalar1); - - // First compute our coefficients in X - x1 *= 3; - x2 *= 3; - - // now search for t given unitX - SkScalar t = find_cubic_t(x1, x2 - 2*x1, x1 - x2 + SK_Scalar1, unitX); - - // now evaluate the cubic in Y - y1 *= 3; - y2 *= 3; - return eval_cubic(y1, y2 - 2*y1, y1 - y2 + SK_Scalar1, t); -} diff --git a/gfx/skia/skia/src/utils/SkCullPoints.cpp b/gfx/skia/skia/src/utils/SkCullPoints.cpp deleted file mode 100644 index f77acf1023..0000000000 --- a/gfx/skia/skia/src/utils/SkCullPoints.cpp +++ /dev/null @@ -1,211 +0,0 @@ -/* - * Copyright 2006 The Android Open Source Project - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "SkCullPoints.h" - -static bool cross_product_is_neg(const SkIPoint& v, int dx, int dy) { -#if 0 - return v.fX * dy - v.fY * dx < 0; -#else - return sk_64_mul(v.fX, dy) < sk_64_mul(dx, v.fY); -#endif -} - -bool SkCullPoints::sect_test(int x0, int y0, int x1, int y1) const { - const SkIRect& r = fR; - - if ((x0 < r.fLeft && x1 < r.fLeft) || - (x0 > r.fRight && x1 > r.fRight) || - (y0 < r.fTop && y1 < r.fTop) || - (y0 > r.fBottom && y1 > r.fBottom)) { - return false; - } - - // since the crossprod test is a little expensive, check for easy-in cases first - if (r.contains(x0, y0) || r.contains(x1, y1)) { - return true; - } - - // At this point we're not sure, so we do a crossprod test - SkIPoint vec; - const SkIPoint* rAsQuad = fAsQuad; - - vec.set(x1 - x0, y1 - y0); - bool isNeg = cross_product_is_neg(vec, x0 - rAsQuad[0].fX, y0 - rAsQuad[0].fY); - for (int i = 1; i < 4; i++) { - if (cross_product_is_neg(vec, x0 - rAsQuad[i].fX, y0 - rAsQuad[i].fY) != isNeg) { - return true; - } - } - return false; // we didn't intersect -} - -static void toQuad(const SkIRect& r, SkIPoint quad[4]) { - SkASSERT(quad); - - quad[0].set(r.fLeft, r.fTop); - quad[1].set(r.fRight, r.fTop); - quad[2].set(r.fRight, r.fBottom); - quad[3].set(r.fLeft, r.fBottom); -} - -SkCullPoints::SkCullPoints() { - SkIRect r; - r.setEmpty(); - this->reset(r); -} - -SkCullPoints::SkCullPoints(const SkIRect& r) { - this->reset(r); -} - -void SkCullPoints::reset(const SkIRect& r) { - fR = r; - toQuad(fR, fAsQuad); - fPrevPt.set(0, 0); - fPrevResult = kNo_Result; -} - -void SkCullPoints::moveTo(int x, int y) { - fPrevPt.set(x, y); - fPrevResult = kNo_Result; // so we trigger a movetolineto later -} - -SkCullPoints::LineToResult SkCullPoints::lineTo(int x, int y, SkIPoint line[]) { - SkASSERT(line != nullptr); - - LineToResult result = kNo_Result; - int x0 = fPrevPt.fX; - int y0 = fPrevPt.fY; - - // need to upgrade sect_test to chop the result - // and to correctly return kLineTo_Result when the result is connected - // to the previous call-out - if (this->sect_test(x0, y0, x, y)) { - line[0].set(x0, y0); - line[1].set(x, y); - - if (fPrevResult != kNo_Result && fPrevPt.equals(x0, y0)) { - result = kLineTo_Result; - } else { - result = kMoveToLineTo_Result; - } - } - - fPrevPt.set(x, y); - fPrevResult = result; - - return result; -} - -///////////////////////////////////////////////////////////////////////////////////////////////// - -#include "SkPath.h" - -SkCullPointsPath::SkCullPointsPath() - : fCP(), fPath(nullptr) { -} - -SkCullPointsPath::SkCullPointsPath(const SkIRect& r, SkPath* dst) - : fCP(r), fPath(dst) { -} - -void SkCullPointsPath::reset(const SkIRect& r, SkPath* dst) { - fCP.reset(r); - fPath = dst; -} - -void SkCullPointsPath::moveTo(int x, int y) { - fCP.moveTo(x, y); -} - -void SkCullPointsPath::lineTo(int x, int y) { - SkIPoint pts[2]; - - switch (fCP.lineTo(x, y, pts)) { - case SkCullPoints::kMoveToLineTo_Result: - fPath->moveTo(SkIntToScalar(pts[0].fX), SkIntToScalar(pts[0].fY)); - // fall through to the lineto case - case SkCullPoints::kLineTo_Result: - fPath->lineTo(SkIntToScalar(pts[1].fX), SkIntToScalar(pts[1].fY)); - break; - default: - break; - } -} - -/////////////////////////////////////////////////////////////////////////////// - -#include "SkMatrix.h" -#include "SkRegion.h" - -bool SkHitTestPath(const SkPath& path, SkRect& target, bool hires) { - if (target.isEmpty()) { - return false; - } - - bool isInverse = path.isInverseFillType(); - if (path.isEmpty()) { - return isInverse; - } - - SkRect bounds = path.getBounds(); - - bool sects = SkRect::Intersects(target, bounds); - if (isInverse) { - if (!sects) { - return true; - } - } else { - if (!sects) { - return false; - } - if (target.contains(bounds)) { - return true; - } - } - - SkPath devPath; - const SkPath* pathPtr; - SkRect devTarget; - - if (hires) { - const SkScalar coordLimit = SkIntToScalar(16384); - const SkRect limit = { 0, 0, coordLimit, coordLimit }; - - SkMatrix matrix; - matrix.setRectToRect(bounds, limit, SkMatrix::kFill_ScaleToFit); - - path.transform(matrix, &devPath); - matrix.mapRect(&devTarget, target); - - pathPtr = &devPath; - } else { - devTarget = target; - pathPtr = &path; - } - - SkIRect iTarget; - devTarget.round(&iTarget); - if (iTarget.isEmpty()) { - iTarget.fLeft = SkScalarFloorToInt(devTarget.fLeft); - iTarget.fTop = SkScalarFloorToInt(devTarget.fTop); - iTarget.fRight = iTarget.fLeft + 1; - iTarget.fBottom = iTarget.fTop + 1; - } - - SkRegion clip(iTarget); - SkRegion rgn; - return rgn.setPath(*pathPtr, clip) ^ isInverse; -} - -bool SkHitTestPath(const SkPath& path, SkScalar x, SkScalar y, bool hires) { - const SkScalar half = SK_ScalarHalf; - const SkScalar one = SK_Scalar1; - SkRect r = SkRect::MakeXYWH(x - half, y - half, one, one); - return SkHitTestPath(path, r, hires); -} diff --git a/gfx/skia/skia/src/utils/SkDashPath.cpp b/gfx/skia/skia/src/utils/SkDashPath.cpp index e0cbe9732d..4e34b87eac 100644 --- a/gfx/skia/skia/src/utils/SkDashPath.cpp +++ b/gfx/skia/skia/src/utils/SkDashPath.cpp @@ -10,7 +10,7 @@ #include "SkStrokeRec.h" static inline int is_even(int x) { - return (~x) << 31; + return !(x & 1); } static SkScalar find_first_interval(const SkScalar intervals[], SkScalar phase, diff --git a/gfx/skia/skia/src/utils/SkDumpCanvas.cpp b/gfx/skia/skia/src/utils/SkDumpCanvas.cpp index 8d0209d964..916c32a655 100644 --- a/gfx/skia/skia/src/utils/SkDumpCanvas.cpp +++ b/gfx/skia/skia/src/utils/SkDumpCanvas.cpp @@ -199,14 +199,14 @@ void SkDumpCanvas::willSave() { this->INHERITED::willSave(); } -SkCanvas::SaveLayerStrategy SkDumpCanvas::willSaveLayer(const SkRect* bounds, const SkPaint* paint, - SaveFlags flags) { +SkCanvas::SaveLayerStrategy SkDumpCanvas::getSaveLayerStrategy(const SaveLayerRec& rec) { SkString str; - str.printf("saveLayer(0x%X)", flags); - if (bounds) { + str.printf("saveLayer(0x%X)", rec.fSaveLayerFlags); + if (rec.fBounds) { str.append(" bounds"); - toString(*bounds, &str); + toString(*rec.fBounds, &str); } + const SkPaint* paint = rec.fPaint; if (paint) { if (paint->getAlpha() != 0xFF) { str.appendf(" alpha:0x%02X", paint->getAlpha()); @@ -216,7 +216,7 @@ SkCanvas::SaveLayerStrategy SkDumpCanvas::willSaveLayer(const SkRect* bounds, co } } this->dump(kSave_Verb, paint, str.c_str()); - return this->INHERITED::willSaveLayer(bounds, paint, flags); + return this->INHERITED::getSaveLayerStrategy(rec); } void SkDumpCanvas::willRestore() { @@ -397,13 +397,6 @@ void SkDumpCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, cons bs.c_str(), rs.c_str()); } -void SkDumpCanvas::onDrawSprite(const SkBitmap& bitmap, int x, int y, const SkPaint* paint) { - SkString str; - bitmap.toString(&str); - this->dump(kDrawBitmap_Verb, paint, "drawSprite(%s %d %d)", str.c_str(), - x, y); -} - void SkDumpCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y, const SkPaint& paint) { SkString str; diff --git a/gfx/skia/skia/src/utils/SkFrontBufferedStream.cpp b/gfx/skia/skia/src/utils/SkFrontBufferedStream.cpp index 0955cfaf71..e0b586a29d 100644 --- a/gfx/skia/skia/src/utils/SkFrontBufferedStream.cpp +++ b/gfx/skia/skia/src/utils/SkFrontBufferedStream.cpp @@ -16,7 +16,7 @@ public: size_t read(void* buffer, size_t size) override; - bool peek(void* buffer, size_t size) const override; + size_t peek(void* buffer, size_t size) const override; bool isAtEnd() const override; @@ -157,18 +157,20 @@ size_t FrontBufferedStream::readDirectlyFromStream(char* dst, size_t size) { return bytesReadDirectly; } -bool FrontBufferedStream::peek(void* dst, size_t size) const { +size_t FrontBufferedStream::peek(void* dst, size_t size) const { // Keep track of the offset so we can return to it. const size_t start = fOffset; - if (start + size > fBufferSize) { - // This stream is not able to buffer enough. - return false; + + if (start >= fBufferSize) { + // This stream is not able to buffer. + return 0; } + + size = SkTMin(size, fBufferSize - start); FrontBufferedStream* nonConstThis = const_cast(this); - SkDEBUGCODE(const size_t bytesRead =) nonConstThis->read(dst, size); - SkASSERT(bytesRead == size); + const size_t bytesRead = nonConstThis->read(dst, size); nonConstThis->fOffset = start; - return true; + return bytesRead; } size_t FrontBufferedStream::read(void* voidDst, size_t size) { diff --git a/gfx/skia/skia/src/utils/SkInterpolator.cpp b/gfx/skia/skia/src/utils/SkInterpolator.cpp index cd9ad3a2db..197d977cac 100644 --- a/gfx/skia/skia/src/utils/SkInterpolator.cpp +++ b/gfx/skia/skia/src/utils/SkInterpolator.cpp @@ -68,7 +68,7 @@ SkScalar SkInterpolatorBase::ComputeRelativeT(SkMSec time, SkMSec prevTime, } SkInterpolatorBase::Result SkInterpolatorBase::timeToT(SkMSec time, SkScalar* T, - int* indexPtr, SkBool* exactPtr) const { + int* indexPtr, bool* exactPtr) const { SkASSERT(fFrameCount > 0); Result result = kNormal_Result; if (fRepeat != SK_Scalar1) { @@ -182,7 +182,7 @@ SkInterpolator::Result SkInterpolator::timeToValues(SkMSec time, SkScalar values[]) const { SkScalar T; int index; - SkBool exact; + bool exact; Result result = timeToT(time, &T, &index, &exact); if (values) { const SkScalar* nextSrc = &fValues[index * fElemCount]; diff --git a/gfx/skia/skia/src/utils/SkLua.cpp b/gfx/skia/skia/src/utils/SkLua.cpp index 307e5f6152..d85fb91a03 100644 --- a/gfx/skia/skia/src/utils/SkLua.cpp +++ b/gfx/skia/skia/src/utils/SkLua.cpp @@ -13,6 +13,7 @@ #include "SkBlurImageFilter.h" #include "SkCanvas.h" +#include "SkColorFilter.h" #include "SkData.h" #include "SkDocument.h" #include "SkGradientShader.h" @@ -42,6 +43,7 @@ template const char* get_mtname(); } DEF_MTNAME(SkCanvas) +DEF_MTNAME(SkColorFilter) DEF_MTNAME(SkDocument) DEF_MTNAME(SkImage) DEF_MTNAME(SkImageFilter) @@ -1079,6 +1081,22 @@ static int lpaint_getEffects(lua_State* L) { return 1; } +static int lpaint_getColorFilter(lua_State* L) { + const SkPaint* paint = get_obj(L, 1); + SkColorFilter* cf = paint->getColorFilter(); + if (cf) { + push_ref(L, cf); + return 1; + } + return 0; +} + +static int lpaint_setColorFilter(lua_State* L) { + SkPaint* paint = get_obj(L, 1); + paint->setColorFilter(get_ref(L, 2)); + return 0; +} + static int lpaint_getImageFilter(lua_State* L) { const SkPaint* paint = get_obj(L, 1); SkImageFilter* imf = paint->getImageFilter(); @@ -1170,6 +1188,8 @@ static const struct luaL_Reg gSkPaint_Methods[] = { { "measureText", lpaint_measureText }, { "getFontMetrics", lpaint_getFontMetrics }, { "getEffects", lpaint_getEffects }, + { "getColorFilter", lpaint_getColorFilter }, + { "setColorFilter", lpaint_setColorFilter }, { "getImageFilter", lpaint_getImageFilter }, { "setImageFilter", lpaint_setImageFilter }, { "getShader", lpaint_getShader }, @@ -1294,6 +1314,18 @@ static const struct luaL_Reg gSkPathEffect_Methods[] = { /////////////////////////////////////////////////////////////////////////////// +static int lpcolorfilter_gc(lua_State* L) { + get_ref(L, 1)->unref(); + return 0; +} + +static const struct luaL_Reg gSkColorFilter_Methods[] = { + { "__gc", lpcolorfilter_gc }, + { nullptr, nullptr } +}; + +/////////////////////////////////////////////////////////////////////////////// + static int lpimagefilter_gc(lua_State* L) { get_ref(L, 1)->unref(); return 0; @@ -2048,6 +2080,7 @@ static void register_Sk(lua_State* L) { void SkLua::Load(lua_State* L) { register_Sk(L); REG_CLASS(L, SkCanvas); + REG_CLASS(L, SkColorFilter); REG_CLASS(L, SkDocument); REG_CLASS(L, SkImage); REG_CLASS(L, SkImageFilter); diff --git a/gfx/skia/skia/src/utils/SkLuaCanvas.cpp b/gfx/skia/skia/src/utils/SkLuaCanvas.cpp index ada76666fe..c51b0d8a7c 100644 --- a/gfx/skia/skia/src/utils/SkLuaCanvas.cpp +++ b/gfx/skia/skia/src/utils/SkLuaCanvas.cpp @@ -86,17 +86,16 @@ void SkLuaCanvas::willSave() { this->INHERITED::willSave(); } -SkCanvas::SaveLayerStrategy SkLuaCanvas::willSaveLayer(const SkRect* bounds, const SkPaint* paint, - SaveFlags flags) { +SkCanvas::SaveLayerStrategy SkLuaCanvas::getSaveLayerStrategy(const SaveLayerRec& rec) { AUTO_LUA("saveLayer"); - if (bounds) { - lua.pushRect(*bounds, "bounds"); + if (rec.fBounds) { + lua.pushRect(*rec.fBounds, "bounds"); } - if (paint) { - lua.pushPaint(*paint, "paint"); + if (rec.fPaint) { + lua.pushPaint(*rec.fPaint, "paint"); } - this->INHERITED::willSaveLayer(bounds, paint, flags); + (void)this->INHERITED::getSaveLayerStrategy(rec); // No need for a layer. return kNoLayer_SaveLayerStrategy; } @@ -245,13 +244,6 @@ void SkLuaCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const } } -void SkLuaCanvas::onDrawSprite(const SkBitmap& bitmap, int x, int y, const SkPaint* paint) { - AUTO_LUA("drawSprite"); - if (paint) { - lua.pushPaint(*paint, "paint"); - } -} - void SkLuaCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y, const SkPaint& paint) { AUTO_LUA("drawText"); diff --git a/gfx/skia/skia/src/utils/SkNWayCanvas.cpp b/gfx/skia/skia/src/utils/SkNWayCanvas.cpp index fb8d0ee13e..4f60ca33cf 100644 --- a/gfx/skia/skia/src/utils/SkNWayCanvas.cpp +++ b/gfx/skia/skia/src/utils/SkNWayCanvas.cpp @@ -66,14 +66,13 @@ void SkNWayCanvas::willSave() { this->INHERITED::willSave(); } -SkCanvas::SaveLayerStrategy SkNWayCanvas::willSaveLayer(const SkRect* bounds, const SkPaint* paint, - SaveFlags flags) { +SkCanvas::SaveLayerStrategy SkNWayCanvas::getSaveLayerStrategy(const SaveLayerRec& rec) { Iter iter(fList); while (iter.next()) { - iter->saveLayer(bounds, paint, flags); + iter->saveLayer(rec); } - this->INHERITED::willSaveLayer(bounds, paint, flags); + this->INHERITED::getSaveLayerStrategy(rec); // No need for a layer. return kNoLayer_SaveLayerStrategy; } @@ -224,13 +223,6 @@ void SkNWayCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, cons } } -void SkNWayCanvas::onDrawSprite(const SkBitmap& bitmap, int x, int y, const SkPaint* paint) { - Iter iter(fList); - while (iter.next()) { - iter->drawSprite(bitmap, x, y, paint); - } -} - void SkNWayCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y, const SkPaint& paint) { Iter iter(fList); diff --git a/gfx/skia/skia/src/utils/SkPaintFilterCanvas.cpp b/gfx/skia/skia/src/utils/SkPaintFilterCanvas.cpp index dff1514acb..ea94068cb6 100644 --- a/gfx/skia/skia/src/utils/SkPaintFilterCanvas.cpp +++ b/gfx/skia/skia/src/utils/SkPaintFilterCanvas.cpp @@ -12,20 +12,21 @@ class SkPaintFilterCanvas::AutoPaintFilter { public: - AutoPaintFilter(const SkPaintFilterCanvas* canvas, Type type, const SkPaint* paint) { - if (paint) { - canvas->onFilterPaint(fLazyPaint.set(*paint), type); - } + AutoPaintFilter(const SkPaintFilterCanvas* canvas, Type type, const SkPaint* paint) + : fPaint(paint) { + fShouldDraw = canvas->onFilter(&fPaint, type); } - AutoPaintFilter(const SkPaintFilterCanvas* canvas, Type type, const SkPaint& paint) { - canvas->onFilterPaint(fLazyPaint.set(paint), type); - } + AutoPaintFilter(const SkPaintFilterCanvas* canvas, Type type, const SkPaint& paint) + : AutoPaintFilter(canvas, type, &paint) { } - const SkPaint* paint() const { return fLazyPaint.getMaybeNull(); } + const SkPaint* paint() const { return fPaint; } + + bool shouldDraw() const { return fShouldDraw; } private: - SkTLazy fLazyPaint; + SkTCopyOnFirstWrite fPaint; + bool fShouldDraw; }; SkPaintFilterCanvas::SkPaintFilterCanvas(int width, int height) : INHERITED(width, height) { } @@ -44,76 +45,102 @@ SkPaintFilterCanvas::SkPaintFilterCanvas(SkCanvas *canvas) void SkPaintFilterCanvas::onDrawPaint(const SkPaint& paint) { AutoPaintFilter apf(this, kPaint_Type, paint); - this->INHERITED::onDrawPaint(*apf.paint()); + if (apf.shouldDraw()) { + this->INHERITED::onDrawPaint(*apf.paint()); + } } void SkPaintFilterCanvas::onDrawPoints(PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint) { AutoPaintFilter apf(this, kPoint_Type, paint); - this->INHERITED::onDrawPoints(mode, count, pts, *apf.paint()); + if (apf.shouldDraw()) { + this->INHERITED::onDrawPoints(mode, count, pts, *apf.paint()); + } } void SkPaintFilterCanvas::onDrawRect(const SkRect& rect, const SkPaint& paint) { AutoPaintFilter apf(this, kRect_Type, paint); - this->INHERITED::onDrawRect(rect, *apf.paint()); + if (apf.shouldDraw()) { + this->INHERITED::onDrawRect(rect, *apf.paint()); + } } void SkPaintFilterCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) { AutoPaintFilter apf(this, kRRect_Type, paint); - this->INHERITED::onDrawRRect(rrect, *apf.paint()); + if (apf.shouldDraw()) { + this->INHERITED::onDrawRRect(rrect, *apf.paint()); + } } void SkPaintFilterCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner, const SkPaint& paint) { AutoPaintFilter apf(this, kDRRect_Type, paint); - this->INHERITED::onDrawDRRect(outer, inner, *apf.paint()); + if (apf.shouldDraw()) { + this->INHERITED::onDrawDRRect(outer, inner, *apf.paint()); + } } void SkPaintFilterCanvas::onDrawOval(const SkRect& rect, const SkPaint& paint) { AutoPaintFilter apf(this, kOval_Type, paint); - this->INHERITED::onDrawOval(rect, *apf.paint()); + if (apf.shouldDraw()) { + this->INHERITED::onDrawOval(rect, *apf.paint()); + } } void SkPaintFilterCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) { AutoPaintFilter apf(this, kPath_Type, paint); - this->INHERITED::onDrawPath(path, *apf.paint()); + if (apf.shouldDraw()) { + this->INHERITED::onDrawPath(path, *apf.paint()); + } } void SkPaintFilterCanvas::onDrawBitmap(const SkBitmap& bm, SkScalar left, SkScalar top, const SkPaint* paint) { AutoPaintFilter apf(this, kBitmap_Type, paint); - this->INHERITED::onDrawBitmap(bm, left, top, apf.paint()); + if (apf.shouldDraw()) { + this->INHERITED::onDrawBitmap(bm, left, top, apf.paint()); + } } void SkPaintFilterCanvas::onDrawBitmapRect(const SkBitmap& bm, const SkRect* src, const SkRect& dst, const SkPaint* paint, SrcRectConstraint constraint) { AutoPaintFilter apf(this, kBitmap_Type, paint); - this->INHERITED::onDrawBitmapRect(bm, src, dst, apf.paint(), constraint); + if (apf.shouldDraw()) { + this->INHERITED::onDrawBitmapRect(bm, src, dst, apf.paint(), constraint); + } +} + +void SkPaintFilterCanvas::onDrawBitmapNine(const SkBitmap& bm, const SkIRect& center, + const SkRect& dst, const SkPaint* paint) { + AutoPaintFilter apf(this, kBitmap_Type, paint); + if (apf.shouldDraw()) { + this->INHERITED::onDrawBitmapNine(bm, center, dst, apf.paint()); + } } void SkPaintFilterCanvas::onDrawImage(const SkImage* image, SkScalar left, SkScalar top, const SkPaint* paint) { AutoPaintFilter apf(this, kBitmap_Type, paint); - this->INHERITED::onDrawImage(image, left, top, apf.paint()); + if (apf.shouldDraw()) { + this->INHERITED::onDrawImage(image, left, top, apf.paint()); + } } void SkPaintFilterCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst, const SkPaint* paint, SrcRectConstraint constraint) { AutoPaintFilter apf(this, kBitmap_Type, paint); - this->INHERITED::onDrawImageRect(image, src, dst, apf.paint(), constraint); + if (apf.shouldDraw()) { + this->INHERITED::onDrawImageRect(image, src, dst, apf.paint(), constraint); + } } -void SkPaintFilterCanvas::onDrawBitmapNine(const SkBitmap& bm, const SkIRect& center, - const SkRect& dst, const SkPaint* paint) { +void SkPaintFilterCanvas::onDrawImageNine(const SkImage* image, const SkIRect& center, + const SkRect& dst, const SkPaint* paint) { AutoPaintFilter apf(this, kBitmap_Type, paint); - this->INHERITED::onDrawBitmapNine(bm, center, dst, apf.paint()); -} - -void SkPaintFilterCanvas::onDrawSprite(const SkBitmap& bm, int left, int top, - const SkPaint* paint) { - AutoPaintFilter apf(this, kBitmap_Type, paint); - this->INHERITED::onDrawSprite(bm, left, top, apf.paint()); + if (apf.shouldDraw()) { + this->INHERITED::onDrawImageNine(image, center, dst, apf.paint()); + } } void SkPaintFilterCanvas::onDrawVertices(VertexMode vmode, int vertexCount, @@ -122,49 +149,65 @@ void SkPaintFilterCanvas::onDrawVertices(VertexMode vmode, int vertexCount, const uint16_t indices[], int indexCount, const SkPaint& paint) { AutoPaintFilter apf(this, kVertices_Type, paint); - this->INHERITED::onDrawVertices(vmode, vertexCount, vertices, texs, colors, xmode, indices, - indexCount, *apf.paint()); + if (apf.shouldDraw()) { + this->INHERITED::onDrawVertices(vmode, vertexCount, vertices, texs, colors, xmode, indices, + indexCount, *apf.paint()); + } } void SkPaintFilterCanvas::onDrawPatch(const SkPoint cubics[], const SkColor colors[], const SkPoint texCoords[], SkXfermode* xmode, const SkPaint& paint) { AutoPaintFilter apf(this, kPatch_Type, paint); - this->INHERITED::onDrawPatch(cubics, colors, texCoords, xmode, *apf.paint()); + if (apf.shouldDraw()) { + this->INHERITED::onDrawPatch(cubics, colors, texCoords, xmode, *apf.paint()); + } } void SkPaintFilterCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* m, const SkPaint* paint) { AutoPaintFilter apf(this, kPicture_Type, paint); - this->INHERITED::onDrawPicture(picture, m, apf.paint()); + if (apf.shouldDraw()) { + this->INHERITED::onDrawPicture(picture, m, apf.paint()); + } } void SkPaintFilterCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y, const SkPaint& paint) { AutoPaintFilter apf(this, kText_Type, paint); - this->INHERITED::onDrawText(text, byteLength, x, y, *apf.paint()); + if (apf.shouldDraw()) { + this->INHERITED::onDrawText(text, byteLength, x, y, *apf.paint()); + } } void SkPaintFilterCanvas::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[], const SkPaint& paint) { AutoPaintFilter apf(this, kText_Type, paint); - this->INHERITED::onDrawPosText(text, byteLength, pos, *apf.paint()); + if (apf.shouldDraw()) { + this->INHERITED::onDrawPosText(text, byteLength, pos, *apf.paint()); + } } void SkPaintFilterCanvas::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[], SkScalar constY, const SkPaint& paint) { AutoPaintFilter apf(this, kText_Type, paint); - this->INHERITED::onDrawPosTextH(text, byteLength, xpos, constY, *apf.paint()); + if (apf.shouldDraw()) { + this->INHERITED::onDrawPosTextH(text, byteLength, xpos, constY, *apf.paint()); + } } void SkPaintFilterCanvas::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path, const SkMatrix* matrix, const SkPaint& paint) { AutoPaintFilter apf(this, kText_Type, paint); - this->INHERITED::onDrawTextOnPath(text, byteLength, path, matrix, *apf.paint()); + if (apf.shouldDraw()) { + this->INHERITED::onDrawTextOnPath(text, byteLength, path, matrix, *apf.paint()); + } } void SkPaintFilterCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y, const SkPaint& paint) { AutoPaintFilter apf(this, kTextBlob_Type, paint); - this->INHERITED::onDrawTextBlob(blob, x, y, *apf.paint()); + if (apf.shouldDraw()) { + this->INHERITED::onDrawTextBlob(blob, x, y, *apf.paint()); + } } diff --git a/gfx/skia/skia/src/utils/SkParsePath.cpp b/gfx/skia/skia/src/utils/SkParsePath.cpp index 3fecb46fe6..3eb9e1ef79 100644 --- a/gfx/skia/skia/src/utils/SkParsePath.cpp +++ b/gfx/skia/skia/src/utils/SkParsePath.cpp @@ -77,6 +77,10 @@ bool SkParsePath::FromSVGString(const char data[], SkPath* result) { char previousOp = '\0'; bool relative = false; for (;;) { + if (!data) { + // Truncated data + return false; + } data = skip_ws(data); if (data[0] == '\0') { break; diff --git a/gfx/skia/skia/src/utils/SkTFitsIn.h b/gfx/skia/skia/src/utils/SkTFitsIn.h index cf92822d8f..08f7aef5f0 100644 --- a/gfx/skia/skia/src/utils/SkTFitsIn.h +++ b/gfx/skia/skia/src/utils/SkTFitsIn.h @@ -31,7 +31,7 @@ template struct SkTHasMoreDigits * that source values are in the range of the Destination. */ template struct SkTOutOfRange_False { - typedef skstd::false_type can_be_true; + typedef std::false_type can_be_true; typedef S source_type; static bool apply(S s) { return false; @@ -42,7 +42,7 @@ template struct SkTOutOfRange_False { * Assumes that Min(S) <= Min(D). */ template struct SkTOutOfRange_LT_MinD { - typedef skstd::true_type can_be_true; + typedef std::true_type can_be_true; typedef S source_type; static bool apply(S s) { typedef SkTHasMoreDigits precondition; @@ -54,7 +54,7 @@ template struct SkTOutOfRange_LT_MinD { /** A low side predicate which tests if the source value is less than 0. */ template struct SkTOutOfRange_LT_Zero { - typedef skstd::true_type can_be_true; + typedef std::true_type can_be_true; typedef S source_type; static bool apply(S s) { return s < static_cast(0); @@ -65,7 +65,7 @@ template struct SkTOutOfRange_LT_Zero { * Assumes that Max(S) >= Max(D). */ template struct SkTOutOfRange_GT_MaxD { - typedef skstd::true_type can_be_true; + typedef std::true_type can_be_true; typedef S source_type; static bool apply(S s) { typedef SkTHasMoreDigits precondition; @@ -79,7 +79,7 @@ template struct SkTOutOfRange_GT_MaxD { * First checks OutOfRange_Low then, if in range, OutOfRange_High. */ template struct SkTOutOfRange_Either { - typedef skstd::true_type can_be_true; + typedef std::true_type can_be_true; typedef typename OutOfRange_Low::source_type source_type; static bool apply(source_type s) { bool outOfRange = OutOfRange_Low::apply(s); diff --git a/gfx/skia/skia/src/utils/android/SkAndroidSDKCanvas.cpp b/gfx/skia/skia/src/utils/android/SkAndroidSDKCanvas.cpp deleted file mode 100644 index e6f802fc5b..0000000000 --- a/gfx/skia/skia/src/utils/android/SkAndroidSDKCanvas.cpp +++ /dev/null @@ -1,374 +0,0 @@ -/* - * Copyright 2015 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "SkAndroidSDKCanvas.h" - -#include "SkColorFilter.h" -#include "SkPaint.h" -#include "SkPathEffect.h" -#include "SkShader.h" -#include "SkTLazy.h" - -namespace { - -/** Discard SkShaders not exposed by the Android Java API. */ - -void CheckShader(SkPaint* paint) { - SkShader* shader = paint->getShader(); - if (!shader) { - return; - } - - if (shader->isABitmap()) { - return; - } - if (shader->asACompose(nullptr)) { - return; - } - SkShader::GradientType gtype = shader->asAGradient(nullptr); - if (gtype == SkShader::kLinear_GradientType || - gtype == SkShader::kRadial_GradientType || - gtype == SkShader::kSweep_GradientType) { - return; - } - paint->setShader(nullptr); -} - -void Filter(SkPaint* paint) { - - uint32_t flags = paint->getFlags(); - flags &= ~SkPaint::kLCDRenderText_Flag; - paint->setFlags(flags); - - // Android doesn't support Xfermodes above kLighten_Mode - SkXfermode::Mode mode; - SkXfermode::AsMode(paint->getXfermode(), &mode); - if (mode > SkXfermode::kLighten_Mode) { - paint->setXfermode(nullptr); - } - - // Force bilinear scaling or none - if (paint->getFilterQuality() != kNone_SkFilterQuality) { - paint->setFilterQuality(kLow_SkFilterQuality); - } - - CheckShader(paint); - - // Android SDK only supports mode & matrix color filters - // (and, again, no modes above kLighten_Mode). - SkColorFilter* cf = paint->getColorFilter(); - if (cf) { - SkColor color; - SkXfermode::Mode mode; - SkScalar srcColorMatrix[20]; - bool isMode = cf->asColorMode(&color, &mode); - if (isMode && mode > SkXfermode::kLighten_Mode) { - paint->setColorFilter( - SkColorFilter::CreateModeFilter(color, SkXfermode::kSrcOver_Mode)); - } else if (!isMode && !cf->asColorMatrix(srcColorMatrix)) { - paint->setColorFilter(nullptr); - } - } - -#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK - SkPathEffect* pe = paint->getPathEffect(); - if (pe && !pe->exposedInAndroidJavaAPI()) { - paint->setPathEffect(nullptr); - } -#endif - - // TODO: Android doesn't support all the flags that can be passed to - // blur filters; we need plumbing to get them out. - - paint->setImageFilter(nullptr); - paint->setLooper(nullptr); -}; - -} // namespace - -#define FILTER(p) \ - SkPaint filteredPaint(p); \ - Filter(&filteredPaint); - -#define FILTER_PTR(p) \ - SkTLazy lazyPaint; \ - SkPaint* filteredPaint = (SkPaint*) p; \ - if (p) { \ - filteredPaint = lazyPaint.set(*p); \ - Filter(filteredPaint); \ - } - - -SkAndroidSDKCanvas::SkAndroidSDKCanvas() : fProxyTarget(nullptr) { } - -void SkAndroidSDKCanvas::reset(SkCanvas* newTarget) { fProxyTarget = newTarget; } - -void SkAndroidSDKCanvas::onDrawPaint(const SkPaint& paint) { - FILTER(paint); - fProxyTarget->drawPaint(filteredPaint); -} -void SkAndroidSDKCanvas::onDrawPoints(PointMode pMode, - size_t count, - const SkPoint pts[], - const SkPaint& paint) { - FILTER(paint); - fProxyTarget->drawPoints(pMode, count, pts, filteredPaint); -} -void SkAndroidSDKCanvas::onDrawOval(const SkRect& r, const SkPaint& paint) { - FILTER(paint); - fProxyTarget->drawOval(r, filteredPaint); -} -void SkAndroidSDKCanvas::onDrawRect(const SkRect& r, const SkPaint& paint) { - FILTER(paint); - fProxyTarget->drawRect(r, filteredPaint); -} -void SkAndroidSDKCanvas::onDrawRRect(const SkRRect& r, const SkPaint& paint) { - FILTER(paint); - fProxyTarget->drawRRect(r, filteredPaint); -} -void SkAndroidSDKCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) { - FILTER(paint); - fProxyTarget->drawPath(path, filteredPaint); -} -void SkAndroidSDKCanvas::onDrawBitmap(const SkBitmap& bitmap, - SkScalar left, - SkScalar top, - const SkPaint* paint) { - FILTER_PTR(paint); - fProxyTarget->drawBitmap(bitmap, left, top, filteredPaint); -} -void SkAndroidSDKCanvas::onDrawBitmapRect(const SkBitmap& bitmap, - const SkRect* src, - const SkRect& dst, - const SkPaint* paint, - SkCanvas::SrcRectConstraint constraint) { - FILTER_PTR(paint); - fProxyTarget->legacy_drawBitmapRect(bitmap, src, dst, filteredPaint, constraint); -} -void SkAndroidSDKCanvas::onDrawBitmapNine(const SkBitmap& bitmap, - const SkIRect& center, - const SkRect& dst, - const SkPaint* paint) { - FILTER_PTR(paint); - fProxyTarget->drawBitmapNine(bitmap, center, dst, filteredPaint); -} -void SkAndroidSDKCanvas::onDrawSprite(const SkBitmap& bitmap, - int left, - int top, - const SkPaint* paint) { - FILTER_PTR(paint); - fProxyTarget->drawSprite(bitmap, left, top, filteredPaint); -} -void SkAndroidSDKCanvas::onDrawVertices(VertexMode vMode, - int vertexCount, - const SkPoint vertices[], - const SkPoint texs[], const SkColor colors[], SkXfermode* xMode, - const uint16_t indices[], int indexCount, - const SkPaint& paint) { - FILTER(paint); - fProxyTarget->drawVertices(vMode, vertexCount, vertices, texs, colors, - xMode, indices, indexCount, filteredPaint); -} - -void SkAndroidSDKCanvas::onDrawDRRect(const SkRRect& outer, - const SkRRect& inner, - const SkPaint& paint) { - FILTER(paint); - fProxyTarget->drawDRRect(outer, inner, filteredPaint); -} - -void SkAndroidSDKCanvas::onDrawText(const void* text, - size_t byteLength, - SkScalar x, - SkScalar y, - const SkPaint& paint) { - FILTER(paint); - fProxyTarget->drawText(text, byteLength, x, y, filteredPaint); -} -void SkAndroidSDKCanvas::onDrawPosText(const void* text, - size_t byteLength, - const SkPoint pos[], - const SkPaint& paint) { - FILTER(paint); - fProxyTarget->drawPosText(text, byteLength, pos, filteredPaint); -} -void SkAndroidSDKCanvas::onDrawPosTextH(const void* text, - size_t byteLength, - const SkScalar xpos[], - SkScalar constY, - const SkPaint& paint) { - FILTER(paint); - fProxyTarget->drawPosTextH(text, byteLength, xpos, constY, filteredPaint); -} -void SkAndroidSDKCanvas::onDrawTextOnPath(const void* text, - size_t byteLength, - const SkPath& path, - const SkMatrix* matrix, - const SkPaint& paint) { - FILTER(paint); - fProxyTarget->drawTextOnPath(text, byteLength, path, matrix, filteredPaint); -} -void SkAndroidSDKCanvas::onDrawTextBlob(const SkTextBlob* blob, - SkScalar x, - SkScalar y, - const SkPaint& paint) { - FILTER(paint); - fProxyTarget->drawTextBlob(blob, x, y, filteredPaint); -} - -void SkAndroidSDKCanvas::onDrawPatch(const SkPoint cubics[12], - const SkColor colors[4], - const SkPoint texCoords[4], - SkXfermode* xmode, - const SkPaint& paint) { - FILTER(paint); - fProxyTarget->drawPatch(cubics, colors, texCoords, xmode, filteredPaint); -} - - -void SkAndroidSDKCanvas::onDrawImage(const SkImage* image, - SkScalar x, - SkScalar y, - const SkPaint* paint) { - FILTER_PTR(paint); - fProxyTarget->drawImage(image, x, y, filteredPaint); -} - -void SkAndroidSDKCanvas::onDrawImageRect(const SkImage* image, - const SkRect* in, - const SkRect& out, - const SkPaint* paint, - SrcRectConstraint constraint) { - FILTER_PTR(paint); - fProxyTarget->legacy_drawImageRect(image, in, out, filteredPaint, constraint); -} - -void SkAndroidSDKCanvas::onDrawPicture(const SkPicture* picture, - const SkMatrix* matrix, - const SkPaint* paint) { - FILTER_PTR(paint); - fProxyTarget->drawPicture(picture, matrix, filteredPaint); -} - -void SkAndroidSDKCanvas::onDrawAtlas(const SkImage* atlas, - const SkRSXform xform[], - const SkRect tex[], - const SkColor colors[], - int count, - SkXfermode::Mode mode, - const SkRect* cullRect, - const SkPaint* paint) { - FILTER_PTR(paint); - fProxyTarget->drawAtlas(atlas, xform, tex, colors, count, mode, cullRect, - filteredPaint); -} - -void SkAndroidSDKCanvas::onDrawImageNine(const SkImage* image, - const SkIRect& center, - const SkRect& dst, - const SkPaint* paint) { - FILTER_PTR(paint); - fProxyTarget->drawImageNine(image, center, dst, filteredPaint); -} - - -void SkAndroidSDKCanvas::onDrawDrawable(SkDrawable* drawable, const SkMatrix* matrix) { - fProxyTarget->drawDrawable(drawable, matrix); -} - -SkISize SkAndroidSDKCanvas::getBaseLayerSize() const { - return fProxyTarget->getBaseLayerSize(); -} -bool SkAndroidSDKCanvas::getClipBounds(SkRect* rect) const { - return fProxyTarget->getClipBounds(rect); -} -bool SkAndroidSDKCanvas::getClipDeviceBounds(SkIRect* rect) const { - return fProxyTarget->getClipDeviceBounds(rect); -} - -bool SkAndroidSDKCanvas::isClipEmpty() const { return fProxyTarget->isClipEmpty(); } -bool SkAndroidSDKCanvas::isClipRect() const { return fProxyTarget->isClipRect(); } - -SkSurface* SkAndroidSDKCanvas::onNewSurface(const SkImageInfo& info, - const SkSurfaceProps& props) { - return fProxyTarget->newSurface(info, &props); -} - -bool SkAndroidSDKCanvas::onPeekPixels(SkPixmap* pmap) { - SkASSERT(pmap); - SkImageInfo info; - size_t rowBytes; - const void* addr = fProxyTarget->peekPixels(&info, &rowBytes); - if (addr) { - pmap->reset(info, addr, rowBytes); - return true; - } - return false; -} - -bool SkAndroidSDKCanvas::onAccessTopLayerPixels(SkPixmap* pmap) { - SkASSERT(pmap); - SkImageInfo info; - size_t rowBytes; - const void* addr = fProxyTarget->accessTopLayerPixels(&info, &rowBytes, nullptr); - if (addr) { - pmap->reset(info, addr, rowBytes); - return true; - } - return false; -} - -void SkAndroidSDKCanvas::willSave() { - fProxyTarget->save(); -} - -SkCanvas::SaveLayerStrategy SkAndroidSDKCanvas::willSaveLayer(const SkRect* rect, - const SkPaint* paint, - SaveFlags flags) { - fProxyTarget->saveLayer(rect, paint, flags); - return SkCanvas::kNoLayer_SaveLayerStrategy; -} - -void SkAndroidSDKCanvas::willRestore() { - fProxyTarget->restore(); -} - -void SkAndroidSDKCanvas::didRestore() { } - -void SkAndroidSDKCanvas::didConcat(const SkMatrix& m) { - fProxyTarget->concat(m); -} - -void SkAndroidSDKCanvas::didSetMatrix(const SkMatrix& m) { - fProxyTarget->setMatrix(m); -} - -void SkAndroidSDKCanvas::onClipRect(const SkRect& rect, - SkRegion::Op op, - ClipEdgeStyle style) { - fProxyTarget->clipRect(rect, op, style); -} - -void SkAndroidSDKCanvas::onClipRRect(const SkRRect& rrect, - SkRegion::Op op, - ClipEdgeStyle style) { - fProxyTarget->clipRRect(rrect, op, style); -} - -void SkAndroidSDKCanvas::onClipPath(const SkPath& path, - SkRegion::Op op, - ClipEdgeStyle style) { - fProxyTarget->clipPath(path, op, style); -} - -void SkAndroidSDKCanvas::onClipRegion(const SkRegion& region, SkRegion::Op op) { - fProxyTarget->clipRegion(region, op); -} - -void SkAndroidSDKCanvas::onDiscard() { fProxyTarget->discard(); } - - diff --git a/gfx/skia/skia/src/utils/android/SkAndroidSDKCanvas.h b/gfx/skia/skia/src/utils/android/SkAndroidSDKCanvas.h deleted file mode 100644 index 08b73f144c..0000000000 --- a/gfx/skia/skia/src/utils/android/SkAndroidSDKCanvas.h +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright 2015 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#ifndef SkAndroidSDKCanvas_DEFINED -#define SkAndroidSDKCanvas_DEFINED - -#include "SkBitmap.h" -#include "SkCanvas.h" -#include "SkPaint.h" -#include "SkPath.h" -#include "SkRect.h" - -/** SkDrawFilter is likely to be deprecated; this is a proxy - canvas that does the same thing: alter SkPaint fields. - - onDraw*() functions may have their SkPaint modified, and are then - passed on to the same function on proxyTarget. THIS BREAKS CONSTNESS! - - This still suffers one of the same architectural flaws as SkDrawFilter: - TextBlob paints are incomplete when filter is called. -*/ - -class SkAndroidSDKCanvas : public SkCanvas { -public: - SkAndroidSDKCanvas(); - void reset(SkCanvas* newTarget); - -protected: - - // FILTERING - - void onDrawPaint(const SkPaint& paint) override; - void onDrawPoints(PointMode pMode, size_t count, const SkPoint pts[], - const SkPaint& paint) override; - void onDrawOval(const SkRect& r, const SkPaint& paint) override; - void onDrawRect(const SkRect& r, const SkPaint& paint) override; - void onDrawRRect(const SkRRect& r, const SkPaint& paint) override; - void onDrawPath(const SkPath& path, const SkPaint& paint) override; - void onDrawBitmap(const SkBitmap& bitmap, SkScalar left, SkScalar top, - const SkPaint* paint) override; - void onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst, - const SkPaint* paint, SkCanvas::SrcRectConstraint) override; - void onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, - const SkRect& dst, const SkPaint* paint) override; - void onDrawSprite(const SkBitmap& bitmap, int left, int top, - const SkPaint* paint) override; - void onDrawVertices(VertexMode vMode, int vertexCount, const SkPoint vertices[], - const SkPoint texs[], const SkColor colors[], SkXfermode* xMode, - const uint16_t indices[], int indexCount, - const SkPaint& paint) override; - - void onDrawDRRect(const SkRRect& outer, const SkRRect& inner, - const SkPaint& paint) override; - - void onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y, - const SkPaint& paint) override; - void onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[], - const SkPaint& paint) override; - void onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[], - SkScalar constY, const SkPaint& paint) override; - void onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path, - const SkMatrix* matrix, const SkPaint& paint) override; - void onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y, - const SkPaint& paint) override; - - void onDrawPatch(const SkPoint cubics[12], const SkColor colors[4], - const SkPoint texCoords[4], SkXfermode* xmode, - const SkPaint& paint) override; - - void onDrawImage(const SkImage*, SkScalar, SkScalar, const SkPaint*) override; - void onDrawImageRect(const SkImage*, const SkRect*, const SkRect&, const SkPaint*, - SrcRectConstraint) override; - void onDrawPicture(const SkPicture*, const SkMatrix*, const SkPaint*); - void onDrawAtlas(const SkImage*, const SkRSXform[], const SkRect[], - const SkColor[], int count, SkXfermode::Mode, - const SkRect* cull, const SkPaint*) override; - void onDrawImageNine(const SkImage*, const SkIRect& center, - const SkRect& dst, const SkPaint*) override; - - // PASS THROUGH - - void onDrawDrawable(SkDrawable*, const SkMatrix*) override; - SkISize getBaseLayerSize() const override; - bool getClipBounds(SkRect*) const override; - bool getClipDeviceBounds(SkIRect*) const override; - bool isClipEmpty() const override; - bool isClipRect() const override; - SkSurface* onNewSurface(const SkImageInfo&, const SkSurfaceProps&) override; - bool onPeekPixels(SkPixmap*) override; - bool onAccessTopLayerPixels(SkPixmap*) override; - void willSave() override; - SaveLayerStrategy willSaveLayer(const SkRect*, const SkPaint*, SaveFlags) override; - void willRestore() override; - void didRestore() override; - void didConcat(const SkMatrix&) override; - void didSetMatrix(const SkMatrix&) override; - void onClipRect(const SkRect&, SkRegion::Op, ClipEdgeStyle) override; - void onClipRRect(const SkRRect&, SkRegion::Op, ClipEdgeStyle) override; - void onClipPath(const SkPath&, SkRegion::Op, ClipEdgeStyle) override; - void onClipRegion(const SkRegion&, SkRegion::Op) override; - void onDiscard() override; - -protected: - SkCanvas* fProxyTarget; -}; - -#endif // SkAndroidSDKCanvas_DEFINED - diff --git a/gfx/skia/skia/src/utils/debugger/SkDebugCanvas.cpp b/gfx/skia/skia/src/utils/debugger/SkDebugCanvas.cpp index f163cc5470..63739aee81 100644 --- a/gfx/skia/skia/src/utils/debugger/SkDebugCanvas.cpp +++ b/gfx/skia/skia/src/utils/debugger/SkDebugCanvas.cpp @@ -69,15 +69,18 @@ public: , fFilterQuality(quality) {} protected: - void onFilterPaint(SkPaint* paint, Type) const override { - if (nullptr != fOverdrawXfermode.get()) { - paint->setAntiAlias(false); - paint->setXfermode(fOverdrawXfermode.get()); - } + bool onFilter(SkTCopyOnFirstWrite* paint, Type) const override { + if (*paint) { + if (nullptr != fOverdrawXfermode.get()) { + paint->writable()->setAntiAlias(false); + paint->writable()->setXfermode(fOverdrawXfermode.get()); + } - if (fOverrideFilterQuality) { - paint->setFilterQuality(fFilterQuality); + if (fOverrideFilterQuality) { + paint->writable()->setFilterQuality(fFilterQuality); + } } + return true; } void onDrawPicture(const SkPicture* picture, @@ -484,10 +487,6 @@ void SkDebugCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner, this->addDrawCommand(new SkDrawDRRectCommand(outer, inner, paint)); } -void SkDebugCanvas::onDrawSprite(const SkBitmap& bitmap, int left, int top, const SkPaint* paint) { - this->addDrawCommand(new SkDrawSpriteCommand(bitmap, left, top, paint)); -} - void SkDebugCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y, const SkPaint& paint) { this->addDrawCommand(new SkDrawTextCommand(text, byteLength, x, y, paint)); @@ -528,10 +527,9 @@ void SkDebugCanvas::willSave() { this->INHERITED::willSave(); } -SkCanvas::SaveLayerStrategy SkDebugCanvas::willSaveLayer(const SkRect* bounds, const SkPaint* paint, - SaveFlags flags) { - this->addDrawCommand(new SkSaveLayerCommand(bounds, paint, flags)); - this->INHERITED::willSaveLayer(bounds, paint, flags); +SkCanvas::SaveLayerStrategy SkDebugCanvas::getSaveLayerStrategy(const SaveLayerRec& rec) { + this->addDrawCommand(new SkSaveLayerCommand(rec)); + (void)this->INHERITED::getSaveLayerStrategy(rec); // No need for a full layer. return kNoLayer_SaveLayerStrategy; } diff --git a/gfx/skia/skia/src/utils/debugger/SkDebugCanvas.h b/gfx/skia/skia/src/utils/debugger/SkDebugCanvas.h index 612ee5756f..217b52e3ce 100644 --- a/gfx/skia/skia/src/utils/debugger/SkDebugCanvas.h +++ b/gfx/skia/skia/src/utils/debugger/SkDebugCanvas.h @@ -165,7 +165,7 @@ public: protected: void willSave() override; - SaveLayerStrategy willSaveLayer(const SkRect*, const SkPaint*, SaveFlags) override; + SaveLayerStrategy getSaveLayerStrategy(const SaveLayerRec&) override; void willRestore() override; void didConcat(const SkMatrix&) override; @@ -205,7 +205,6 @@ protected: const SkPaint*, SrcRectConstraint) override; void onDrawBitmapNine(const SkBitmap&, const SkIRect& center, const SkRect& dst, const SkPaint*) override; - void onDrawSprite(const SkBitmap&, int left, int top, const SkPaint*) override; void onClipRect(const SkRect&, SkRegion::Op, ClipEdgeStyle) override; void onClipRRect(const SkRRect&, SkRegion::Op, ClipEdgeStyle) override; void onClipPath(const SkPath&, SkRegion::Op, ClipEdgeStyle) override; diff --git a/gfx/skia/skia/src/utils/debugger/SkDrawCommand.cpp b/gfx/skia/skia/src/utils/debugger/SkDrawCommand.cpp index 66f0c14f2d..17c59e67a6 100644 --- a/gfx/skia/skia/src/utils/debugger/SkDrawCommand.cpp +++ b/gfx/skia/skia/src/utils/debugger/SkDrawCommand.cpp @@ -11,6 +11,7 @@ #include "SkObjectParser.h" #include "SkPicture.h" #include "SkTextBlob.h" +#include "SkTextBlobRunIterator.h" // TODO(chudy): Refactor into non subclass model. @@ -47,7 +48,6 @@ const char* SkDrawCommand::GetCommandString(OpType type) { case kDrawPosTextH_OpType: return "DrawPosTextH"; case kDrawRect_OpType: return "DrawRect"; case kDrawRRect_OpType: return "DrawRRect"; - case kDrawSprite_OpType: return "DrawSprite"; case kDrawText_OpType: return "DrawText"; case kDrawTextBlob_OpType: return "DrawTextBlob"; case kDrawTextOnPath_OpType: return "DrawTextOnPath"; @@ -675,21 +675,49 @@ void SkDrawPosTextHCommand::execute(SkCanvas* canvas) const { canvas->drawPosTextH(fText, fByteLength, fXpos, fConstY, fPaint); } +static const char* gPositioningLabels[] = { + "kDefault_Positioning", + "kHorizontal_Positioning", + "kFull_Positioning", +}; + SkDrawTextBlobCommand::SkDrawTextBlobCommand(const SkTextBlob* blob, SkScalar x, SkScalar y, const SkPaint& paint) : INHERITED(kDrawTextBlob_OpType) - , fBlob(blob) + , fBlob(SkRef(blob)) , fXPos(x) , fYPos(y) , fPaint(paint) { - blob->ref(); - - // FIXME: push blob info + SkAutoTDelete runsStr(new SkString); fInfo.push(SkObjectParser::ScalarToString(x, "XPOS: ")); fInfo.push(SkObjectParser::ScalarToString(y, "YPOS: ")); fInfo.push(SkObjectParser::RectToString(fBlob->bounds(), "Bounds: ")); + fInfo.push(runsStr); fInfo.push(SkObjectParser::PaintToString(paint)); + + unsigned runs = 0; + SkPaint runPaint(paint); + SkTextBlobRunIterator iter(blob); + while (!iter.done()) { + SkAutoTDelete tmpStr(new SkString); + tmpStr->printf("==== Run [%d] ====", runs++); + fInfo.push(tmpStr.release()); + + fInfo.push(SkObjectParser::IntToString(iter.glyphCount(), "GlyphCount: ")); + tmpStr.reset(new SkString("GlyphPositioning: ")); + tmpStr->append(gPositioningLabels[iter.positioning()]); + fInfo.push(tmpStr.release()); + + iter.applyFontToPaint(&runPaint); + fInfo.push(SkObjectParser::PaintToString(runPaint)); + + iter.next(); + } + + runsStr->printf("Runs: %d", runs); + // runStr is owned by fInfo at this point. + runsStr.release(); } void SkDrawTextBlobCommand::execute(SkCanvas* canvas) const { @@ -780,36 +808,6 @@ bool SkDrawDRRectCommand::render(SkCanvas* canvas) const { return true; } -SkDrawSpriteCommand::SkDrawSpriteCommand(const SkBitmap& bitmap, int left, int top, - const SkPaint* paint) - : INHERITED(kDrawSprite_OpType) { - fBitmap = bitmap; - fLeft = left; - fTop = top; - if (paint) { - fPaint = *paint; - fPaintPtr = &fPaint; - } else { - fPaintPtr = nullptr; - } - - fInfo.push(SkObjectParser::BitmapToString(bitmap)); - fInfo.push(SkObjectParser::IntToString(left, "Left: ")); - fInfo.push(SkObjectParser::IntToString(top, "Top: ")); - if (paint) { - fInfo.push(SkObjectParser::PaintToString(*paint)); - } -} - -void SkDrawSpriteCommand::execute(SkCanvas* canvas) const { - canvas->drawSprite(fBitmap, fLeft, fTop, fPaintPtr); -} - -bool SkDrawSpriteCommand::render(SkCanvas* canvas) const { - render_bitmap(canvas, fBitmap); - return true; -} - SkDrawTextCommand::SkDrawTextCommand(const void* text, size_t byteLength, SkScalar x, SkScalar y, const SkPaint& paint) : INHERITED(kDrawText_OpType) { @@ -937,36 +935,35 @@ void SkSaveCommand::execute(SkCanvas* canvas) const { canvas->save(); } -SkSaveLayerCommand::SkSaveLayerCommand(const SkRect* bounds, const SkPaint* paint, - SkCanvas::SaveFlags flags) +SkSaveLayerCommand::SkSaveLayerCommand(const SkCanvas::SaveLayerRec& rec) : INHERITED(kSaveLayer_OpType) { - if (bounds) { - fBounds = *bounds; + if (rec.fBounds) { + fBounds = *rec.fBounds; } else { fBounds.setEmpty(); } - if (paint) { - fPaint = *paint; + if (rec.fPaint) { + fPaint = *rec.fPaint; fPaintPtr = &fPaint; } else { fPaintPtr = nullptr; } - fFlags = flags; + fSaveLayerFlags = rec.fSaveLayerFlags; - if (bounds) { - fInfo.push(SkObjectParser::RectToString(*bounds, "Bounds: ")); + if (rec.fBounds) { + fInfo.push(SkObjectParser::RectToString(*rec.fBounds, "Bounds: ")); } - if (paint) { - fInfo.push(SkObjectParser::PaintToString(*paint)); + if (rec.fPaint) { + fInfo.push(SkObjectParser::PaintToString(*rec.fPaint)); } - fInfo.push(SkObjectParser::SaveFlagsToString(flags)); + fInfo.push(SkObjectParser::SaveLayerFlagsToString(fSaveLayerFlags)); } void SkSaveLayerCommand::execute(SkCanvas* canvas) const { - canvas->saveLayer(fBounds.isEmpty() ? nullptr : &fBounds, - fPaintPtr, - fFlags); + canvas->saveLayer(SkCanvas::SaveLayerRec(fBounds.isEmpty() ? nullptr : &fBounds, + fPaintPtr, + fSaveLayerFlags)); } void SkSaveLayerCommand::vizExecute(SkCanvas* canvas) const { diff --git a/gfx/skia/skia/src/utils/debugger/SkDrawCommand.h b/gfx/skia/skia/src/utils/debugger/SkDrawCommand.h index c7e5f008a4..f67df92de7 100644 --- a/gfx/skia/skia/src/utils/debugger/SkDrawCommand.h +++ b/gfx/skia/skia/src/utils/debugger/SkDrawCommand.h @@ -41,7 +41,6 @@ public: kDrawPosTextH_OpType, kDrawRect_OpType, kDrawRRect_OpType, - kDrawSprite_OpType, kDrawText_OpType, kDrawTextBlob_OpType, kDrawTextOnPath_OpType, @@ -518,21 +517,6 @@ private: typedef SkDrawCommand INHERITED; }; -class SkDrawSpriteCommand : public SkDrawCommand { -public: - SkDrawSpriteCommand(const SkBitmap& bitmap, int left, int top, const SkPaint* paint); - void execute(SkCanvas* canvas) const override; - bool render(SkCanvas* canvas) const override; -private: - SkBitmap fBitmap; - int fLeft; - int fTop; - SkPaint fPaint; - SkPaint* fPaintPtr; - - typedef SkDrawCommand INHERITED; -}; - class SkDrawVerticesCommand : public SkDrawCommand { public: SkDrawVerticesCommand(SkCanvas::VertexMode vmode, int vertexCount, @@ -567,8 +551,7 @@ private: class SkSaveLayerCommand : public SkDrawCommand { public: - SkSaveLayerCommand(const SkRect* bounds, const SkPaint* paint, - SkCanvas::SaveFlags flags); + SkSaveLayerCommand(const SkCanvas::SaveLayerRec&); void execute(SkCanvas* canvas) const override; void vizExecute(SkCanvas* canvas) const override; Action action() const override{ return kPushLayer_Action; } @@ -578,12 +561,12 @@ public: const SkPaint* paint() const { return fPaintPtr; } private: - SkRect fBounds; - SkPaint fPaint; - SkPaint* fPaintPtr; - SkCanvas::SaveFlags fFlags; + SkRect fBounds; + SkPaint fPaint; + SkPaint* fPaintPtr; + uint32_t fSaveLayerFlags; - bool fActive; + bool fActive; typedef SkDrawCommand INHERITED; }; diff --git a/gfx/skia/skia/src/utils/debugger/SkObjectParser.cpp b/gfx/skia/skia/src/utils/debugger/SkObjectParser.cpp index 3dbc03901f..6d71a38b7b 100644 --- a/gfx/skia/skia/src/utils/debugger/SkObjectParser.cpp +++ b/gfx/skia/skia/src/utils/debugger/SkObjectParser.cpp @@ -338,16 +338,13 @@ SkString* SkObjectParser::RegionToString(const SkRegion& region) { return mRegion; } -SkString* SkObjectParser::SaveFlagsToString(SkCanvas::SaveFlags flags) { +SkString* SkObjectParser::SaveLayerFlagsToString(SkCanvas::SaveLayerFlags saveLayerFlags) { SkString* mFlags = new SkString("SkCanvas::SaveFlags: "); - if (flags & SkCanvas::kHasAlphaLayer_SaveFlag) { - mFlags->append("kHasAlphaLayer_SaveFlag "); + if (saveLayerFlags & SkCanvas::kIsOpaque_SaveLayerFlag) { + mFlags->append("kIsOpaque_SaveLayerFlag "); } - if (flags & SkCanvas::kFullColorLayer_SaveFlag) { - mFlags->append("kFullColorLayer_SaveFlag "); - } - if (flags & SkCanvas::kClipToLayer_SaveFlag) { - mFlags->append("kClipToLayer_SaveFlag "); + if (saveLayerFlags & SkCanvas::kPreserveLCDText_SaveLayerFlag) { + mFlags->append("kPreserveLCDText_SaveLayerFlag "); } return mFlags; } diff --git a/gfx/skia/skia/src/utils/debugger/SkObjectParser.h b/gfx/skia/skia/src/utils/debugger/SkObjectParser.h index 4c04935257..9bdfad5e79 100644 --- a/gfx/skia/skia/src/utils/debugger/SkObjectParser.h +++ b/gfx/skia/skia/src/utils/debugger/SkObjectParser.h @@ -110,10 +110,10 @@ public: static SkString* RegionToString(const SkRegion& region); /** - Returns a string representation of the SkCanvas::SaveFlags enum. - @param flags SkCanvas::SaveFlags enum + Returns a string representation of the SkCanvas::SaveLayerFlags enum. + @param flags SkCanvas::SaveLayerFlags enum */ - static SkString* SaveFlagsToString(SkCanvas::SaveFlags flags); + static SkString* SaveLayerFlagsToString(uint32_t saveLayerFlags); /** Returns a string representation of an SkScalar with the text parameter diff --git a/gfx/skia/skia/src/views/SkView.cpp b/gfx/skia/skia/src/views/SkView.cpp index ec75d610aa..9e8182b16a 100644 --- a/gfx/skia/skia/src/views/SkView.cpp +++ b/gfx/skia/skia/src/views/SkView.cpp @@ -1,17 +1,21 @@ - /* * Copyright 2011 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ + #include "SkView.h" #include "SkCanvas.h" +static inline uint32_t SkSetClearShift(uint32_t bits, bool cond, unsigned shift) { + SkASSERT((int)cond == 0 || (int)cond == 1); + return (bits & ~(1 << shift)) | ((int)cond << shift); +} + //////////////////////////////////////////////////////////////////////// -SkView::SkView(uint32_t flags) : fFlags(SkToU8(flags)) -{ +SkView::SkView(uint32_t flags) : fFlags(SkToU8(flags)) { fWidth = fHeight = 0; fLoc.set(0, 0); fParent = fFirstChild = fNextSibling = fPrevSibling = nullptr; @@ -19,13 +23,11 @@ SkView::SkView(uint32_t flags) : fFlags(SkToU8(flags)) fContainsFocus = 0; } -SkView::~SkView() -{ +SkView::~SkView() { this->detachAllChildren(); } -void SkView::setFlags(uint32_t flags) -{ +void SkView::setFlags(uint32_t flags) { SkASSERT((flags & ~kAllFlagMasks) == 0); uint32_t diff = fFlags ^ flags; @@ -35,24 +37,20 @@ void SkView::setFlags(uint32_t flags) fFlags = SkToU8(flags); - if (diff & kVisible_Mask) - { + if (diff & kVisible_Mask) { this->inval(nullptr); } } -void SkView::setVisibleP(bool pred) -{ +void SkView::setVisibleP(bool pred) { this->setFlags(SkSetClearShift(fFlags, pred, kVisible_Shift)); } -void SkView::setEnabledP(bool pred) -{ +void SkView::setEnabledP(bool pred) { this->setFlags(SkSetClearShift(fFlags, pred, kEnabled_Shift)); } -void SkView::setFocusableP(bool pred) -{ +void SkView::setFocusableP(bool pred) { this->setFlags(SkSetClearShift(fFlags, pred, kFocusable_Shift)); } @@ -60,8 +58,7 @@ void SkView::setClipToBounds(bool pred) { this->setFlags(SkSetClearShift(fFlags, !pred, kNoClip_Shift)); } -void SkView::setSize(SkScalar width, SkScalar height) -{ +void SkView::setSize(SkScalar width, SkScalar height) { width = SkMaxScalar(0, width); height = SkMaxScalar(0, height); @@ -76,38 +73,31 @@ void SkView::setSize(SkScalar width, SkScalar height) } } -void SkView::setLoc(SkScalar x, SkScalar y) -{ - if (fLoc.fX != x || fLoc.fY != y) - { +void SkView::setLoc(SkScalar x, SkScalar y) { + if (fLoc.fX != x || fLoc.fY != y) { this->inval(nullptr); fLoc.set(x, y); this->inval(nullptr); } } -void SkView::offset(SkScalar dx, SkScalar dy) -{ +void SkView::offset(SkScalar dx, SkScalar dy) { if (dx || dy) this->setLoc(fLoc.fX + dx, fLoc.fY + dy); } -void SkView::setLocalMatrix(const SkMatrix& matrix) -{ +void SkView::setLocalMatrix(const SkMatrix& matrix) { this->inval(nullptr); fMatrix = matrix; this->inval(nullptr); } -void SkView::draw(SkCanvas* canvas) -{ - if (fWidth && fHeight && this->isVisible()) - { +void SkView::draw(SkCanvas* canvas) { + if (fWidth && fHeight && this->isVisible()) { SkRect r; r.set(fLoc.fX, fLoc.fY, fLoc.fX + fWidth, fLoc.fY + fHeight); - if (this->isClipToBounds() && - canvas->quickReject(r)) { - return; + if (this->isClipToBounds() && canvas->quickReject(r)) { + return; } SkAutoCanvasRestore as(canvas, true); @@ -178,84 +168,79 @@ void SkView::inval(SkRect* rect) { //////////////////////////////////////////////////////////////////////////// -bool SkView::setFocusView(SkView* fv) -{ +bool SkView::setFocusView(SkView* fv) { SkView* view = this; do { - if (view->onSetFocusView(fv)) + if (view->onSetFocusView(fv)) { return true; + } } while ((view = view->fParent) != nullptr); return false; } -SkView* SkView::getFocusView() const -{ - SkView* focus = nullptr; - const SkView* view = this; +SkView* SkView::getFocusView() const { + SkView* focus = nullptr; + const SkView* view = this; do { - if (view->onGetFocusView(&focus)) + if (view->onGetFocusView(&focus)) { break; + } } while ((view = view->fParent) != nullptr); return focus; } -bool SkView::hasFocus() const -{ +bool SkView::hasFocus() const { return this == this->getFocusView(); } -bool SkView::acceptFocus() -{ +bool SkView::acceptFocus() { return this->isFocusable() && this->setFocusView(this); } /* Try to give focus to this view, or its children */ -SkView* SkView::acceptFocus(FocusDirection dir) -{ - if (dir == kNext_FocusDirection) - { - if (this->acceptFocus()) +SkView* SkView::acceptFocus(FocusDirection dir) { + if (dir == kNext_FocusDirection) { + if (this->acceptFocus()) { return this; - + } B2FIter iter(this); SkView* child, *focus; - while ((child = iter.next()) != nullptr) - if ((focus = child->acceptFocus(dir)) != nullptr) + while ((child = iter.next()) != nullptr) { + if ((focus = child->acceptFocus(dir)) != nullptr) { return focus; - } - else // prev - { + } + } + } else { // prev F2BIter iter(this); SkView* child, *focus; - while ((child = iter.next()) != nullptr) - if ((focus = child->acceptFocus(dir)) != nullptr) + while ((child = iter.next()) != nullptr) { + if ((focus = child->acceptFocus(dir)) != nullptr) { return focus; - - if (this->acceptFocus()) + } + } + if (this->acceptFocus()) { return this; + } } - return nullptr; } -SkView* SkView::moveFocus(FocusDirection dir) -{ +SkView* SkView::moveFocus(FocusDirection dir) { SkView* focus = this->getFocusView(); - if (focus == nullptr) - { // start with the root + if (focus == nullptr) { // start with the root focus = this; - while (focus->fParent) + while (focus->fParent) { focus = focus->fParent; + } } - SkView* child, *parent; + SkView* child, *parent; - if (dir == kNext_FocusDirection) - { + if (dir == kNext_FocusDirection) { parent = focus; child = focus->fFirstChild; if (child) @@ -264,8 +249,7 @@ SkView* SkView::moveFocus(FocusDirection dir) goto NEXT_SIB; do { - while (child != parent->fFirstChild) - { + while (child != parent->fFirstChild) { FIRST_CHILD: if ((focus = child->acceptFocus(dir)) != nullptr) return focus; @@ -275,26 +259,22 @@ SkView* SkView::moveFocus(FocusDirection dir) child = parent->fNextSibling; parent = parent->fParent; } while (parent != nullptr); - } - else // prevfocus - { + } else { // prevfocus parent = focus->fParent; - if (parent == nullptr) // we're the root + if (parent == nullptr) { // we're the root return focus->acceptFocus(dir); - else - { + } else { child = focus; - while (parent) - { - while (child != parent->fFirstChild) - { + while (parent) { + while (child != parent->fFirstChild) { child = child->fPrevSibling; - if ((focus = child->acceptFocus(dir)) != nullptr) + if ((focus = child->acceptFocus(dir)) != nullptr) { return focus; + } } - if (parent->acceptFocus()) + if (parent->acceptFocus()) { return parent; - + } child = parent; parent = parent->fParent; } @@ -303,15 +283,13 @@ SkView* SkView::moveFocus(FocusDirection dir) return nullptr; } -void SkView::onFocusChange(bool gainFocusP) -{ +void SkView::onFocusChange(bool gainFocusP) { this->inval(nullptr); } //////////////////////////////////////////////////////////////////////////// -SkView::Click::Click(SkView* target) -{ +SkView::Click::Click(SkView* target) { SkASSERT(target); fTargetID = target->getSinkID(); fType = nullptr; @@ -319,49 +297,43 @@ SkView::Click::Click(SkView* target) fOwner = nullptr; } -SkView::Click::~Click() -{ +SkView::Click::~Click() { this->resetType(); } -void SkView::Click::resetType() -{ - if (fWeOwnTheType) - { +void SkView::Click::resetType() { + if (fWeOwnTheType) { sk_free(fType); fWeOwnTheType = false; } fType = nullptr; } -bool SkView::Click::isType(const char type[]) const -{ +bool SkView::Click::isType(const char type[]) const { const char* t = fType; - if (type == t) + if (type == t) { return true; - - if (type == nullptr) + } + if (type == nullptr) { type = ""; - if (t == nullptr) + } + if (t == nullptr) { t = ""; + } return !strcmp(t, type); } -void SkView::Click::setType(const char type[]) -{ +void SkView::Click::setType(const char type[]) { this->resetType(); fType = (char*)type; } -void SkView::Click::copyType(const char type[]) -{ - if (fType != type) - { +void SkView::Click::copyType(const char type[]) { + if (fType != type) { this->resetType(); - if (type) - { - size_t len = strlen(type) + 1; + if (type) { + size_t len = strlen(type) + 1; fType = (char*)sk_malloc_throw(len); memcpy(fType, type, len); fWeOwnTheType = true; @@ -400,8 +372,7 @@ SkView::Click* SkView::findClickHandler(SkScalar x, SkScalar y, unsigned modi) { return this->onFindClickHandler(x, y, modi); } -void SkView::DoClickDown(Click* click, int x, int y, unsigned modi) -{ +void SkView::DoClickDown(Click* click, int x, int y, unsigned modi) { SkASSERT(click); SkView* target = (SkView*)SkEventSink::FindSink(click->fTargetID); @@ -424,8 +395,7 @@ void SkView::DoClickDown(Click* click, int x, int y, unsigned modi) target->onClick(click); } -void SkView::DoClickMoved(Click* click, int x, int y, unsigned modi) -{ +void SkView::DoClickMoved(Click* click, int x, int y, unsigned modi) { SkASSERT(click); SkView* target = (SkView*)SkEventSink::FindSink(click->fTargetID); @@ -448,8 +418,7 @@ void SkView::DoClickMoved(Click* click, int x, int y, unsigned modi) target->onClick(click); } -void SkView::DoClickUp(Click* click, int x, int y, unsigned modi) -{ +void SkView::DoClickUp(Click* click, int x, int y, unsigned modi) { SkASSERT(click); SkView* target = (SkView*)SkEventSink::FindSink(click->fTargetID); @@ -637,8 +606,8 @@ void SkView::localToGlobal(SkMatrix* matrix) const { } } } -bool SkView::globalToLocal(SkScalar x, SkScalar y, SkPoint* local) const -{ + +bool SkView::globalToLocal(SkScalar x, SkScalar y, SkPoint* local) const { SkASSERT(this); if (local) { @@ -684,9 +653,11 @@ void SkView::onInflate(const SkDOM& dom, const SkDOM::Node* node) { bool b; uint32_t flags = this->getFlags(); - for (unsigned i = 0; i < SK_ARRAY_COUNT(gFlagNames); i++) - if (dom.findBool(node, gFlagNames[i], &b)) + for (unsigned i = 0; i < SK_ARRAY_COUNT(gFlagNames); i++) { + if (dom.findBool(node, gFlagNames[i], &b)) { flags = SkSetClearShift(flags, b, i); + } + } this->setFlags(flags); } @@ -788,20 +759,19 @@ void SkView::validate() const { } } -static inline void show_if_nonzero(const char name[], SkScalar value) -{ - if (value) +static inline void show_if_nonzero(const char name[], SkScalar value) { + if (value) { SkDebugf("%s=\"%g\"", name, value/65536.); + } } -static void tab(int level) -{ - for (int i = 0; i < level; i++) +static void tab(int level) { + for (int i = 0; i < level; i++) { SkDebugf(" "); + } } -static void dumpview(const SkView* view, int level, bool recurse) -{ +static void dumpview(const SkView* view, int level, bool recurse) { tab(level); SkDebugf("width()); show_if_nonzero(" height", view->height()); - if (recurse) - { + if (recurse) { SkView::B2FIter iter(view); SkView* child; bool noChildren = true; - while ((child = iter.next()) != nullptr) - { - if (noChildren) + while ((child = iter.next()) != nullptr) { + if (noChildren) { SkDebugf(">\n"); + } noChildren = false; dumpview(child, level + 1, true); } - if (!noChildren) - { + if (!noChildren) { tab(level); SkDebugf("\n"); - } - else + } else { goto ONELINER; - } - else - { + } + } else { ONELINER: SkDebugf(" />\n"); } } -void SkView::dump(bool recurse) const -{ +void SkView::dump(bool recurse) const { dumpview(this, 0, recurse); } diff --git a/gfx/skia/skia/src/views/sdl/SkOSWindow_SDL.cpp b/gfx/skia/skia/src/views/sdl/SkOSWindow_SDL.cpp index 45beaee53e..c39a4fdfef 100644 --- a/gfx/skia/skia/src/views/sdl/SkOSWindow_SDL.cpp +++ b/gfx/skia/skia/src/views/sdl/SkOSWindow_SDL.cpp @@ -15,72 +15,39 @@ #include #endif -const int SCREEN_WIDTH = 640; -const int SCREEN_HEIGHT = 480; +const int kInitialWindowWidth = 640; +const int kInitialWindowHeight = 480; +static SkOSWindow* gCurrentWindow; -static void handle_error() { +static void report_sdl_error(const char* failure) { const char* error = SDL_GetError(); - SkDebugf("SDL Error: %s\n", error); + SkASSERT(error); // Called only to check SDL error. + SkDebugf("%s SDL Error: %s.\n", failure, error); SDL_ClearError(); } +SkOSWindow::SkOSWindow(void*) + : fWindow(nullptr) + , fGLContext(nullptr) + , fWindowMSAASampleCount(0) { -SkOSWindow::SkOSWindow(void* screen) : fQuit(false) , fGLContext(nullptr) { -#if defined(SK_BUILD_FOR_ANDROID) - // TODO we should try and get a 3.0 context first - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); - fWindowFlags = SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | - SDL_WINDOW_BORDERLESS | SDL_WINDOW_FULLSCREEN_DESKTOP | - SDL_WINDOW_ALLOW_HIGHDPI; -#else - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); - SDL_StartTextInput(); + SkASSERT(!gCurrentWindow); + gCurrentWindow = this; - fWindowFlags = SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE; -#endif - SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5); - SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 5); - SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5); - SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); - SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); - SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8); - - SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1); - - if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) != 0) { - handle_error(); - return; - } - - SDL_DisplayMode dm; - if (SDL_GetDesktopDisplayMode(0, &dm) != 0) { - handle_error(); - return; - } - - fWindow = SDL_CreateWindow("SDL Window", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, - dm.w, dm.h, fWindowFlags); - - if (!fWindow) { - handle_error(); - return; - } - this->resize(dm.w, dm.h); + this->createWindow(0); } SkOSWindow::~SkOSWindow() { - if (fGLContext) { - SDL_GL_DeleteContext(fGLContext); + this->destroyWindow(); + gCurrentWindow = nullptr; +} + +SkOSWindow* SkOSWindow::GetInstanceForWindowID(Uint32 windowID) { + if (gCurrentWindow && + gCurrentWindow->fWindow && + SDL_GetWindowID(gCurrentWindow->fWindow) == windowID) { + return gCurrentWindow; } - - //Destroy window - SDL_DestroyWindow(fWindow); - - //Quit SDL subsystems - SDL_Quit(); + return nullptr; } void SkOSWindow::detach() { @@ -88,67 +55,67 @@ void SkOSWindow::detach() { SDL_GL_DeleteContext(fGLContext); fGLContext = nullptr; } - -#if defined(SK_BUILD_FOR_ANDROID) - if (fWindow) { - // Destroy window - // Not totally sure why, but we have to do this or swapbuffers will hang - SDL_DestroyWindow(fWindow); - fWindow = nullptr; - } -#endif } bool SkOSWindow::attach(SkBackEndTypes attachType, int msaaSampleCount, AttachmentInfo* info) { + this->createWindow(msaaSampleCount); if (!fWindow) { - fWindow = SDL_CreateWindow("SDL Window", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, - SCREEN_WIDTH, SCREEN_HEIGHT, - fWindowFlags); + return false; + } + if (!fGLContext) { + fGLContext = SDL_GL_CreateContext(fWindow); + if (!fGLContext) { + report_sdl_error("Failed to create SDL GL context."); + return false; + } + glClearColor(0, 0, 0, 0); + glClearStencil(0); + glStencilMask(0xffffffff); + glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); } - if (msaaSampleCount > 0) { - SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1); - SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, msaaSampleCount); + if (SDL_GL_MakeCurrent(fWindow, fGLContext) != 0) { + report_sdl_error("Failed to make SDL GL context current."); + this->detach(); + return false; } info->fSampleCount = msaaSampleCount; info->fStencilBits = 8; - fGLContext = SDL_GL_CreateContext(fWindow); - if (!fGLContext) { - handle_error(); - return false; - } - - int success = SDL_GL_MakeCurrent(fWindow, fGLContext); - if (success != 0) { - handle_error(); - return false; - } - - glViewport(0, 0, SkScalarFloorToInt(this->width()), SkScalarFloorToInt(this->height())); - glClearColor(1, 1, 1, 1); - glClearStencil(0); - glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); - + glViewport(0, 0, SkScalarRoundToInt(this->width()), SkScalarRoundToInt(this->height())); return true; } void SkOSWindow::present() { + if (!fWindow) { + return; + } SDL_GL_SwapWindow(fWindow); } bool SkOSWindow::makeFullscreen() { - SDL_SetWindowFullscreen(fWindow, SDL_WINDOW_FULLSCREEN); + if (!fWindow) { + return false; + } + SDL_SetWindowFullscreen(fWindow, SDL_WINDOW_FULLSCREEN_DESKTOP); return true; } void SkOSWindow::setVsync(bool vsync) { + if (!fWindow) { + return; + } SDL_GL_SetSwapInterval(vsync ? 1 : 0); } void SkOSWindow::closeWindow() { - fQuit = true; + this->destroyWindow(); + + // Currently closing the window causes the app to quit. + SDL_Event event; + event.type = SDL_QUIT; + SDL_PushEvent(&event); } static SkKey convert_sdlkey_to_skkey(SDL_Keycode src) { @@ -194,59 +161,200 @@ static SkKey convert_sdlkey_to_skkey(SDL_Keycode src) { } } -void SkOSWindow::handleEvents() { - SkEvent::ServiceQueueTimer(); - SkEvent::ProcessEvent(); +void SkOSWindow::createWindow(int msaaSampleCount) { + if (fWindowMSAASampleCount != msaaSampleCount) { + this->destroyWindow(); + } + if (fWindow) { + return; + } + uint32_t windowFlags = +#if defined(SK_BUILD_FOR_ANDROID) + SDL_WINDOW_BORDERLESS | SDL_WINDOW_FULLSCREEN_DESKTOP | + SDL_WINDOW_ALLOW_HIGHDPI | +#endif + SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE; - SDL_Event event; - while(SDL_PollEvent(&event)) { - switch (event.type) { - case SDL_MOUSEMOTION: - if (event.motion.state == SDL_PRESSED) { - this->handleClick(event.motion.x, event.motion.y, - SkView::Click::kMoved_State, nullptr); - } - break; - case SDL_MOUSEBUTTONDOWN: - case SDL_MOUSEBUTTONUP: - this->handleClick(event.button.x, event.button.y, - event.button.state == SDL_PRESSED ? - SkView::Click::kDown_State : - SkView::Click::kUp_State, nullptr); - break; - case SDL_KEYDOWN: { - SDL_Keycode key = event.key.keysym.sym; - SkKey sk = convert_sdlkey_to_skkey(key); - if (kNONE_SkKey != sk) { - if (event.key.state == SDL_PRESSED) { - this->handleKey(sk); - } else { - this->handleKeyUp(sk); - } - } else if (key == SDLK_ESCAPE) { - fQuit = true; - } - break; - } - case SDL_TEXTINPUT: { - size_t len = strlen(event.text.text); - for (size_t i = 0; i < len; i++) { - this->handleChar((SkUnichar)event.text.text[i]); - } - break; - } - case SDL_QUIT: - fQuit = true; - break; - default: - break; + // GL settings are part of SDL_WINDOW_OPENGL window creation arguments. +#if defined(SK_BUILD_FOR_ANDROID) + // TODO we should try and get a 3.0 context first + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); +#else + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); +#endif + SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); + SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); + SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8); +#if defined(SK_BUILD_FOR_UNIX) + // Apparently MSAA request matches "slow caveat". Make SDL not set anything for caveat for MSAA + // by setting -1 for ACCELERATED_VISUAL. For non-MSAA, set ACCELERATED_VISUAL to 1 just for + // compatiblity with other platforms. + SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, msaaSampleCount > 0 ? -1 : 1); +#else + SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1); +#endif + SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, msaaSampleCount > 0 ? 1 : 0); + SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, msaaSampleCount); + + // This is an approximation for sizing purposes. + bool isInitialWindow = this->width() == 0 && this->height() == 0; + SkScalar windowWidth = isInitialWindow ? kInitialWindowWidth : this->width(); + SkScalar windowHeight = isInitialWindow ? kInitialWindowHeight : this->height(); + + fWindow = SDL_CreateWindow(this->getTitle(), SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, + windowWidth, windowHeight, windowFlags); + if (!fWindow) { + report_sdl_error("Failed to create SDL window."); + return; + } + fWindowMSAASampleCount = msaaSampleCount; +} + +void SkOSWindow::destroyWindow() { + this->detach(); + if (fWindow) { + SDL_DestroyWindow(fWindow); + fWindow = nullptr; + fWindowMSAASampleCount = 0; + } +} + +bool SkOSWindow::HasDirtyWindows() { + if (gCurrentWindow && gCurrentWindow->fWindow) { + return gCurrentWindow->isDirty(); + } + return false; +} + +void SkOSWindow::UpdateDirtyWindows() { + if (gCurrentWindow && gCurrentWindow->fWindow) { + if (gCurrentWindow->isDirty()) { + // This will call present. + gCurrentWindow->update(nullptr); } } } +void SkOSWindow::HandleEvent(const SDL_Event& event) { + switch (event.type) { + case SDL_MOUSEMOTION: + if (SkOSWindow* window = GetInstanceForWindowID(event.motion.windowID)) { + if (event.motion.state == SDL_PRESSED) { + window->handleClick(event.motion.x, event.motion.y, + SkView::Click::kMoved_State, nullptr); + } + } + break; + case SDL_MOUSEBUTTONDOWN: + case SDL_MOUSEBUTTONUP: + if (SkOSWindow* window = GetInstanceForWindowID(event.button.windowID)) { + window->handleClick(event.button.x, event.button.y, + event.button.state == SDL_PRESSED ? + SkView::Click::kDown_State : + SkView::Click::kUp_State, nullptr); + } + break; + case SDL_KEYDOWN: + if (SkOSWindow* window = GetInstanceForWindowID(event.key.windowID)) { + SDL_Keycode key = event.key.keysym.sym; + SkKey sk = convert_sdlkey_to_skkey(key); + if (kNONE_SkKey != sk) { + if (event.key.state == SDL_PRESSED) { + window->handleKey(sk); + } else { + window->handleKeyUp(sk); + } + } else if (key == SDLK_ESCAPE) { + window->closeWindow(); + } + } + break; + case SDL_TEXTINPUT: + if (SkOSWindow* window = GetInstanceForWindowID(event.text.windowID)) { + size_t len = strlen(event.text.text); + for (size_t i = 0; i < len; i++) { + window->handleChar((SkUnichar)event.text.text[i]); + } + } + break; + case SDL_WINDOWEVENT: + switch (event.window.event) { + case SDL_WINDOWEVENT_SHOWN: + // For initialization purposes, we resize upon first show. + // Fallthrough. + case SDL_WINDOWEVENT_SIZE_CHANGED: + if (SkOSWindow* window = GetInstanceForWindowID(event.window.windowID)) { + int w = 0; + int h = 0; + SDL_GetWindowSize(window->fWindow, &w, &h); + window->resize(w, h); + } + break; + case SDL_WINDOWEVENT_FOCUS_GAINED: + if (GetInstanceForWindowID(event.text.windowID)) { + SDL_StartTextInput(); + } + break; + default: + break; + } + break; + default: + break; + } +} + +SkMSec gTimerDelay; + +void SkOSWindow::RunEventLoop() { + for (;;) { + SkEvent::ServiceQueueTimer(); + bool hasMoreSkEvents = SkEvent::ProcessEvent(); + + SDL_Event event; + bool hasSDLEvents = SDL_PollEvent(&event) == 1; + + // Invalidations do not post to event loop, rather we just go through the + // windows for each event loop iteration. + bool hasDirtyWindows = HasDirtyWindows(); + + if (!hasSDLEvents && !hasMoreSkEvents && !hasDirtyWindows) { + // If there is no SDL events, SkOSWindow updates or SkEvents + // to be done, wait for the SDL events. + if (gTimerDelay > 0) { + hasSDLEvents = SDL_WaitEventTimeout(&event, gTimerDelay) == 1; + } else { + hasSDLEvents = SDL_WaitEvent(&event) == 1; + } + } + while (hasSDLEvents) { + if (event.type == SDL_QUIT) { + return; + } + HandleEvent(event); + hasSDLEvents = SDL_PollEvent(&event); + } + UpdateDirtyWindows(); + } +} void SkOSWindow::onSetTitle(const char title[]) { - SDL_SetWindowTitle(fWindow, title); + if (!fWindow) { + return; + } + this->updateWindowTitle(); +} + +void SkOSWindow::updateWindowTitle() { + SDL_SetWindowTitle(fWindow, this->getTitle()); } /////////////////////////////////////////////////////////////////////////////////////// @@ -255,13 +363,7 @@ void SkEvent::SignalNonEmptyQueue() { } void SkEvent::SignalQueueTimer(SkMSec delay) { - // just need to record the delay time. We handle waking up for it in -} - -void SkOSWindow::onHandleInval(const SkIRect& rect) { -} - -void SkOSWindow::onPDFSaved(const char title[], const char desc[], const char path[]) { + gTimerDelay = delay; } ////////////////////////////////////////////////////////////////////////////////////////////// @@ -275,17 +377,24 @@ int SDL_main(int argc, char** argv) { #else int main(int argc, char** argv) { #endif + if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) != 0) { + report_sdl_error("Failed to init SDL."); + return -1; + } + + application_init(); + SkOSWindow* window = create_sk_window(nullptr, argc, argv); // drain any events that occurred before |window| was assigned. while (SkEvent::ProcessEvent()); - // Start normal Skia sequence - application_init(); - - window->loop(); + SkOSWindow::RunEventLoop(); delete window; application_term(); + + SDL_Quit(); + return 0; } diff --git a/gfx/skia/skia/src/xml/SkDOM.cpp b/gfx/skia/skia/src/xml/SkDOM.cpp index 68e2938d9a..0f0b614f11 100644 --- a/gfx/skia/skia/src/xml/SkDOM.cpp +++ b/gfx/skia/skia/src/xml/SkDOM.cpp @@ -240,7 +240,7 @@ protected: } *fParentStack.push() = node; - memcpy(node->attrs(), fAttrs.begin(), attrCount * sizeof(SkDOM::Attr)); + sk_careful_memcpy(node->attrs(), fAttrs.begin(), attrCount * sizeof(SkDOM::Attr)); fAttrs.reset(); } diff --git a/gfx/src/nsDeviceContext.cpp b/gfx/src/nsDeviceContext.cpp index 913a9a9a43..f21847caef 100644 --- a/gfx/src/nsDeviceContext.cpp +++ b/gfx/src/nsDeviceContext.cpp @@ -678,38 +678,13 @@ nsDeviceContext::CalcPrintingSize() return (mWidth > 0 && mHeight > 0); } - bool inPoints = true; - - gfxSize size(0, 0); - switch (mPrintingSurface->GetType()) { -#ifdef XP_WIN - case gfxSurfaceType::Win32Printing: - { - inPoints = false; - HDC dc = reinterpret_cast(mPrintingSurface.get())->GetDC(); - if (!dc) - dc = GetDC((HWND)mWidget->GetNativeData(NS_NATIVE_WIDGET)); - size.width = NSFloatPixelsToAppUnits(::GetDeviceCaps(dc, HORZRES)/mPrintingScale, AppUnitsPerDevPixel()); - size.height = NSFloatPixelsToAppUnits(::GetDeviceCaps(dc, VERTRES)/mPrintingScale, AppUnitsPerDevPixel()); - mDepth = (uint32_t)::GetDeviceCaps(dc, BITSPIXEL); - if (dc != reinterpret_cast(mPrintingSurface.get())->GetDC()) - ReleaseDC((HWND)mWidget->GetNativeData(NS_NATIVE_WIDGET), dc); - break; - } -#endif - default: - size = mPrintingSurface->GetSize(); - } - - if (inPoints) { - // For printing, CSS inches and physical inches are identical - // so it doesn't matter which we use here - mWidth = NSToCoordRound(float(size.width) * AppUnitsPerPhysicalInch() / 72); - mHeight = NSToCoordRound(float(size.height) * AppUnitsPerPhysicalInch() / 72); - } else { - mWidth = NSToIntRound(size.width); - mHeight = NSToIntRound(size.height); - } + gfxSize size = mPrintingSurface->GetSize(); + // For printing, CSS inches and physical inches are identical + // so it doesn't matter which we use here + mWidth = NSToCoordRound(size.width * AppUnitsPerPhysicalInch() + / POINTS_PER_INCH_FLOAT); + mHeight = NSToCoordRound(size.height * AppUnitsPerPhysicalInch() + / POINTS_PER_INCH_FLOAT); return (mWidth > 0 && mHeight > 0); } diff --git a/gfx/thebes/gfxContext.cpp b/gfx/thebes/gfxContext.cpp index 659f80cf0c..d4bfe409fa 100644 --- a/gfx/thebes/gfxContext.cpp +++ b/gfx/thebes/gfxContext.cpp @@ -17,6 +17,7 @@ #include "gfxPattern.h" #include "gfxPlatform.h" #include "gfxTeeSurface.h" +#include "gfxPrefs.h" #include "GeckoProfiler.h" #include "gfx2DGlue.h" #include "mozilla/gfx/PathHelpers.h" @@ -118,8 +119,10 @@ gfxContext::CurrentSurface(gfxFloat *dx, gfxFloat *dy) (cairo_surface_t*)mDT->GetNativeSurface(NativeSurfaceType::CAIRO_SURFACE); if (s) { if (dx && dy) { - *dx = -CurrentState().deviceOffset.x; - *dy = -CurrentState().deviceOffset.y; + double sdx, sdy; + cairo_surface_get_device_offset(s, &sdx, &sdy); + *dx = -CurrentState().deviceOffset.x + sdx; + *dy = -CurrentState().deviceOffset.y + sdy; } return gfxASurface::Wrap(s); } @@ -881,19 +884,24 @@ gfxContext::Paint(gfxFloat alpha) void gfxContext::PushGroupForBlendBack(gfxContentType content, Float aOpacity, SourceSurface* aMask, const Matrix& aMaskTransform) { - DrawTarget* oldDT = mDT; + if (gfxPrefs::UseNativePushLayer()) { + Save(); + mDT->PushLayer(content == gfxContentType::COLOR, aOpacity, aMask, aMaskTransform); + } else { + DrawTarget* oldDT = mDT; - PushNewDT(content); + PushNewDT(content); - if (oldDT != mDT) { - PushClipsToDT(mDT); + if (oldDT != mDT) { + PushClipsToDT(mDT); + } + mDT->SetTransform(GetDTTransform()); + + CurrentState().mBlendOpacity = aOpacity; + CurrentState().mBlendMask = aMask; + CurrentState().mWasPushedForBlendBack = true; + CurrentState().mBlendMaskTransform = aMaskTransform; } - mDT->SetTransform(GetDTTransform()); - - CurrentState().mBlendOpacity = aOpacity; - CurrentState().mBlendMask = aMask; - CurrentState().mWasPushedForBlendBack = true; - CurrentState().mBlendMaskTransform = aMaskTransform; } static gfxRect @@ -914,111 +922,128 @@ gfxContext::PushGroupAndCopyBackground(gfxContentType content, Float aOpacity, S gfxRect clipRect = GetRoundOutDeviceClipExtents(this); clipExtents = IntRect(clipRect.x, clipRect.y, clipRect.width, clipRect.height); } - if ((mDT->GetFormat() == SurfaceFormat::B8G8R8X8 || - mDT->GetOpaqueRect().Contains(clipExtents)) && - !mDT->GetUserData(&sDontUseAsSourceKey)) { - DrawTarget *oldDT = mDT; - RefPtr source = mDT->Snapshot(); - Point oldDeviceOffset = CurrentState().deviceOffset; + bool pushOpaqueWithCopiedBG = (mDT->GetFormat() == SurfaceFormat::B8G8R8X8 || + mDT->GetOpaqueRect().Contains(clipExtents)) && + !mDT->GetUserData(&sDontUseAsSourceKey); - PushNewDT(gfxContentType::COLOR); + if (gfxPrefs::UseNativePushLayer()) { + Save(); - if (oldDT == mDT) { - // Creating new DT failed. + if (pushOpaqueWithCopiedBG) { + mDT->PushLayer(true, aOpacity, aMask, aMaskTransform, IntRect(), true); + } else { + mDT->PushLayer(content == gfxContentType::COLOR, aOpacity, aMask, aMaskTransform, IntRect(), false); + } + } else { + if (pushOpaqueWithCopiedBG) { + DrawTarget *oldDT = mDT; + RefPtr source = mDT->Snapshot(); + Point oldDeviceOffset = CurrentState().deviceOffset; + + PushNewDT(gfxContentType::COLOR); + + if (oldDT == mDT) { + // Creating new DT failed. + return; + } + + CurrentState().mBlendOpacity = aOpacity; + CurrentState().mBlendMask = aMask; + CurrentState().mWasPushedForBlendBack = true; + CurrentState().mBlendMaskTransform = aMaskTransform; + + Point offset = CurrentState().deviceOffset - oldDeviceOffset; + Rect surfRect(0, 0, Float(mDT->GetSize().width), Float(mDT->GetSize().height)); + Rect sourceRect = surfRect + offset; + + mDT->SetTransform(Matrix()); + + // XXX: It's really sad that we have to do this (for performance). + // Once DrawTarget gets a PushLayer API we can implement this within + // DrawTargetTiled. + if (source->GetType() == SurfaceType::TILED) { + SnapshotTiled *sourceTiled = static_cast(source.get()); + for (uint32_t i = 0; i < sourceTiled->mSnapshots.size(); i++) { + Rect tileSourceRect = sourceRect.Intersect(Rect(sourceTiled->mOrigins[i].x, + sourceTiled->mOrigins[i].y, + sourceTiled->mSnapshots[i]->GetSize().width, + sourceTiled->mSnapshots[i]->GetSize().height)); + + if (tileSourceRect.IsEmpty()) { + continue; + } + Rect tileDestRect = tileSourceRect - offset; + tileSourceRect -= sourceTiled->mOrigins[i]; + + mDT->DrawSurface(sourceTiled->mSnapshots[i], tileDestRect, tileSourceRect); + } + } else { + mDT->DrawSurface(source, surfRect, sourceRect); + } + mDT->SetOpaqueRect(oldDT->GetOpaqueRect()); + + PushClipsToDT(mDT); + mDT->SetTransform(GetDTTransform()); return; } + DrawTarget* oldDT = mDT; + PushNewDT(content); + + if (oldDT != mDT) { + PushClipsToDT(mDT); + } + + mDT->SetTransform(GetDTTransform()); CurrentState().mBlendOpacity = aOpacity; CurrentState().mBlendMask = aMask; CurrentState().mWasPushedForBlendBack = true; CurrentState().mBlendMaskTransform = aMaskTransform; - - Point offset = CurrentState().deviceOffset - oldDeviceOffset; - Rect surfRect(0, 0, Float(mDT->GetSize().width), Float(mDT->GetSize().height)); - Rect sourceRect = surfRect + offset; - - mDT->SetTransform(Matrix()); - - // XXX: It's really sad that we have to do this (for performance). - // Once DrawTarget gets a PushLayer API we can implement this within - // DrawTargetTiled. - if (source->GetType() == SurfaceType::TILED) { - SnapshotTiled *sourceTiled = static_cast(source.get()); - for (uint32_t i = 0; i < sourceTiled->mSnapshots.size(); i++) { - Rect tileSourceRect = sourceRect.Intersect(Rect(sourceTiled->mOrigins[i].x, - sourceTiled->mOrigins[i].y, - sourceTiled->mSnapshots[i]->GetSize().width, - sourceTiled->mSnapshots[i]->GetSize().height)); - - if (tileSourceRect.IsEmpty()) { - continue; - } - Rect tileDestRect = tileSourceRect - offset; - tileSourceRect -= sourceTiled->mOrigins[i]; - - mDT->DrawSurface(sourceTiled->mSnapshots[i], tileDestRect, tileSourceRect); - } - } else { - mDT->DrawSurface(source, surfRect, sourceRect); - } - mDT->SetOpaqueRect(oldDT->GetOpaqueRect()); - - PushClipsToDT(mDT); - mDT->SetTransform(GetDTTransform()); - return; } - DrawTarget* oldDT = mDT; - - PushNewDT(content); - - if (oldDT != mDT) { - PushClipsToDT(mDT); - } - - mDT->SetTransform(GetDTTransform()); - CurrentState().mBlendOpacity = aOpacity; - CurrentState().mBlendMask = aMask; - CurrentState().mWasPushedForBlendBack = true; - CurrentState().mBlendMaskTransform = aMaskTransform; } void gfxContext::PopGroupAndBlend() { - MOZ_ASSERT(CurrentState().mWasPushedForBlendBack); - Float opacity = CurrentState().mBlendOpacity; - RefPtr mask = CurrentState().mBlendMask; - Matrix maskTransform = CurrentState().mBlendMaskTransform; - - RefPtr src = mDT->Snapshot(); - Point deviceOffset = CurrentState().deviceOffset; - Restore(); - CurrentState().sourceSurfCairo = nullptr; - CurrentState().sourceSurface = src; - CurrentState().sourceSurfaceDeviceOffset = deviceOffset; - CurrentState().pattern = nullptr; - CurrentState().patternTransformChanged = false; - - Matrix mat = mTransform; - mat.Invert(); - mat.PreTranslate(deviceOffset.x, deviceOffset.y); // device offset translation - - CurrentState().surfTransform = mat; - - CompositionOp oldOp = GetOp(); - SetOp(CompositionOp::OP_OVER); - - if (mask) { - if (!maskTransform.HasNonTranslation()) { - Mask(mask, opacity, Point(maskTransform._31, maskTransform._32)); - } else { - Mask(mask, opacity, maskTransform); - } + if (gfxPrefs::UseNativePushLayer()) { + mDT->PopLayer(); + Restore(); } else { - Paint(opacity); - } + MOZ_ASSERT(CurrentState().mWasPushedForBlendBack); + Float opacity = CurrentState().mBlendOpacity; + RefPtr mask = CurrentState().mBlendMask; + Matrix maskTransform = CurrentState().mBlendMaskTransform; - SetOp(oldOp); + RefPtr src = mDT->Snapshot(); + Point deviceOffset = CurrentState().deviceOffset; + Restore(); + CurrentState().sourceSurfCairo = nullptr; + CurrentState().sourceSurface = src; + CurrentState().sourceSurfaceDeviceOffset = deviceOffset; + CurrentState().pattern = nullptr; + CurrentState().patternTransformChanged = false; + + Matrix mat = mTransform; + mat.Invert(); + mat.PreTranslate(deviceOffset.x, deviceOffset.y); // device offset translation + + CurrentState().surfTransform = mat; + + CompositionOp oldOp = GetOp(); + SetOp(CompositionOp::OP_OVER); + + if (mask) { + if (!maskTransform.HasNonTranslation()) { + Mask(mask, opacity, Point(maskTransform._31, maskTransform._32)); + } else { + Mask(mask, opacity, maskTransform); + } + } else { + Paint(opacity); + } + + SetOp(oldOp); + } } #ifdef MOZ_DUMP_PAINTING diff --git a/gfx/thebes/gfxPrefs.h b/gfx/thebes/gfxPrefs.h index 7afe129fe2..0faa80afbe 100644 --- a/gfx/thebes/gfxPrefs.h +++ b/gfx/thebes/gfxPrefs.h @@ -257,6 +257,8 @@ private: DECL_GFX_PREF(Live, "gfx.testing.device-reset", DeviceResetForTesting, int32_t, 0); DECL_GFX_PREF(Live, "gfx.testing.device-fail", DeviceFailForTesting, bool, false); + DECL_GFX_PREF(Live, "gfx.content.use-native-pushlayer", UseNativePushLayer, bool, false); + // These times should be in milliseconds DECL_GFX_PREF(Once, "gfx.touch.resample.delay-threshold", TouchResampleVsyncDelayThreshold, int32_t, 20); DECL_GFX_PREF(Once, "gfx.touch.resample.max-predict", TouchResampleMaxPredict, int32_t, 8); diff --git a/gfx/thebes/gfxUtils.cpp b/gfx/thebes/gfxUtils.cpp index 7e63b49857..e9a722ffeb 100644 --- a/gfx/thebes/gfxUtils.cpp +++ b/gfx/thebes/gfxUtils.cpp @@ -1314,7 +1314,9 @@ EncodeSourceSurfaceInternal(SourceSurface* aSurface, &numReadThisTime)) == NS_OK && numReadThisTime > 0) { // Update the length of the vector without overwriting the new data. - imgData.growByUninitialized(numReadThisTime); + if (!imgData.growByUninitialized(numReadThisTime)) { + return NS_ERROR_OUT_OF_MEMORY; + } imgSize += numReadThisTime; if (imgSize == bufSize) { diff --git a/gfx/thebes/gfxWindowsNativeDrawing.cpp b/gfx/thebes/gfxWindowsNativeDrawing.cpp index fc0404381a..b457e9479a 100644 --- a/gfx/thebes/gfxWindowsNativeDrawing.cpp +++ b/gfx/thebes/gfxWindowsNativeDrawing.cpp @@ -53,9 +53,13 @@ gfxWindowsNativeDrawing::BeginNativeDrawing() cairo = static_cast (drawTarget->GetNativeSurface(NativeSurfaceType::CAIRO_CONTEXT)); if (cairo) { - cairo_surface_t* s = cairo_get_target(cairo); + cairo_surface_t* s = cairo_get_group_target(cairo); if (s) { mDeviceOffset = mContext->GetDeviceOffset(); + double sdx, sdy; + cairo_surface_get_device_offset(s, &sdx, &sdy); + mDeviceOffset.x -= sdx; + mDeviceOffset.y -= sdy; surf = gfxASurface::Wrap(s); } } diff --git a/gfx/thebes/gfxWindowsSurface.cpp b/gfx/thebes/gfxWindowsSurface.cpp index 27b165da2a..6249faf47f 100644 --- a/gfx/thebes/gfxWindowsSurface.cpp +++ b/gfx/thebes/gfxWindowsSurface.cpp @@ -308,6 +308,15 @@ gfxWindowsSurface::EndPage() const mozilla::gfx::IntSize gfxWindowsSurface::GetSize() const { + if (mForPrinting) { + // On Windows we need to use the printable area of the page. + float width = (::GetDeviceCaps(mDC, HORZRES) * POINTS_PER_INCH_FLOAT) + / ::GetDeviceCaps(mDC, LOGPIXELSX); + float height = (::GetDeviceCaps(mDC, VERTRES) * POINTS_PER_INCH_FLOAT) + / ::GetDeviceCaps(mDC, LOGPIXELSY); + return mozilla::gfx::IntSize(width, height); + } + if (!mSurfaceValid) { NS_WARNING ("GetImageSurface on an invalid (null) surface; who's calling this without checking for surface errors?"); return mozilla::gfx::IntSize(-1, -1); diff --git a/image/StreamingLexer.h b/image/StreamingLexer.h index 0f49981b00..968d3a0c3e 100644 --- a/image/StreamingLexer.h +++ b/image/StreamingLexer.h @@ -316,7 +316,9 @@ public: size_t toRead = std::min(aLength, mTransition.Size() - mBuffer.length()); - mBuffer.append(aInput, toRead); + if (!mBuffer.append(aInput, toRead)) { + return Some(TerminalState::FAILURE); + } aInput += toRead; aLength -= toRead; if (mBuffer.length() != mTransition.Size()) { @@ -394,7 +396,9 @@ public: if (!mBuffer.reserve(mTransition.Size())) { return Some(TerminalState::FAILURE); // Done due to allocation failure. } - mBuffer.append(aInput, aLength); + if (!mBuffer.append(aInput, aLength)) { + return Some(TerminalState::FAILURE); + } return Nothing(); // Need more input. } diff --git a/image/test/gtest/TestStreamingLexer.cpp b/image/test/gtest/TestStreamingLexer.cpp index 5be764e52f..8f59ae286b 100644 --- a/image/test/gtest/TestStreamingLexer.cpp +++ b/image/test/gtest/TestStreamingLexer.cpp @@ -55,7 +55,7 @@ DoLexWithUnbuffered(TestState aState, const char* aData, size_t aLength, return Transition::ToUnbuffered(TestState::TWO, TestState::UNBUFFERED, 3); case TestState::UNBUFFERED: EXPECT_TRUE(aLength <= 3); - aUnbufferedVector.append(aData, aLength); + EXPECT_TRUE(aUnbufferedVector.append(aData, aLength)); return Transition::ContinueUnbuffered(TestState::UNBUFFERED); case TestState::TWO: CheckData(aUnbufferedVector.begin(), aUnbufferedVector.length()); diff --git a/ipc/glue/MessageChannel.cpp b/ipc/glue/MessageChannel.cpp index 4aa13455a0..708c3d953b 100644 --- a/ipc/glue/MessageChannel.cpp +++ b/ipc/glue/MessageChannel.cpp @@ -226,7 +226,8 @@ public: if (mThat.mCxxStackFrames.empty()) mThat.EnteredCxxStack(); - mThat.mCxxStackFrames.append(InterruptFrame(direction, msg)); + if (!mThat.mCxxStackFrames.append(InterruptFrame(direction, msg))) + MOZ_CRASH(); const InterruptFrame& frame = mThat.mCxxStackFrames.back(); @@ -778,7 +779,8 @@ MessageChannel::ProcessPendingRequests() for (MessageQueue::iterator it = mPending.begin(); it != mPending.end(); ) { Message &msg = *it; if (!ShouldDeferMessage(msg)) { - toProcess.append(Move(msg)); + if (!toProcess.append(Move(msg))) + MOZ_CRASH(); it = mPending.erase(it); continue; } diff --git a/js/src/builtin/Profilers.cpp b/js/src/builtin/Profilers.cpp index 30f5f86de6..100b26fbe6 100644 --- a/js/src/builtin/Profilers.cpp +++ b/js/src/builtin/Profilers.cpp @@ -513,7 +513,8 @@ bool js_StartPerf() "--pid", mainPidStr, "--output", outfile}; Vector args; - args.append(defaultArgs, ArrayLength(defaultArgs)); + if (!args.append(defaultArgs, ArrayLength(defaultArgs))) + return false; const char* flags = getenv("MOZ_PROFILE_PERF_FLAGS"); if (!flags) { @@ -530,11 +531,13 @@ bool js_StartPerf() char* toksave; char* tok = strtok_r(flags2, " ", &toksave); while (tok) { - args.append(tok); + if (!args.append(tok)) + return false; tok = strtok_r(nullptr, " ", &toksave); } - args.append((char*) nullptr); + if (!args.append((char*) nullptr)) + return false; execvp("perf", const_cast(args.begin())); diff --git a/js/src/gc/Nursery.cpp b/js/src/gc/Nursery.cpp index d6d51a38c6..c36ffc5ed5 100644 --- a/js/src/gc/Nursery.cpp +++ b/js/src/gc/Nursery.cpp @@ -10,6 +10,7 @@ #include "mozilla/DebugOnly.h" #include "mozilla/IntegerPrintfMacros.h" #include "mozilla/Move.h" +#include "mozilla/unused.h" #include "jscompartment.h" #include "jsfriendapi.h" @@ -535,7 +536,7 @@ js::Nursery::collect(JSRuntime* rt, JS::gcreason::Reason reason, ObjectGroupList for (size_t i = 0; i < ArrayLength(tenureCounts.entries); i++) { const TenureCount& entry = tenureCounts.entries[i]; if (entry.count >= 3000) - (void)pretenureGroups->append(entry.group); // ignore alloc failure + mozilla::Unused << pretenureGroups->append(entry.group); // ignore alloc failure } } TIME_END(pretenure); diff --git a/js/src/jit-test/tests/ion/dce-with-rinstructions.js b/js/src/jit-test/tests/ion/dce-with-rinstructions.js index 3226fb641c..014fde1dfc 100644 --- a/js/src/jit-test/tests/ion/dce-with-rinstructions.js +++ b/js/src/jit-test/tests/ion/dce-with-rinstructions.js @@ -232,6 +232,16 @@ function rmul_number(i) { return i; } +var uceFault_mul_overflow = eval(uneval(uceFault).replace('uceFault', 'uceFault_mul_overflow')); +function rmul_overflow(i) { + var x = Math.pow(2, i * 16 / 99) | 0; + x = x * x; + if (uceFault_mul_overflow(i) || uceFault_mul_overflow(i)) + assertEq(x, Math.pow(2, 32)); + assertRecoveredOnBailout(x, true); + return i; +} + var uceFault_mul_float = eval(uneval(uceFault).replace('uceFault', 'uceFault_mul_float')); function rmul_float(i) { var t = Math.fround(1/3); @@ -255,6 +265,37 @@ function rmul_object(i) { return i; } +var uceFault_imul_number = eval(uneval(uceFault).replace('uceFault', 'uceFault_imul_number')); +function rimul_number(i) { + var x = Math.imul(2, i); + if (uceFault_imul_number(i) || uceFault_imul_number(i)) + assertEq(x, 198 /* = 1 * 99 */); + assertRecoveredOnBailout(x, true); + return i; +} + +var uceFault_imul_overflow = eval(uneval(uceFault).replace('uceFault', 'uceFault_imul_overflow')); +function rimul_overflow(i) { + var x = Math.pow(2, i * 16 / 99) | 0; + x = Math.imul(x, x); + if (uceFault_imul_overflow(i) || uceFault_imul_overflow(i)) + assertEq(x, 0); + assertRecoveredOnBailout(x, true); + return i; +} + +var uceFault_imul_object = eval(uneval(uceFault).replace('uceFault', 'uceFault_imul_object')); +function rimul_object(i) { + var t = i; + var o = { valueOf: function () { return t; } }; + var x = Math.imul(o, i); /* computed with t == i, not 1000 */ + t = 1000; + if (uceFault_imul_object(i) || uceFault_imul_object(i)) + assertEq(x, 9801); + assertRecoveredOnBailout(x, false); + return i; +} + var uceFault_div_number = eval(uneval(uceFault).replace('uceFault', 'uceFault_div_number')); function rdiv_number(i) { var x = 1 / i; @@ -1263,8 +1304,12 @@ for (i = 0; i < 100; i++) { rsub_float(i); rsub_object(i); rmul_number(i); + rmul_overflow(i); rmul_float(i); rmul_object(i); + rimul_number(i); + rimul_overflow(i); + rimul_object(i); rdiv_number(i); rdiv_float(i); rdiv_object(i); diff --git a/js/src/jit/Recover.cpp b/js/src/jit/Recover.cpp index fae79ea90d..da795663bc 100644 --- a/js/src/jit/Recover.cpp +++ b/js/src/jit/Recover.cpp @@ -406,12 +406,15 @@ MMul::writeRecoverData(CompactBufferWriter& writer) const MOZ_ASSERT(canRecoverOnBailout()); writer.writeUnsigned(uint32_t(RInstruction::Recover_Mul)); writer.writeByte(specialization_ == MIRType_Float32); + MOZ_ASSERT(Mode(uint8_t(mode_)) == mode_); + writer.writeByte(uint8_t(mode_)); return true; } RMul::RMul(CompactBufferReader& reader) { isFloatOperation_ = reader.readByte(); + mode_ = reader.readByte(); } bool @@ -421,13 +424,19 @@ RMul::recover(JSContext* cx, SnapshotIterator& iter) const RootedValue rhs(cx, iter.read()); RootedValue result(cx); - if (!js::MulValues(cx, &lhs, &rhs, &result)) - return false; + if (MMul::Mode(mode_) == MMul::Normal) { + if (!js::MulValues(cx, &lhs, &rhs, &result)) + return false; - // MIRType_Float32 is a specialization embedding the fact that the result is - // rounded to a Float32. - if (isFloatOperation_ && !RoundFloat32(cx, result, &result)) - return false; + // MIRType_Float32 is a specialization embedding the fact that the + // result is rounded to a Float32. + if (isFloatOperation_ && !RoundFloat32(cx, result, &result)) + return false; + } else { + MOZ_ASSERT(MMul::Mode(mode_) == MMul::Integer); + if (!js::math_imul_handle(cx, lhs, rhs, &result)) + return false; + } iter.storeInstructionResult(result); return true; diff --git a/js/src/jit/Recover.h b/js/src/jit/Recover.h index 4d2d879267..f543214611 100644 --- a/js/src/jit/Recover.h +++ b/js/src/jit/Recover.h @@ -291,6 +291,7 @@ class RMul final : public RInstruction { private: bool isFloatOperation_; + uint8_t mode_; public: RINSTRUCTION_HEADER_(Mul) diff --git a/js/src/jit/StackSlotAllocator.h b/js/src/jit/StackSlotAllocator.h index 8b0da8ca20..a72fe26eb8 100644 --- a/js/src/jit/StackSlotAllocator.h +++ b/js/src/jit/StackSlotAllocator.h @@ -7,6 +7,8 @@ #ifndef jit_StackSlotAllocator_h #define jit_StackSlotAllocator_h +#include "mozilla/unused.h" + #include "jit/Registers.h" namespace js { @@ -21,10 +23,10 @@ class StackSlotAllocator void addAvailableSlot(uint32_t index) { // Ignoring OOM here (and below) is fine; it just means the stack slot // will be unused. - (void)normalSlots.append(index); + mozilla::Unused << normalSlots.append(index); } void addAvailableDoubleSlot(uint32_t index) { - (void)doubleSlots.append(index); + mozilla::Unused << doubleSlots.append(index); } uint32_t allocateQuadSlot() { diff --git a/js/src/jsmath.cpp b/js/src/jsmath.cpp index 7d5b11054d..3f4ddd9b4b 100644 --- a/js/src/jsmath.cpp +++ b/js/src/jsmath.cpp @@ -39,6 +39,15 @@ #include "jsobjinlines.h" +#if defined(XP_WIN) +// #define needed to link in RtlGenRandom(), a.k.a. SystemFunction036. See the +// "Community Additions" comment on MSDN here: +// https://msdn.microsoft.com/en-us/library/windows/desktop/aa387694.aspx +# define SystemFunction036 NTAPI SystemFunction036 +# include +# undef SystemFunction036 +#endif + #if defined(ANDROID) || defined(XP_DARWIN) || defined(__DragonFly__) || \ defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) # include @@ -475,22 +484,28 @@ js::math_floor(JSContext* cx, unsigned argc, Value* vp) return math_floor_handle(cx, args[0], args.rval()); } +bool +js::math_imul_handle(JSContext* cx, HandleValue lhs, HandleValue rhs, MutableHandleValue res) +{ + uint32_t a = 0, b = 0; + if (!lhs.isUndefined() && !ToUint32(cx, lhs, &a)) + return false; + if (!rhs.isUndefined() && !ToUint32(cx, rhs, &b)) + return false; + + uint32_t product = a * b; + res.setInt32(product > INT32_MAX + ? int32_t(INT32_MIN + (product - INT32_MAX - 1)) + : int32_t(product)); + return true; +} + bool js::math_imul(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); - uint32_t a = 0, b = 0; - if (args.hasDefined(0) && !ToUint32(cx, args[0], &a)) - return false; - if (args.hasDefined(1) && !ToUint32(cx, args[1], &b)) - return false; - - uint32_t product = a * b; - args.rval().setInt32(product > INT32_MAX - ? int32_t(INT32_MIN + (product - INT32_MAX - 1)) - : int32_t(product)); - return true; + return math_imul_handle(cx, args.get(0), args.get(1), args.rval()); } // Implements Math.fround (20.2.2.16) up to step 3 @@ -733,82 +748,27 @@ js::math_pow(JSContext* cx, unsigned argc, Value* vp) return math_pow_handle(cx, args.get(0), args.get(1), args.rval()); } -static void -GenerateSeed(uint64_t* seedBuffer, size_t length) +static uint64_t +GenerateSeed() { - MOZ_ASSERT(length > 0); + uint64_t seed = 0; #if defined(XP_WIN) - /* - * Temporary diagnostic for bug 1167248: Test whether the injected hooks - * react differently to LoadLibraryW / LoadLibraryExW. - */ - HMODULE oldWay = LoadLibraryW(L"ADVAPI32.DLL"); - HMODULE newWay = LoadLibraryExW(L"ADVAPI32.DLL", - nullptr, - LOAD_LIBRARY_SEARCH_SYSTEM32); - /* Fallback for older versions of Windows */ - if (!newWay && GetLastError() == ERROR_INVALID_PARAMETER) - newWay = LoadLibraryExW(L"ADVAPI32.DLL", nullptr, 0); - - if (oldWay && !newWay) - MOZ_CRASH(); - - union { - uint32_t u32[2]; - uint64_t u64; - } seed; - seed.u64 = 0; - - errno_t error = rand_s(&seed.u32[0]); - - if (oldWay) - FreeLibrary(oldWay); - if (newWay) - FreeLibrary(newWay); - - MOZ_ASSERT(error == 0, "rand_s() error?!"); - - error = rand_s(&seed.u32[1]); - MOZ_ASSERT(error == 0, "rand_s() error?!"); - - seedBuffer[0] = seed.u64 ^= PRMJ_Now(); - for (size_t i = 1; i < length; i++) { - error = rand_s(&seed.u32[0]); - MOZ_ASSERT(error == 0, "rand_s() error?!"); - - error = rand_s(&seed.u32[1]); - MOZ_ASSERT(error == 0, "rand_s() error?!"); - - seedBuffer[i] = seed.u64 ^ PRMJ_Now(); - } - + MOZ_ALWAYS_TRUE(RtlGenRandom(&seed, sizeof(seed))); #elif defined(HAVE_ARC4RANDOM) - union { - uint32_t u32[2]; - uint64_t u64; - } seed; - seed.u64 = 0; - - for (size_t i = 0; i < length; i++) { - seed.u32[0] = arc4random(); - seed.u32[1] = arc4random(); - seedBuffer[i] = seed.u64 ^ PRMJ_Now(); - } - + seed = (static_cast(arc4random()) << 32) | arc4random(); #elif defined(XP_UNIX) int fd = open("/dev/urandom", O_RDONLY); - MOZ_ASSERT(fd >= 0, "Can't open /dev/urandom?!"); if (fd >= 0) { - ssize_t size = length * sizeof(seedBuffer[0]); - ssize_t nread = read(fd, (char *) seedBuffer, size); - MOZ_ASSERT(nread == size, "Can't read /dev/urandom?!"); - mozilla::Unused << nread; + mozilla::Unused << read(fd, static_cast(&seed), sizeof(seed)); close(fd); } #else -# error "Platform needs to implement random_generateSeed()" +# error "Platform needs to implement GenerateSeed()" #endif + + // Also mix in PRMJ_Now() in case we couldn't read random bits from the OS. + return seed ^ PRMJ_Now(); } void @@ -816,7 +776,8 @@ js::GenerateXorShift128PlusSeed(mozilla::Array& seed) { // XorShift128PlusRNG must be initialized with a non-zero seed. do { - GenerateSeed(seed.begin(), mozilla::ArrayLength(seed)); + seed[0] = GenerateSeed(); + seed[1] = GenerateSeed(); } while (seed[0] == 0 && seed[1] == 0); } diff --git a/js/src/jsmath.h b/js/src/jsmath.h index 2250af8237..e703c703c6 100644 --- a/js/src/jsmath.h +++ b/js/src/jsmath.h @@ -138,6 +138,9 @@ math_sincos_impl(MathCache* mathCache, double x, double *sin, double *cos); extern bool math_sqrt_handle(JSContext* cx, js::HandleValue number, js::MutableHandleValue result); +extern bool +math_imul_handle(JSContext* cx, HandleValue lhs, HandleValue rhs, MutableHandleValue res); + extern bool math_imul(JSContext* cx, unsigned argc, js::Value* vp); diff --git a/js/src/vm/Stopwatch.cpp b/js/src/vm/Stopwatch.cpp index c06724a8ed..faa37f765f 100644 --- a/js/src/vm/Stopwatch.cpp +++ b/js/src/vm/Stopwatch.cpp @@ -221,8 +221,10 @@ AutoStopwatch::AutoStopwatch(JSContext* cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IM } for (auto group = groups->begin(); group < groups->end(); group++) { auto acquired = acquireGroup(*group); - if (acquired) - groups_.append(acquired); + if (acquired) { + if (!groups_.append(acquired)) + MOZ_CRASH(); + } } if (groups_.length() == 0) { // We are not in charge of monitoring anything. diff --git a/js/xpconnect/src/XPCJSRuntime.cpp b/js/xpconnect/src/XPCJSRuntime.cpp index fd24b7871f..8cb3032b35 100644 --- a/js/xpconnect/src/XPCJSRuntime.cpp +++ b/js/xpconnect/src/XPCJSRuntime.cpp @@ -44,6 +44,7 @@ #include "mozilla/Atomics.h" #include "mozilla/Attributes.h" #include "mozilla/ProcessHangMonitor.h" +#include "mozilla/unused.h" #include "AccessCheck.h" #include "nsGlobalWindow.h" #include "nsAboutProtocolUtils.h" @@ -2655,7 +2656,7 @@ class JSMainRuntimeCompartmentsReporter final : public nsIMemoryReporter ? NS_LITERAL_CSTRING("js-main-runtime-compartments/system/") : NS_LITERAL_CSTRING("js-main-runtime-compartments/user/"), 0); - data->paths.append(path); + mozilla::Unused << data->paths.append(path); } NS_IMETHOD CollectReports(nsIMemoryReporterCallback* cb, diff --git a/layout/reftests/bugs/reftest.list b/layout/reftests/bugs/reftest.list index a74e2b1bd8..acb7258267 100644 --- a/layout/reftests/bugs/reftest.list +++ b/layout/reftests/bugs/reftest.list @@ -1684,7 +1684,7 @@ fails-if(layersGPUAccelerated&&cocoaWidget) fails-if(Android&&AndroidVersion==15 != 656875.html about:blank == 658952.html 658952-ref.html == 660682-1.html 660682-1-ref.html -skip-if((B2G&&browserIsRemote)||Mulet) skip-if(Android) == 664127-1.xul 664127-1-ref.xul # B2G: bug 974780. Android: Intermittent failures - bug 1019131 # Initial mulet triage: parity with B2G/B2G Desktop +fuzzy-if(d2d,1,256) skip-if((B2G&&browserIsRemote)||Mulet) skip-if(Android) == 664127-1.xul 664127-1-ref.xul # B2G: bug 974780. Android: Intermittent failures - bug 1019131 # Initial mulet triage: parity with B2G/B2G Desktop skip-if(B2G||Mulet) == 665597-1.html 665597-1-ref.html # Initial mulet triage: parity with B2G/B2G Desktop skip-if(B2G||Mulet) == 665597-2.html 665597-2-ref.html # Initial mulet triage: parity with B2G/B2G Desktop skip-if((B2G&&browserIsRemote)||Mulet) == 668319-1.xul about:blank # bug 974780 # Initial mulet triage: parity with B2G/B2G Desktop @@ -1833,7 +1833,7 @@ fuzzy-if(/^Windows\x20NT\x205\.1/.test(http.oscpu),255,1) == 1013054-1.html 1013 == 1021564-3.html 1021564-ref.html == 1021564-4.html 1021564-ref.html pref(browser.display.use_document_fonts,0) == 1022481-1.html 1022481-1-ref.html -== 1022612-1.html 1022612-1-ref.html +fuzzy-if(d2d,1,125200) == 1022612-1.html 1022612-1-ref.html == 1024473-1.html 1024473-1-ref.html == 1025914-1.html 1025914-1-ref.html == 1042104-1.html 1042104-1-ref.html diff --git a/layout/reftests/css-blending/reftest.list b/layout/reftests/css-blending/reftest.list index 2764c10570..1ce023ce41 100644 --- a/layout/reftests/css-blending/reftest.list +++ b/layout/reftests/css-blending/reftest.list @@ -43,7 +43,7 @@ fuzzy(64,37) pref(layout.css.mix-blend-mode.enabled,true) == mix-blend-mode-9520 pref(layout.css.mix-blend-mode.enabled,true) pref(layout.css.filters.enabled,true) == mix-blend-mode-and-filter.html mix-blend-mode-and-filter-ref.html pref(layout.css.mix-blend-mode.enabled,true) pref(layout.css.filters.enabled,true) == mix-blend-mode-and-filter.svg mix-blend-mode-and-filter-ref.svg -pref(layout.css.mix-blend-mode.enabled,true) == mix-blend-mode-child-of-blended-has-opacity.html mix-blend-mode-child-of-blended-has-opacity-ref.html +pref(layout.css.mix-blend-mode.enabled,true) fuzzy-if(d2d,1,14400) == mix-blend-mode-child-of-blended-has-opacity.html mix-blend-mode-child-of-blended-has-opacity-ref.html pref(layout.css.mix-blend-mode.enabled,true) == mix-blend-mode-nested-976533.html mix-blend-mode-nested-976533-ref.html pref(layout.css.mix-blend-mode.enabled,true) == mix-blend-mode-culling-1207041.html mix-blend-mode-culling-1207041-ref.html diff --git a/layout/reftests/table-background/reftest.list b/layout/reftests/table-background/reftest.list index e38308551d..931b727ed3 100644 --- a/layout/reftests/table-background/reftest.list +++ b/layout/reftests/table-background/reftest.list @@ -40,17 +40,17 @@ skip-if(B2G||Mulet) == border-collapse-table-column.html border-collapse-table-c skip-if(B2G||Mulet) == border-collapse-table-row-group.html border-collapse-table-row-group-ref.html # bug 773482 # Initial mulet triage: parity with B2G/B2G Desktop == border-collapse-table-row.html border-collapse-table-row-ref.html == border-collapse-table.html border-collapse-table-ref.html -skip-if(B2G||Mulet) == border-collapse-opacity-table-cell.html border-collapse-opacity-table-cell-ref.html # bug 773482 # Initial mulet triage: parity with B2G/B2G Desktop +skip-if(B2G||Mulet) fuzzy-if(d2d,1,1083) == border-collapse-opacity-table-cell.html border-collapse-opacity-table-cell-ref.html # bug 773482 # Initial mulet triage: parity with B2G/B2G Desktop fails == border-collapse-opacity-table-column-group.html border-collapse-opacity-table-column-group-ref.html # bug 424274 fails == border-collapse-opacity-table-column.html border-collapse-opacity-table-column-ref.html # bug 424274 -skip-if(B2G||Mulet) == border-collapse-opacity-table-row-group.html border-collapse-opacity-table-row-group-ref.html # bug 773482 # Initial mulet triage: parity with B2G/B2G Desktop -skip-if(B2G||Mulet) == border-collapse-opacity-table-row.html border-collapse-opacity-table-row-ref.html # bug 773482 # Initial mulet triage: parity with B2G/B2G Desktop -skip-if(B2G||Mulet) == border-collapse-opacity-table.html border-collapse-opacity-table-ref.html # bug 773482 # Initial mulet triage: parity with B2G/B2G Desktop -skip-if(B2G||Mulet) == border-separate-opacity-table-cell.html border-separate-opacity-table-cell-ref.html # bug 773482 # Initial mulet triage: parity with B2G/B2G Desktop +skip-if(B2G||Mulet) fuzzy-if(d2d,1,16359) == border-collapse-opacity-table-row-group.html border-collapse-opacity-table-row-group-ref.html # bug 773482 # Initial mulet triage: parity with B2G/B2G Desktop +skip-if(B2G||Mulet) fuzzy-if(d2d,1,5453) == border-collapse-opacity-table-row.html border-collapse-opacity-table-row-ref.html # bug 773482 # Initial mulet triage: parity with B2G/B2G Desktop +skip-if(B2G||Mulet) fuzzy-if(d2d,1,29973) == border-collapse-opacity-table.html border-collapse-opacity-table-ref.html # bug 773482 # Initial mulet triage: parity with B2G/B2G Desktop +skip-if(B2G||Mulet) fuzzy-if(d2d,1,2478) == border-separate-opacity-table-cell.html border-separate-opacity-table-cell-ref.html # bug 773482 # Initial mulet triage: parity with B2G/B2G Desktop fails == border-separate-opacity-table-column-group.html border-separate-opacity-table-column-group-ref.html # bug 424274 fails == border-separate-opacity-table-column.html border-separate-opacity-table-column-ref.html # bug 424274 -== border-separate-opacity-table-row-group.html border-separate-opacity-table-row-group-ref.html -skip-if(B2G||Mulet) == border-separate-opacity-table-row.html border-separate-opacity-table-row-ref.html # bug 773482 # Initial mulet triage: parity with B2G/B2G Desktop +fuzzy-if(d2d,1,37170) == border-separate-opacity-table-row-group.html border-separate-opacity-table-row-group-ref.html +skip-if(B2G||Mulet) fuzzy-if(d2d,1,12390) == border-separate-opacity-table-row.html border-separate-opacity-table-row-ref.html # bug 773482 # Initial mulet triage: parity with B2G/B2G Desktop skip-if(B2G||Mulet) == border-separate-opacity-table.html border-separate-opacity-table-ref.html # bug 773482 # Initial mulet triage: parity with B2G/B2G Desktop != scrollable-rowgroup-collapse-background.html scrollable-rowgroup-collapse-notref.html != scrollable-rowgroup-collapse-border.html scrollable-rowgroup-collapse-notref.html diff --git a/media/webrtc/signaling/src/peerconnection/PeerConnectionCtx.cpp b/media/webrtc/signaling/src/peerconnection/PeerConnectionCtx.cpp index 1707273d5c..537b866a31 100644 --- a/media/webrtc/signaling/src/peerconnection/PeerConnectionCtx.cpp +++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionCtx.cpp @@ -318,7 +318,9 @@ PeerConnectionCtx::EverySecondTelemetryCallback_m(nsITimer* timer, void *closure for (auto p = ctx->mPeerConnections.begin(); p != ctx->mPeerConnections.end(); ++p) { if (p->second->HasMedia()) { - queries->append(nsAutoPtr(new RTCStatsQuery(true))); + if (!queries->append(nsAutoPtr(new RTCStatsQuery(true)))) { + return; + } if (NS_WARN_IF(NS_FAILED(p->second->BuildStatsQuery_m(nullptr, // all tracks queries->back())))) { queries->popBack(); diff --git a/media/webrtc/signaling/src/peerconnection/WebrtcGlobalInformation.cpp b/media/webrtc/signaling/src/peerconnection/WebrtcGlobalInformation.cpp index 361ea83473..76310ad5bf 100644 --- a/media/webrtc/signaling/src/peerconnection/WebrtcGlobalInformation.cpp +++ b/media/webrtc/signaling/src/peerconnection/WebrtcGlobalInformation.cpp @@ -386,7 +386,9 @@ BuildStatsQueryList( if (aPcIdFilter.IsEmpty() || aPcIdFilter.EqualsASCII(pc.second->GetIdAsAscii().c_str())) { if (pc.second->HasMedia()) { - queries->append(nsAutoPtr(new RTCStatsQuery(true))); + if (!queries->append(nsAutoPtr(new RTCStatsQuery(true)))) { + return NS_ERROR_OUT_OF_MEMORY; + } rv = pc.second->BuildStatsQuery_m(nullptr, queries->back()); // all tracks if (NS_WARN_IF(NS_FAILED(rv))) { return rv; diff --git a/mfbt/Compiler.h b/mfbt/Compiler.h index 6d6fcbbb19..e8cf2e9d3c 100644 --- a/mfbt/Compiler.h +++ b/mfbt/Compiler.h @@ -10,7 +10,7 @@ #define mozilla_Compiler_h #define MOZ_IS_GCC 0 -#define MOS_IS_MSVC 0 +#define MOZ_IS_MSVC 0 #if !defined(__clang__) && defined(__GNUC__) diff --git a/mfbt/Function.h b/mfbt/Function.h index 1cc3a5622d..6a617e3831 100644 --- a/mfbt/Function.h +++ b/mfbt/Function.h @@ -11,7 +11,8 @@ #include "mozilla/Attributes.h" // for MOZ_IMPLICIT #include "mozilla/Move.h" -#include "mozilla/UniquePtr.h" +#include "mozilla/RefCounted.h" +#include "mozilla/RefPtr.h" // |Function| is a wrapper that can hold any type of callable // object that can be invoked in a way that's compatible with |Signature|. @@ -40,9 +41,11 @@ namespace mozilla { namespace detail { template -class FunctionImplBase +class FunctionImplBase : public mozilla::RefCounted> { public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FunctionImplBase) + virtual ~FunctionImplBase() {} virtual ReturnType call(Arguments... aArguments) = 0; }; @@ -137,7 +140,12 @@ public: // This constructor is implicit to match the interface of |std::function|. template MOZ_IMPLICIT Function(const Callable& aCallable) - : mImpl(MakeUnique>(aCallable)) + : mImpl(new detail::FunctionImpl(aCallable)) + {} + MOZ_IMPLICIT Function(const Function& aFunction) + : mImpl(aFunction.mImpl) + {} + MOZ_IMPLICIT Function(decltype(nullptr)) {} // Move constructor and move assingment operator. @@ -151,7 +159,17 @@ public: template Function& operator=(const Callable& aCallable) { - mImpl = MakeUnique>(aCallable); + mImpl = new detail::FunctionImpl(aCallable); + return *this; + } + Function& operator=(const Function& aFunction) + { + mImpl = aFunction.mImpl; + return *this; + } + Function& operator=(decltype(nullptr)) + { + mImpl = nullptr; return *this; } @@ -161,9 +179,15 @@ public: MOZ_ASSERT(mImpl); return mImpl->call(Forward(aArguments)...); } + + explicit operator bool() const + { + return bool(mImpl); + } + private: // TODO: Consider implementing a small object optimization. - UniquePtr> mImpl; + RefPtr> mImpl; }; } // namespace mozilla diff --git a/mfbt/InitializerList.h b/mfbt/InitializerList.h index 0f32425327..f58458009d 100644 --- a/mfbt/InitializerList.h +++ b/mfbt/InitializerList.h @@ -40,7 +40,9 @@ class initializer_list size_t mSize; /* This constructor is called directly by the compiler */ - initializer_list(const T* begin, size_t size) : mBegin(begin), mSize(size) {} + initializer_list(const T* aBegin, size_t aSize) + : mBegin(aBegin) + , mSize(aSize) { } public: MOZ_CONSTEXPR initializer_list() : mBegin(nullptr), mSize(0) {} diff --git a/mfbt/RefPtr.h b/mfbt/RefPtr.h index 4798d4333f..20688545f0 100644 --- a/mfbt/RefPtr.h +++ b/mfbt/RefPtr.h @@ -19,6 +19,27 @@ class nsCOMPtr_helper; namespace mozilla { template class OwningNonNull; + +// Traditionally, RefPtr supports automatic refcounting of any pointer type +// with AddRef() and Release() methods that follow the traditional semantics. +// +// This traits class can be specialized to operate on other pointer types. For +// example, we specialize this trait for opaque FFI types that represent +// refcounted objects in Rust. +// +// Given the use of ConstRemovingRefPtrTraits below, U should not be a const- +// qualified type. +template +struct RefPtrTraits +{ + static void AddRef(U* aPtr) { + aPtr->AddRef(); + } + static void Release(U* aPtr) { + aPtr->Release(); + } +}; + } // namespace mozilla template @@ -29,7 +50,7 @@ private: assign_with_AddRef(T* aRawPtr) { if (aRawPtr) { - AddRefTraits::AddRef(aRawPtr); + ConstRemovingRefPtrTraits::AddRef(aRawPtr); } assign_assuming_AddRef(aRawPtr); } @@ -40,7 +61,7 @@ private: T* oldPtr = mRawPtr; mRawPtr = aNewPtr; if (oldPtr) { - AddRefTraits::Release(oldPtr); + ConstRemovingRefPtrTraits::Release(oldPtr); } } @@ -53,7 +74,7 @@ public: ~RefPtr() { if (mRawPtr) { - AddRefTraits::Release(mRawPtr); + ConstRemovingRefPtrTraits::Release(mRawPtr); } } @@ -70,7 +91,7 @@ public: // copy-constructor { if (mRawPtr) { - AddRefTraits::AddRef(mRawPtr); + ConstRemovingRefPtrTraits::AddRef(mRawPtr); } } @@ -86,7 +107,7 @@ public: : mRawPtr(aRawPtr) { if (mRawPtr) { - AddRefTraits::AddRef(mRawPtr); + ConstRemovingRefPtrTraits::AddRef(mRawPtr); } } @@ -110,7 +131,7 @@ public: // copy-construct from a smart pointer with a related pointer type { if (mRawPtr) { - AddRefTraits::AddRef(mRawPtr); + ConstRemovingRefPtrTraits::AddRef(mRawPtr); } } @@ -346,40 +367,24 @@ private: // This should be sound because while |RefPtr| provides a // const view of an object, the object itself should not be const (it // would have to be allocated as |new const T| or similar to be const). - - // Because some classes make their AddRef/Release implementations private - // and then friend RefPtr to make them visible, we redirect AddRefTraits's - // calls to static helper functions in RefPtr so we don't have to figure - // out how to make AddRefTraits visible to *those* classes. - static MOZ_ALWAYS_INLINE void - AddRefTraitsAddRefHelper(typename mozilla::RemoveConst::Type* aPtr) - { - aPtr->AddRef(); - } - static MOZ_ALWAYS_INLINE void - AddRefTraitsReleaseHelper(typename mozilla::RemoveConst::Type* aPtr) - { - aPtr->Release(); - } - template - struct AddRefTraits + struct ConstRemovingRefPtrTraits { static void AddRef(U* aPtr) { - RefPtr::AddRefTraitsAddRefHelper(aPtr); + mozilla::RefPtrTraits::AddRef(aPtr); } static void Release(U* aPtr) { - RefPtr::AddRefTraitsReleaseHelper(aPtr); + mozilla::RefPtrTraits::Release(aPtr); } }; template - struct AddRefTraits + struct ConstRemovingRefPtrTraits { static void AddRef(const U* aPtr) { - RefPtr::AddRefTraitsAddRefHelper(const_cast(aPtr)); + mozilla::RefPtrTraits::Type>::AddRef(const_cast(aPtr)); } static void Release(const U* aPtr) { - RefPtr::AddRefTraitsReleaseHelper(const_cast(aPtr)); + mozilla::RefPtrTraits::Type>::Release(const_cast(aPtr)); } }; }; diff --git a/mfbt/TypeTraits.h b/mfbt/TypeTraits.h index 2952b54389..f6fe92ee7a 100644 --- a/mfbt/TypeTraits.h +++ b/mfbt/TypeTraits.h @@ -355,6 +355,41 @@ struct IsArithmetic : IntegralConstant::value || IsFloatingPoint::value> {}; +namespace detail { + +template +struct IsMemberPointerHelper : FalseType {}; + +template +struct IsMemberPointerHelper : TrueType {}; + +} // namespace detail + +/** + * IsMemberPointer determines whether a type is pointer to non-static member + * object or a pointer to non-static member function. + * + * mozilla::IsMemberPointer::value is true + * mozilla::IsMemberPointer::value is false + */ +template +struct IsMemberPointer + : detail::IsMemberPointerHelper::Type> +{}; + +/** + * IsScalar determines whether a type is a scalar type. + * + * mozilla::IsScalar::value is true + * mozilla::IsScalar::value is true + * mozilla::IsScalar::value is false + */ +template +struct IsScalar + : IntegralConstant::value || IsEnum::value || + IsPointer::value || IsMemberPointer::value> +{}; + /* 20.9.4.3 Type properties [meta.unary.prop] */ /** @@ -644,18 +679,15 @@ struct IsBaseOf namespace detail { -// This belongs inside ConvertibleTester, but it's pulled out to -// work around a bug in the compiler used for hazard builds. -template -static void ConvertibleTestHelper(To); - template struct ConvertibleTester { private: - template(DeclVal()))> - static char test(int); + template + static char test_helper(To1); + + template + static decltype(test_helper(DeclVal())) test(int); template static int test(...); diff --git a/mfbt/UniquePtr.h b/mfbt/UniquePtr.h index 525426c352..9381cca5a4 100644 --- a/mfbt/UniquePtr.h +++ b/mfbt/UniquePtr.h @@ -226,7 +226,7 @@ public: } UniquePtr(UniquePtr&& aOther) - : mTuple(aOther.release(), Forward(aOther.getDeleter())) + : mTuple(aOther.release(), Forward(aOther.get_deleter())) {} MOZ_IMPLICIT @@ -247,7 +247,7 @@ public: ? IsSame::value : IsConvertible::value), int>::Type aDummy = 0) - : mTuple(aOther.release(), Forward(aOther.getDeleter())) + : mTuple(aOther.release(), Forward(aOther.get_deleter())) { } @@ -256,7 +256,7 @@ public: UniquePtr& operator=(UniquePtr&& aOther) { reset(aOther.release()); - getDeleter() = Forward(aOther.getDeleter()); + get_deleter() = Forward(aOther.get_deleter()); return *this; } @@ -270,7 +270,7 @@ public: "can't assign from UniquePtr holding an array"); reset(aOther.release()); - getDeleter() = Forward(aOther.getDeleter()); + get_deleter() = Forward(aOther.get_deleter()); return *this; } @@ -291,10 +291,10 @@ public: Pointer get() const { return ptr(); } - DeleterType& getDeleter() { return del(); } - const DeleterType& getDeleter() const { return del(); } + DeleterType& get_deleter() { return del(); } + const DeleterType& get_deleter() const { return del(); } - Pointer release() + MOZ_WARN_UNUSED_RESULT Pointer release() { Pointer p = ptr(); ptr() = nullptr; @@ -306,7 +306,7 @@ public: Pointer old = ptr(); ptr() = aPtr; if (old != nullptr) { - getDeleter()(old); + get_deleter()(old); } } @@ -395,7 +395,7 @@ public: = delete; UniquePtr(UniquePtr&& aOther) - : mTuple(aOther.release(), Forward(aOther.getDeleter())) + : mTuple(aOther.release(), Forward(aOther.get_deleter())) {} MOZ_IMPLICIT @@ -411,7 +411,7 @@ public: UniquePtr& operator=(UniquePtr&& aOther) { reset(aOther.release()); - getDeleter() = Forward(aOther.getDeleter()); + get_deleter() = Forward(aOther.get_deleter()); return *this; } @@ -426,10 +426,10 @@ public: T& operator[](decltype(sizeof(int)) aIndex) const { return get()[aIndex]; } Pointer get() const { return mTuple.first(); } - DeleterType& getDeleter() { return mTuple.second(); } - const DeleterType& getDeleter() const { return mTuple.second(); } + DeleterType& get_deleter() { return mTuple.second(); } + const DeleterType& get_deleter() const { return mTuple.second(); } - Pointer release() + MOZ_WARN_UNUSED_RESULT Pointer release() { Pointer p = mTuple.first(); mTuple.first() = nullptr; diff --git a/mfbt/Vector.h b/mfbt/Vector.h index 6652bc1d6d..4461242a94 100644 --- a/mfbt/Vector.h +++ b/mfbt/Vector.h @@ -281,8 +281,8 @@ class Vector final : private AllocPolicy friend struct detail::VectorTesting; - bool growStorageBy(size_t aIncr); - bool convertToHeapStorage(size_t aNewCap); + MOZ_WARN_UNUSED_RESULT bool growStorageBy(size_t aIncr); + MOZ_WARN_UNUSED_RESULT bool convertToHeapStorage(size_t aNewCap); /* magic constants */ @@ -522,7 +522,7 @@ public: * Given that the vector is empty and has no inline storage, grow to * |capacity|. */ - bool initCapacity(size_t aRequest); + MOZ_WARN_UNUSED_RESULT bool initCapacity(size_t aRequest); /** * If reserve(aRequest) succeeds and |aRequest >= length()|, then appending @@ -532,7 +532,7 @@ public: * A request to reserve an amount less than the current length does not affect * reserved space. */ - bool reserve(size_t aRequest); + MOZ_WARN_UNUSED_RESULT bool reserve(size_t aRequest); /** * Destroy elements in the range [end() - aIncr, end()). Does not deallocate @@ -547,18 +547,18 @@ public: void shrinkTo(size_t aNewLength); /** Grow the vector by aIncr elements. */ - bool growBy(size_t aIncr); + MOZ_WARN_UNUSED_RESULT bool growBy(size_t aIncr); /** Call shrinkBy or growBy based on whether newSize > length(). */ - bool resize(size_t aNewLength); + MOZ_WARN_UNUSED_RESULT bool resize(size_t aNewLength); /** * Increase the length of the vector, but don't initialize the new elements * -- leave them as uninitialized memory. */ - bool growByUninitialized(size_t aIncr); + MOZ_WARN_UNUSED_RESULT bool growByUninitialized(size_t aIncr); void infallibleGrowByUninitialized(size_t aIncr); - bool resizeUninitialized(size_t aNewLength); + MOZ_WARN_UNUSED_RESULT bool resizeUninitialized(size_t aNewLength); /** Shorthand for shrinkBy(length()). */ void clear(); @@ -581,13 +581,13 @@ public: * vector, instead of copying it. If it fails, |aU| is left unmoved. ("We are * not amused.") */ - template bool append(U&& aU); + template MOZ_WARN_UNUSED_RESULT bool append(U&& aU); /** * Construct a T in-place as a new entry at the end of this vector. */ template - bool emplaceBack(Args&&... aArgs) + MOZ_WARN_UNUSED_RESULT bool emplaceBack(Args&&... aArgs) { if (!growByUninitialized(1)) return false; @@ -596,10 +596,10 @@ public: } template - bool appendAll(const Vector& aU); - bool appendN(const T& aT, size_t aN); - template bool append(const U* aBegin, const U* aEnd); - template bool append(const U* aBegin, size_t aLength); + MOZ_WARN_UNUSED_RESULT bool appendAll(const Vector& aU); + MOZ_WARN_UNUSED_RESULT bool appendN(const T& aT, size_t aN); + template MOZ_WARN_UNUSED_RESULT bool append(const U* aBegin, const U* aEnd); + template MOZ_WARN_UNUSED_RESULT bool append(const U* aBegin, size_t aLength); /* * Guaranteed-infallible append operations for use upon vectors whose @@ -643,7 +643,7 @@ public: * * N.B. Although a T*, only the range [0, length()) is constructed. */ - T* extractRawBuffer(); + MOZ_WARN_UNUSED_RESULT T* extractRawBuffer(); /** * Transfer ownership of an array of objects into the vector. The caller @@ -671,7 +671,7 @@ public: * This is inherently a linear-time operation. Be careful! */ template - T* insert(T* aP, U&& aVal); + MOZ_WARN_UNUSED_RESULT T* insert(T* aP, U&& aVal); /** * Removes the element |aT|, which must fall in the bounds [begin, end), diff --git a/mfbt/tests/TestTypeTraits.cpp b/mfbt/tests/TestTypeTraits.cpp index 3dfedac82e..bad834055a 100644 --- a/mfbt/tests/TestTypeTraits.cpp +++ b/mfbt/tests/TestTypeTraits.cpp @@ -7,6 +7,12 @@ #include "mozilla/Assertions.h" #include "mozilla/TypeTraits.h" +#define TEST_CV_QUALIFIERS(test, type, ...) \ + test(type, __VA_ARGS__) \ + test(const type, __VA_ARGS__) \ + test(volatile type, __VA_ARGS__) \ + test(const volatile type, __VA_ARGS__) + using mozilla::AddLvalueReference; using mozilla::AddPointer; using mozilla::AddRvalueReference; @@ -137,6 +143,77 @@ static_assert(IsReference::value, static_assert(IsReference::value, "int&& is a reference"); +namespace CPlusPlus11IsMemberPointer { + +using mozilla::IsMemberPointer; + +struct S {}; +union U {}; + +#define ASSERT_IS_MEMBER_POINTER(type, msg) \ + static_assert(IsMemberPointer::value, #type msg); +#define TEST_IS_MEMBER_POINTER(type) \ + TEST_CV_QUALIFIERS(ASSERT_IS_MEMBER_POINTER, type, \ + " is a member pointer type") + +TEST_IS_MEMBER_POINTER(int S::*) +TEST_IS_MEMBER_POINTER(int U::*) + +#undef TEST_IS_MEMBER_POINTER +#undef ASSERT_IS_MEMBER_POINTER + +#define ASSERT_IS_NOT_MEMBER_POINTER(type, msg) \ + static_assert(!IsMemberPointer::value, #type msg); +#define TEST_IS_NOT_MEMBER_POINTER(type) \ + TEST_CV_QUALIFIERS(ASSERT_IS_NOT_MEMBER_POINTER, type, \ + " is not a member pointer type") + +TEST_IS_NOT_MEMBER_POINTER(int*) + +#undef TEST_IS_NOT_MEMBER_POINTER +#undef ASSERT_IS_NOT_MEMBER_POINTER + +} // CPlusPlus11IsMemberPointer + +namespace CPlusPlus11IsScalar { + +using mozilla::IsScalar; + +enum E {}; +enum class EC {}; +class C {}; +struct S {}; +union U {}; + +#define ASSERT_IS_SCALAR(type, msg) \ + static_assert(IsScalar::value, #type msg); +#define TEST_IS_SCALAR(type) \ + TEST_CV_QUALIFIERS(ASSERT_IS_SCALAR, type, " is a scalar type") + +TEST_IS_SCALAR(int) +TEST_IS_SCALAR(float) +TEST_IS_SCALAR(E) +TEST_IS_SCALAR(EC) +TEST_IS_SCALAR(S*) +TEST_IS_SCALAR(int S::*) + +#undef TEST_IS_SCALAR +#undef ASSERT_IS_SCALAR + +#define ASSERT_IS_NOT_SCALAR(type, msg) \ + static_assert(!IsScalar::value, #type msg); +#define TEST_IS_NOT_SCALAR(type) \ + TEST_CV_QUALIFIERS(ASSERT_IS_NOT_SCALAR, type, " is not a scalar type") + +TEST_IS_NOT_SCALAR(C) +TEST_IS_NOT_SCALAR(S) +TEST_IS_NOT_SCALAR(U) + +#undef TEST_IS_NOT_SCALAR +#undef ASSERT_IS_NOT_SCALAR + +} // CPlusPlus11IsScalar + struct S1 {}; union U1 { int mX; }; diff --git a/mfbt/tests/TestUniquePtr.cpp b/mfbt/tests/TestUniquePtr.cpp index 3455d64973..27d13e596c 100644 --- a/mfbt/tests/TestUniquePtr.cpp +++ b/mfbt/tests/TestUniquePtr.cpp @@ -292,7 +292,7 @@ TestReferenceDeleterGuts() IntDeleterRef id2(Move(id1)); CHECK(id1 == nullptr); CHECK(nullptr != id2); - CHECK(&id1.getDeleter() == &id2.getDeleter()); + CHECK(&id1.get_deleter() == &id2.get_deleter()); IntDeleterRef id3(Move(id2)); diff --git a/modules/libpref/greprefs.js b/modules/libpref/greprefs.js index 5f0b68c70d..d59110b6a6 100644 --- a/modules/libpref/greprefs.js +++ b/modules/libpref/greprefs.js @@ -1,12 +1,12 @@ #include ../../netwerk/base/security-prefs.js #include init/all.js #ifdef MOZ_DATA_REPORTING -#include ../../services/datareporting/datareporting-prefs.js +#include ../../toolkit/components/telemetry/datareporting-prefs.js #endif #ifdef MOZ_SERVICES_HEALTHREPORT #if MOZ_WIDGET_TOOLKIT == android #include ../../mobile/android/chrome/content/healthreport-prefs.js #else -#include ../../services/healthreport/healthreport-prefs.js +#include ../../toolkit/components/telemetry/healthreport-prefs.js #endif #endif diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index 422b88a5fc..8eb838074d 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -4672,17 +4672,23 @@ pref("layers.bufferrotation.enabled", true); pref("layers.componentalpha.enabled", true); +// Use the DT-backend implemented PushLayer +pref("gfx.content.use-native-pushlayer", false); + #ifdef ANDROID pref("gfx.apitrace.enabled",false); #endif #ifdef MOZ_X11 +pref("gfx.content.use-native-pushlayer", true); #ifdef MOZ_WIDGET_GTK pref("gfx.xrender.enabled",true); #endif #endif #ifdef XP_WIN +pref("gfx.content.use-native-pushlayer", true); + // Whether to disable the automatic detection and use of direct2d. pref("gfx.direct2d.disabled", false); pref("gfx.direct2d.use1_1", true); diff --git a/netwerk/system/linux/nsNotifyAddrListener_Linux.cpp b/netwerk/system/linux/nsNotifyAddrListener_Linux.cpp index b6c6c314b3..b6aace2aab 100644 --- a/netwerk/system/linux/nsNotifyAddrListener_Linux.cpp +++ b/netwerk/system/linux/nsNotifyAddrListener_Linux.cpp @@ -18,7 +18,6 @@ #include "nsServiceManagerUtils.h" #include "nsNotifyAddrListener_Linux.h" #include "nsString.h" -#include "nsAutoPtr.h" #include "mozilla/Logging.h" #include "mozilla/Services.h" @@ -36,6 +35,10 @@ /* a shorter name that better explains what it does */ #define EINTR_RETRY(x) MOZ_TEMP_FAILURE_RETRY(x) +// period during which to absorb subsequent network change events, in +// milliseconds +static const unsigned int kNetworkChangeCoalescingPeriod = 1000; + using namespace mozilla; static LazyLogModule gNotifyAddrLog("nsNotifyAddr"); @@ -53,6 +56,7 @@ nsNotifyAddrListener::nsNotifyAddrListener() , mStatusKnown(false) , mAllowChangedEvent(true) , mChildThreadShutdown(false) + , mCoalescingActive(false) { mShutdownPipe[0] = -1; mShutdownPipe[1] = -1; @@ -144,7 +148,6 @@ void nsNotifyAddrListener::checkLink(void) void nsNotifyAddrListener::OnNetlinkMessage(int aNetlinkSocket) { struct nlmsghdr *nlh; - struct rtmsg *route_entry; // The buffer size below, (4095) was chosen partly based on testing and // partly on existing sample source code using this size. It needs to be @@ -152,7 +155,8 @@ void nsNotifyAddrListener::OnNetlinkMessage(int aNetlinkSocket) char buffer[4095]; struct rtattr *attr; int attr_len; - bool link_local; + const struct ifaddrmsg* newifam; + ssize_t rc = EINTR_RETRY(recv(aNetlinkSocket, buffer, sizeof(buffer), 0)); if (rc < 0) { @@ -166,64 +170,85 @@ void nsNotifyAddrListener::OnNetlinkMessage(int aNetlinkSocket) for (; NLMSG_OK(nlh, netlink_bytes); nlh = NLMSG_NEXT(nlh, netlink_bytes)) { + char prefixaddr[INET6_ADDRSTRLEN]; + char localaddr[INET6_ADDRSTRLEN]; + char* addr = nullptr; + prefixaddr[0] = localaddr[0] = '\0'; if (NLMSG_DONE == nlh->nlmsg_type) { break; } - switch(nlh->nlmsg_type) { - case RTM_DELROUTE: - case RTM_NEWROUTE: - // Get the route data - route_entry = static_cast(NLMSG_DATA(nlh)); + LOG(("nsNotifyAddrListener::OnNetlinkMessage: new/deleted address\n")); + newifam = reinterpret_cast(NLMSG_DATA(nlh)); - // We are just intrested in main routing table - if (route_entry->rtm_table != RT_TABLE_MAIN) - continue; + if ((newifam->ifa_family != AF_INET) && + (newifam->ifa_family != AF_INET6)) { + continue; + } - if ((route_entry->rtm_family != AF_INET) && - (route_entry->rtm_family != AF_INET6)) { - continue; - } - - attr = (struct rtattr *) RTM_RTA(route_entry); - attr_len = RTM_PAYLOAD(nlh); - link_local = false; - - /* Loop through all attributes */ - for ( ; RTA_OK(attr, attr_len); attr = RTA_NEXT(attr, attr_len)) { - if (attr->rta_type == RTA_GATEWAY) { - if (route_entry->rtm_family == AF_INET6) { - unsigned char *g = (unsigned char *) - RTA_DATA(attr); - if ((g[0] == 0xFE) && ((g[1] & 0xc0) == 0x80)) { - link_local = true; - break; - } - } + attr = IFA_RTA (newifam); + attr_len = IFA_PAYLOAD (nlh); + for (;attr_len && RTA_OK (attr, attr_len); + attr = RTA_NEXT (attr, attr_len)) { + if (attr->rta_type == IFA_ADDRESS) { + if (newifam->ifa_family == AF_INET) { + struct in_addr* in = (struct in_addr*)RTA_DATA(attr); + inet_ntop(AF_INET, in, prefixaddr, INET_ADDRSTRLEN); + } else { + struct in6_addr* in = (struct in6_addr*)RTA_DATA(attr); + inet_ntop(AF_INET6, in, prefixaddr, INET6_ADDRSTRLEN); + } + } else if (attr->rta_type == IFA_LOCAL) { + if (newifam->ifa_family == AF_INET) { + struct in_addr* in = (struct in_addr*)RTA_DATA(attr); + inet_ntop(AF_INET, in, localaddr, INET_ADDRSTRLEN); + } else { + struct in6_addr* in = (struct in6_addr*)RTA_DATA(attr); + inet_ntop(AF_INET6, in, localaddr, INET6_ADDRSTRLEN); } } - - if (!link_local) { - LOG(("OnNetlinkMessage: route update\n")); - networkChange = true; - } else { - LOG(("OnNetlinkMessage: ignored link-local route update\n")); - } - break; - - case RTM_NEWADDR: - LOG(("OnNetlinkMessage: new address\n")); - networkChange = true; - break; - - default: + } + if (localaddr[0]) { + addr = localaddr; + } else if (prefixaddr[0]) { + addr = prefixaddr; + } else { continue; } + if (nlh->nlmsg_type == RTM_NEWADDR) { + LOG(("nsNotifyAddrListener::OnNetlinkMessage: a new address " + "- %s.", addr)); + struct ifaddrmsg* ifam; + nsCString addrStr; + addrStr.Assign(addr); + if (mAddressInfo.Get(addrStr, &ifam)) { + LOG(("nsNotifyAddrListener::OnNetlinkMessage: the address " + "already known.")); + if (memcmp(ifam, newifam, sizeof(struct ifaddrmsg))) { + LOG(("nsNotifyAddrListener::OnNetlinkMessage: but " + "the address info has been changed.")); + networkChange = true; + memcpy(ifam, newifam, sizeof(struct ifaddrmsg)); + } + } else { + networkChange = true; + ifam = (struct ifaddrmsg*)malloc(sizeof(struct ifaddrmsg)); + memcpy(ifam, newifam, sizeof(struct ifaddrmsg)); + mAddressInfo.Put(addrStr,ifam); + } + } else { + LOG(("nsNotifyAddrListener::OnNetlinkMessage: an address " + "has been deleted - %s.", addr)); + networkChange = true; + nsCString addrStr; + addrStr.Assign(addr); + mAddressInfo.Remove(addrStr); + } } if (networkChange && mAllowChangedEvent) { - SendEvent(NS_NETWORK_LINK_DATA_CHANGED); + NetworkChanged(); } if (networkChange) { @@ -243,8 +268,7 @@ nsNotifyAddrListener::Run() memset(&addr, 0, sizeof(addr)); // clear addr addr.nl_family = AF_NETLINK; - addr.nl_groups = RTMGRP_IPV4_ROUTE | RTMGRP_IPV4_IFADDR | - RTMGRP_IPV6_IFADDR | RTMGRP_IPV6_ROUTE; + addr.nl_groups = RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR; if (bind(netlinkSocket, (struct sockaddr *)&addr, sizeof(addr)) < 0) { // failure! @@ -275,8 +299,9 @@ nsNotifyAddrListener::Run() nsresult rv = NS_OK; bool shutdown = false; + int pollWait = pollTimeout; while (!shutdown) { - int rc = EINTR_RETRY(poll(fds, 2, pollTimeout)); + int rc = EINTR_RETRY(poll(fds, 2, pollWait)); if (rc > 0) { if (fds[0].revents & POLLIN) { @@ -291,6 +316,19 @@ nsNotifyAddrListener::Run() rv = NS_ERROR_FAILURE; break; } + if (mCoalescingActive) { + // check if coalescing period should continue + double period = (TimeStamp::Now() - mChangeTime).ToMilliseconds(); + if (period >= kNetworkChangeCoalescingPeriod) { + SendEvent(NS_NETWORK_LINK_DATA_CHANGED); + mCoalescingActive = false; + pollWait = pollTimeout; // restore to default + } else { + // wait no longer than to the end of the period + pollWait = static_cast + (kNetworkChangeCoalescingPeriod - period); + } + } if (mChildThreadShutdown) { LOG(("thread shutdown via variable, dying...\n")); shutdown = true; @@ -384,6 +422,27 @@ nsNotifyAddrListener::Shutdown(void) return rv; } + +/* + * A network event has been registered. Delay the actual sending of the event + * for a while and absorb subsequent events in the mean time in an effort to + * squash potentially many triggers into a single event. + * Only ever called from the same thread. + */ +nsresult +nsNotifyAddrListener::NetworkChanged() +{ + if (mCoalescingActive) { + LOG(("NetworkChanged: absorbed an event (coalescing active)\n")); + } else { + // A fresh trigger! + mChangeTime = TimeStamp::Now(); + mCoalescingActive = true; + LOG(("NetworkChanged: coalescing period started\n")); + } + return NS_OK; +} + /* Sends the given event. Assumes aEventID never goes out of scope (static * strings are ideal). */ diff --git a/netwerk/system/linux/nsNotifyAddrListener_Linux.h b/netwerk/system/linux/nsNotifyAddrListener_Linux.h index 51c5408049..1d213c1c3d 100644 --- a/netwerk/system/linux/nsNotifyAddrListener_Linux.h +++ b/netwerk/system/linux/nsNotifyAddrListener_Linux.h @@ -22,6 +22,8 @@ #include "nsCOMPtr.h" #include "mozilla/Atomics.h" #include "mozilla/TimeStamp.h" +#include "nsITimer.h" +#include "nsClassHashtable.h" class nsNotifyAddrListener : public nsINetworkLinkService, public nsIRunnable, @@ -53,6 +55,9 @@ private: // Called when xpcom-shutdown-threads is received. nsresult Shutdown(void); + // Called when a network change was detected + nsresult NetworkChanged(); + // Sends the network event. nsresult SendEvent(const char *aEventID); @@ -78,6 +83,17 @@ private: // Flag to signal child thread kill with mozilla::Atomic mChildThreadShutdown; -}; + + // Flag set while coalescing change events + bool mCoalescingActive; + + // Time stamp for first event during coalescing + mozilla::TimeStamp mChangeTime; + + // Seen Ip addresses. For Ipv6 addresses some time router renews their + // lifetime and we should not detect this as a network link change, so we + // keep info about all seen addresses. + nsClassHashtable mAddressInfo; + }; #endif /* NSNOTIFYADDRLISTENER_LINUX_H_ */ diff --git a/services/common/bagheeraclient.js b/services/common/bagheeraclient.js deleted file mode 100644 index c678b4cf7b..0000000000 --- a/services/common/bagheeraclient.js +++ /dev/null @@ -1,281 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/** - * This file contains a client API for the Bagheera data storage service. - * - * Information about Bagheera is available at - * https://github.com/mozilla-metrics/bagheera - */ - -#ifndef MERGED_COMPARTMENT - -"use strict"; - -this.EXPORTED_SYMBOLS = [ - "BagheeraClient", - "BagheeraClientRequestResult", -]; - -var {classes: Cc, interfaces: Ci, utils: Cu} = Components; - -#endif - -Cu.import("resource://gre/modules/Promise.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://gre/modules/Log.jsm"); -Cu.import("resource://services-common/rest.js"); -Cu.import("resource://services-common/utils.js"); - - -/** - * Represents the result of a Bagheera request. - */ -this.BagheeraClientRequestResult = function BagheeraClientRequestResult() { - this.transportSuccess = false; - this.serverSuccess = false; - this.request = null; -}; - -Object.freeze(BagheeraClientRequestResult.prototype); - - -/** - * Wrapper around RESTRequest so logging is sane. - */ -function BagheeraRequest(uri) { - RESTRequest.call(this, uri); - - this._log = Log.repository.getLogger("Services.BagheeraClient"); - this._log.level = Log.Level.Debug; -} - -BagheeraRequest.prototype = Object.freeze({ - __proto__: RESTRequest.prototype, -}); - - -/** - * Create a new Bagheera client instance. - * - * Each client is associated with a specific Bagheera HTTP URI endpoint. - * - * @param baseURI - * (string) The base URI of the Bagheera HTTP endpoint. - */ -this.BagheeraClient = function BagheeraClient(baseURI) { - if (!baseURI) { - throw new Error("baseURI argument must be defined."); - } - - this._log = Log.repository.getLogger("Services.BagheeraClient"); - this._log.level = Log.Level.Debug; - - this.baseURI = baseURI; - - if (!baseURI.endsWith("/")) { - this.baseURI += "/"; - } -}; - -BagheeraClient.prototype = Object.freeze({ - /** - * Channel load flags for all requests. - * - * Caching is not applicable, so we bypass and disable it. We also - * ignore any cookies that may be present for the domain because - * Bagheera does not utilize cookies and the release of cookies may - * inadvertantly constitute unncessary information disclosure. - */ - _loadFlags: Ci.nsIRequest.LOAD_BYPASS_CACHE | - Ci.nsIRequest.INHIBIT_CACHING | - Ci.nsIRequest.LOAD_ANONYMOUS, - - DEFAULT_TIMEOUT_MSEC: 5 * 60 * 1000, // 5 minutes. - - _RE_URI_IDENTIFIER: /^[a-zA-Z0-9_-]+$/, - - /** - * Upload a JSON payload to the server. - * - * The return value is a Promise which will be resolved with a - * BagheeraClientRequestResult when the request has finished. - * - * @param namespace - * (string) The namespace to post this data to. - * @param id - * (string) The ID of the document being uploaded. This is typically - * a UUID in hex form. - * @param payload - * (string|object) Data to upload. Can be specified as a string (which - * is assumed to be JSON) or an object. If an object, it will be fed into - * JSON.stringify() for serialization. - * @param options - * (object) Extra options to control behavior. Recognized properties: - * - * deleteIDs -- (array) Old document IDs to delete as part of - * upload. If not specified, no old documents will be deleted as - * part of upload. The array values are typically UUIDs in hex - * form. - * - * telemetryCompressed -- (string) Telemetry histogram to record - * compressed size of payload under. If not defined, no telemetry - * data for the compressed size will be recorded. - * - * @return Promise - */ - uploadJSON: function uploadJSON(namespace, id, payload, options={}) { - if (!namespace) { - throw new Error("namespace argument must be defined."); - } - - if (!id) { - throw new Error("id argument must be defined."); - } - - if (!payload) { - throw new Error("payload argument must be defined."); - } - - if (options && typeof(options) != "object") { - throw new Error("Unexpected type for options argument. Expected object. " + - "Got: " + typeof(options)); - } - - let uri = this._submitURI(namespace, id); - - let data = payload; - - if (typeof(payload) == "object") { - data = JSON.stringify(payload); - } - - if (typeof(data) != "string") { - throw new Error("Unknown type for payload: " + typeof(data)); - } - - this._log.info("Uploading data to " + uri); - - let request = new BagheeraRequest(uri); - request.loadFlags = this._loadFlags; - request.timeout = this.DEFAULT_TIMEOUT_MSEC; - - // Since API changed, throw on old API usage. - if ("deleteID" in options) { - throw new Error("API has changed, use (array) deleteIDs instead"); - } - - let deleteIDs; - if (options.deleteIDs && options.deleteIDs.length > 0) { - deleteIDs = options.deleteIDs; - this._log.debug("Will delete " + deleteIDs.join(", ")); - request.setHeader("X-Obsolete-Document", deleteIDs.join(",")); - } - - let deferred = Promise.defer(); - - // The string converter service used by CommonUtils.convertString() - // silently throws away high bytes. We need to convert the string to - // consist of only low bytes first. - data = CommonUtils.encodeUTF8(data); - data = CommonUtils.convertString(data, "uncompressed", "deflate"); - if (options.telemetryCompressed) { - try { - let h = Services.telemetry.getHistogramById(options.telemetryCompressed); - h.add(data.length); - } catch (ex) { - this._log.warn("Unable to record telemetry for compressed payload size: " + - CommonUtils.exceptionStr(ex)); - } - } - - // TODO proper header per bug 807134. - request.setHeader("Content-Type", "application/json+zlib; charset=utf-8"); - - this._log.info("Request body length: " + data.length); - - let result = new BagheeraClientRequestResult(); - result.namespace = namespace; - result.id = id; - result.deleteIDs = deleteIDs ? deleteIDs.slice(0) : null; - - request.onComplete = this._onComplete.bind(this, request, deferred, result); - request.post(data); - - return deferred.promise; - }, - - /** - * Delete the specified document. - * - * @param namespace - * (string) Namespace from which to delete the document. - * @param id - * (string) ID of document to delete. - * - * @return Promise - */ - deleteDocument: function deleteDocument(namespace, id) { - let uri = this._submitURI(namespace, id); - - let request = new BagheeraRequest(uri); - request.loadFlags = this._loadFlags; - request.timeout = this.DEFAULT_TIMEOUT_MSEC; - - let result = new BagheeraClientRequestResult(); - result.namespace = namespace; - result.id = id; - let deferred = Promise.defer(); - - request.onComplete = this._onComplete.bind(this, request, deferred, result); - request.delete(); - - return deferred.promise; - }, - - _submitURI: function _submitURI(namespace, id) { - if (!this._RE_URI_IDENTIFIER.test(namespace)) { - throw new Error("Illegal namespace name. Must be alphanumeric + [_-]: " + - namespace); - } - - if (!this._RE_URI_IDENTIFIER.test(id)) { - throw new Error("Illegal id value. Must be alphanumeric + [_-]: " + id); - } - - return this.baseURI + "1.0/submit/" + namespace + "/" + id; - }, - - _onComplete: function _onComplete(request, deferred, result, error) { - result.request = request; - - if (error) { - this._log.info("Transport failure on request: " + - CommonUtils.exceptionStr(error)); - result.transportSuccess = false; - deferred.resolve(result); - return; - } - - result.transportSuccess = true; - - let response = request.response; - - switch (response.status) { - case 200: - case 201: - result.serverSuccess = true; - break; - - default: - result.serverSuccess = false; - - this._log.info("Received unexpected status code: " + response.status); - this._log.debug("Response body: " + response.body); - } - - deferred.resolve(result); - }, -}); - diff --git a/services/common/modules-testing/bagheeraserver.js b/services/common/modules-testing/bagheeraserver.js deleted file mode 100644 index 81328b40bf..0000000000 --- a/services/common/modules-testing/bagheeraserver.js +++ /dev/null @@ -1,298 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -"use strict"; - -var {utils: Cu} = Components; - -this.EXPORTED_SYMBOLS = ["BagheeraServer"]; - -Cu.import("resource://gre/modules/Log.jsm"); -Cu.import("resource://services-common/utils.js"); -Cu.import("resource://testing-common/httpd.js"); - - -/** - * This is an implementation of the Bagheera server. - * - * The purpose of the server is to facilitate testing of the Bagheera - * client and the Firefox Health report. It is *not* meant to be a - * production grade server. - * - * The Bagheera server is essentially a glorified document store. - */ -this.BagheeraServer = function BagheeraServer() { - this._log = Log.repository.getLogger("metrics.BagheeraServer"); - - this.server = new HttpServer(); - this.namespaces = {}; - - this.allowAllNamespaces = false; -} - -BagheeraServer.prototype = { - /** - * Whether this server has a namespace defined. - * - * @param ns - * (string) Namepsace whose existence to query for. - * @return bool - */ - hasNamespace: function hasNamespace(ns) { - return ns in this.namespaces; - }, - - /** - * Whether this server has an ID in a particular namespace. - * - * @param ns - * (string) Namespace to look for item in. - * @param id - * (string) ID of object to look for. - * @return bool - */ - hasDocument: function hasDocument(ns, id) { - let namespace = this.namespaces[ns]; - - if (!namespace) { - return false; - } - - return id in namespace; - }, - - /** - * Obtain a document from the server. - * - * @param ns - * (string) Namespace to retrieve document from. - * @param id - * (string) ID of document to retrieve. - * - * @return string The content of the document or null if the document - * does not exist. - */ - getDocument: function getDocument(ns, id) { - let namespace = this.namespaces[ns]; - - if (!namespace) { - return null; - } - - return namespace[id]; - }, - - /** - * Set the contents of a document in the server. - * - * @param ns - * (string) Namespace to add document to. - * @param id - * (string) ID of document being added. - * @param payload - * (string) The content of the document. - */ - setDocument: function setDocument(ns, id, payload) { - let namespace = this.namespaces[ns]; - - if (!namespace) { - if (!this.allowAllNamespaces) { - throw new Error("Namespace does not exist: " + ns); - } - - this.createNamespace(ns); - namespace = this.namespaces[ns]; - } - - namespace[id] = payload; - }, - - /** - * Create a namespace in the server. - * - * The namespace will initially be empty. - * - * @param ns - * (string) The name of the namespace to create. - */ - createNamespace: function createNamespace(ns) { - if (ns in this.namespaces) { - throw new Error("Namespace already exists: " + ns); - } - - this.namespaces[ns] = {}; - }, - - start: function start(port=-1) { - this.server.registerPrefixHandler("/", this._handleRequest.bind(this)); - this.server.start(port); - let i = this.server.identity; - - this.serverURI = i.primaryScheme + "://" + i.primaryHost + ":" + - i.primaryPort + "/"; - this.port = i.primaryPort; - }, - - stop: function stop(cb) { - let handler = {onStopped: cb}; - - this.server.stop(handler); - }, - - /** - * Our root path handler. - */ - _handleRequest: function _handleRequest(request, response) { - let path = request.path; - this._log.info("Received request: " + request.method + " " + path + " " + - "HTTP/" + request.httpVersion); - - try { - if (path.startsWith("/1.0/submit/")) { - return this._handleV1Submit(request, response, - path.substr("/1.0/submit/".length)); - } else { - throw HTTP_404; - } - } catch (ex) { - if (ex instanceof HttpError) { - this._log.info("HttpError thrown: " + ex.code + " " + ex.description); - } else { - this._log.warn("Exception processing request: " + - CommonUtils.exceptionStr(ex)); - } - - throw ex; - } - }, - - /** - * Handles requests to /submit/*. - */ - _handleV1Submit: function _handleV1Submit(request, response, rest) { - if (!rest.length) { - throw HTTP_404; - } - - let namespace; - let index = rest.indexOf("/"); - if (index == -1) { - namespace = rest; - rest = ""; - } else { - namespace = rest.substr(0, index); - rest = rest.substr(index + 1); - } - - this._handleNamespaceSubmit(namespace, rest, request, response); - }, - - _handleNamespaceSubmit: function _handleNamespaceSubmit(namespace, rest, - request, response) { - if (!this.hasNamespace(namespace)) { - if (!this.allowAllNamespaces) { - this._log.info("Request to unknown namespace: " + namespace); - throw HTTP_404; - } - - this.createNamespace(namespace); - } - - if (!rest) { - this._log.info("No ID defined."); - throw HTTP_404; - } - - let id = rest; - if (id.includes("/")) { - this._log.info("URI has too many components."); - throw HTTP_404; - } - - if (request.method == "POST") { - return this._handleNamespaceSubmitPost(namespace, id, request, response); - } - - if (request.method == "DELETE") { - return this._handleNamespaceSubmitDelete(namespace, id, request, response); - } - - this._log.info("Unsupported HTTP method on namespace handler: " + - request.method); - response.setHeader("Allow", "POST,DELETE"); - throw HTTP_405; - }, - - _handleNamespaceSubmitPost: - function _handleNamespaceSubmitPost(namespace, id, request, response) { - - this._log.info("Handling data upload for " + namespace + ":" + id); - - let requestBody = CommonUtils.readBytesFromInputStream(request.bodyInputStream); - this._log.info("Raw body length: " + requestBody.length); - - if (!request.hasHeader("Content-Type")) { - this._log.info("Request does not have Content-Type header."); - throw HTTP_400; - } - - const ALLOWED_TYPES = [ - // TODO proper content types from bug 807134. - "application/json; charset=utf-8", - "application/json+zlib; charset=utf-8", - ]; - - let ct = request.getHeader("Content-Type"); - if (ALLOWED_TYPES.indexOf(ct) == -1) { - this._log.info("Unknown media type: " + ct); - // Should generate proper HTTP response headers for this error. - throw HTTP_415; - } - - if (ct.startsWith("application/json+zlib")) { - this._log.debug("Uncompressing entity body with deflate."); - requestBody = CommonUtils.convertString(requestBody, "deflate", - "uncompressed"); - } - - requestBody = CommonUtils.decodeUTF8(requestBody); - - this._log.debug("HTTP request body: " + requestBody); - - let doc; - try { - doc = JSON.parse(requestBody); - } catch(ex) { - this._log.info("JSON parse error."); - throw HTTP_400; - } - - this.namespaces[namespace][id] = doc; - - if (request.hasHeader("X-Obsolete-Document")) { - let obsolete = request.getHeader("X-Obsolete-Document"); - this._log.info("Deleting from X-Obsolete-Document header: " + obsolete); - for (let obsolete_id of obsolete.split(",")) { - delete this.namespaces[namespace][obsolete_id]; - } - } - - response.setStatusLine(request.httpVersion, 201, "Created"); - response.setHeader("Content-Type", "text/plain"); - - let body = id; - response.bodyOutputStream.write(body, body.length); - }, - - _handleNamespaceSubmitDelete: - function _handleNamespaceSubmitDelete(namespace, id, request, response) { - - delete this.namespaces[namespace][id]; - - let body = id; - response.bodyOutputStream.write(body, body.length); - }, -}; - -Object.freeze(BagheeraServer.prototype); diff --git a/services/common/moz.build b/services/common/moz.build index 41c855d1e2..e1737935cd 100644 --- a/services/common/moz.build +++ b/services/common/moz.build @@ -24,15 +24,12 @@ EXTRA_JS_MODULES['services-common'] += [ EXTRA_PP_JS_MODULES['services-common'] += [ 'async.js', - 'bagheeraclient.js', 'observers.js', 'rest.js', ] TESTING_JS_MODULES.services.common += [ - 'modules-testing/bagheeraserver.js', 'modules-testing/logging.js', - 'modules-testing/storageserver.js', 'modules-testing/utils.js', ] diff --git a/services/common/tests/mach_commands.py b/services/common/tests/mach_commands.py index 6777b81e4c..b57fa3aa26 100644 --- a/services/common/tests/mach_commands.py +++ b/services/common/tests/mach_commands.py @@ -34,7 +34,6 @@ DEFAULT_HOSTNAME = 'localhost' SRCDIR = mozpath.abspath(mozpath.dirname(__file__)) STORAGE_SERVER_SCRIPT = mozpath.join(SRCDIR, 'run_storage_server.js') -BAGHEERA_SERVER_SCRIPT = mozpath.join(SRCDIR, 'run_bagheera_server.js') def SyncStorageCommand(func): """Decorator that adds shared command arguments to services commands.""" @@ -110,8 +109,3 @@ class SyncTestCommands(MachCommandBase): @SyncStorageCommand def run_storage_server(self, port=DEFAULT_PORT, address=DEFAULT_HOSTNAME): exit(self.run_server(STORAGE_SERVER_SCRIPT, address, port)) - - @Command('bagheera-server', category='services', - description='Run a bagheera server.') - def run_bagheera_server(self, port=DEFAULT_PORT, address=DEFAULT_HOSTNAME): - exit(self.run_server(BAGHEERA_SERVER_SCRIPT, address, port)) diff --git a/services/common/tests/run_bagheera_server.js b/services/common/tests/run_bagheera_server.js deleted file mode 100644 index a97eb11eab..0000000000 --- a/services/common/tests/run_bagheera_server.js +++ /dev/null @@ -1,26 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/** - * This file runs a stub Bagheera server. - * - * It is meant to be executed with an xpcshell. - * - * The Makefile in this directory contains a target to run it: - * - * $ make bagheera-server - */ - -Cu.import("resource://testing-common/services/common/bagheeraserver.js"); - -initTestLogging(); - -var server = new BagheeraServer(); -server.allowAllNamespaces = true; -server.start(SERVER_PORT); -_("Bagheera server started on port " + SERVER_PORT); - -// Launch the thread manager. -_do_main(); - diff --git a/services/common/tests/unit/test_bagheera_client.js b/services/common/tests/unit/test_bagheera_client.js deleted file mode 100644 index 2cd65f5c1d..0000000000 --- a/services/common/tests/unit/test_bagheera_client.js +++ /dev/null @@ -1,161 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -"use strict"; - -Cu.import("resource://services-common/bagheeraclient.js"); -Cu.import("resource://services-common/rest.js"); -Cu.import("resource://testing-common/services/common/bagheeraserver.js"); -Cu.import("resource://gre/modules/Promise.jsm"); -Cu.import("resource://gre/modules/Task.jsm"); - -function getClientAndServer() { - let server = new BagheeraServer(); - server.start(); - - let client = new BagheeraClient(server.serverURI); - - return [client, server]; -} - -function run_test() { - initTestLogging("Trace"); - run_next_test(); -} - -add_test(function test_constructor() { - let client = new BagheeraClient("http://localhost:1234/"); - - run_next_test(); -}); - -add_test(function test_post_json_transport_failure() { - let client = new BagheeraClient("http://localhost:1234/"); - - client.uploadJSON("foo", "bar", {}).then(function onResult(result) { - do_check_false(result.transportSuccess); - - run_next_test(); - }); -}); - -add_test(function test_post_json_simple() { - let [client, server] = getClientAndServer(); - - server.createNamespace("foo"); - let promise = client.uploadJSON("foo", "bar", {foo: "bar", biz: "baz"}); - - promise.then(function onSuccess(result) { - do_check_true(result instanceof BagheeraClientRequestResult); - do_check_true(result.request instanceof RESTRequest); - do_check_true(result.transportSuccess); - do_check_true(result.serverSuccess); - - server.stop(run_next_test); - }, do_check_null); -}); - -add_test(function test_post_json_bad_data() { - let [client, server] = getClientAndServer(); - - server.createNamespace("foo"); - - client.uploadJSON("foo", "bar", "{this is invalid json}").then( - function onResult(result) { - do_check_true(result.transportSuccess); - do_check_false(result.serverSuccess); - - server.stop(run_next_test); - }); -}); - -add_task(function* test_unicode_payload() { - let [client, server] = getClientAndServer(); - server.createNamespace("foo"); - - const EXPECTED = "πόλλ' οἶδ' ἀλώπηξ, ἀλλ' ἐχῖνος ἓν μέγα"; - - let result = yield client.uploadJSON("foo", "bar", {test: EXPECTED}); - Assert.ok(result.transportSuccess); - Assert.ok(result.serverSuccess); - - let p = server.getDocument("foo", "bar"); - Assert.equal(p.test, EXPECTED); - - result = yield client.uploadJSON("foo", "baz", JSON.stringify({test: EXPECTED})); - Assert.ok(result.transportSuccess); - Assert.ok(result.serverSuccess); - p = server.getDocument("foo", "baz"); - Assert.equal(p.test, EXPECTED); - - let deferred = Promise.defer(); - server.stop(() => deferred.resolve()); - yield deferred.promise; -}); - -add_task(function test_post_delete_multiple_obsolete_documents () { - let [client, server] = getClientAndServer(); - let namespace = "foo"; - let documents = [ - [namespace, "one", "{v:1}"], - [namespace, "two", "{v:2}"], - [namespace, "three", "{v:3}"], - [namespace, "four", "{v:4}"], - ]; - - try { - // create initial documents - server.createNamespace(namespace); - for (let [ns, id, payload] of documents) { - server.setDocument(ns, id, payload); - do_check_true(server.hasDocument(ns, id)); - } - - // Test uploading with deleting some documents. - let deleteIDs = [0, 1].map((no) => { return documents[no][1]; }); - let result = yield client.uploadJSON(namespace, "new-1", {foo: "bar"}, {deleteIDs: deleteIDs}); - do_check_true(result.transportSuccess); - do_check_true(result.serverSuccess); - do_check_true(server.hasDocument(namespace, "new-1")); - for (let id of deleteIDs) { - do_check_false(server.hasDocument(namespace, id)); - } - // Check if the documents that were not staged for deletion are still there. - for (let [,id,] of documents) { - if (deleteIDs.indexOf(id) == -1) { - do_check_true(server.hasDocument(namespace, id)); - } - } - - // Test upload without deleting documents. - let ids = Object.keys(server.namespaces[namespace]); - result = yield client.uploadJSON(namespace, "new-2", {foo: "bar"}); - do_check_true(result.transportSuccess); - do_check_true(result.serverSuccess); - do_check_true(server.hasDocument(namespace, "new-2")); - // Check to see if all the original documents are still there. - for (let id of ids) { - do_check_true(deleteIDs.indexOf(id) !== -1 || server.hasDocument(namespace, id)); - } - } finally { - let deferred = Promise.defer(); - server.stop(deferred.resolve.bind(deferred)); - yield deferred.promise; - } -}); - -add_test(function test_delete_document() { - let [client, server] = getClientAndServer(); - - server.createNamespace("foo"); - server.setDocument("foo", "bar", "{}"); - - client.deleteDocument("foo", "bar").then(function onResult(result) { - do_check_true(result.transportSuccess); - do_check_true(result.serverSuccess); - - do_check_null(server.getDocument("foo", "bar")); - - server.stop(run_next_test); - }); -}); diff --git a/services/common/tests/unit/test_bagheera_server.js b/services/common/tests/unit/test_bagheera_server.js deleted file mode 100644 index 52d234b7c8..0000000000 --- a/services/common/tests/unit/test_bagheera_server.js +++ /dev/null @@ -1,30 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -"use strict"; - -Cu.import("resource://testing-common/services/common/bagheeraserver.js"); - -function run_test() { - run_next_test(); -} - -add_test(function test_server_empty() { - let server = new BagheeraServer(); - - do_check_false(server.hasNamespace("foo")); - do_check_false(server.hasDocument("foo", "bar")); - do_check_null(server.getDocument("foo", "bar")); - - server.createNamespace("foo"); - do_check_true(server.hasNamespace("foo")); - - run_next_test(); -}); - -add_test(function test_server_start() { - let server = new BagheeraServer(); - server.start(); - server.stop(run_next_test); -}); - diff --git a/services/common/tests/unit/xpcshell.ini b/services/common/tests/unit/xpcshell.ini index 083ec60a6a..7229596fb7 100644 --- a/services/common/tests/unit/xpcshell.ini +++ b/services/common/tests/unit/xpcshell.ini @@ -25,8 +25,7 @@ skip-if = toolkit == 'gonk' [test_async_chain.js] [test_async_querySpinningly.js] -[test_bagheera_server.js] -[test_bagheera_client.js] + [test_hawkclient.js] [test_hawkrequest.js] [test_logmanager.js] diff --git a/services/datareporting/DataReporting.manifest b/services/datareporting/DataReporting.manifest deleted file mode 100644 index 112443cc1a..0000000000 --- a/services/datareporting/DataReporting.manifest +++ /dev/null @@ -1,15 +0,0 @@ -# b2g: {3c2e2abc-06d4-11e1-ac3b-374f68613e61} -# browser: {8de7fcbb-c55c-4fbe-bfc5-fc555c87dbc4} -# mobile/android: {aa3c5121-dab2-40e2-81ca-7ea25febc110} -# mobile/xul: {a23983c0-fd0e-11dc-95ff-0800200c9a66} -# suite (comm): {92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a} - -# The Data Reporting Service drives collection and submission of metrics -# and other useful data to Mozilla. It drives the display of the data -# submission notification info bar and thus is required by Firefox Health -# Report and Telemetry. - -component {41f6ae36-a79f-4613-9ac3-915e70f83789} DataReportingService.js -contract @mozilla.org/datareporting/service;1 {41f6ae36-a79f-4613-9ac3-915e70f83789} -category app-startup DataReportingService service,@mozilla.org/datareporting/service;1 application={3c2e2abc-06d4-11e1-ac3b-374f68613e61} application={8de7fcbb-c55c-4fbe-bfc5-fc555c87dbc4} application={aa3c5121-dab2-40e2-81ca-7ea25febc110} application={a23983c0-fd0e-11dc-95ff-0800200c9a66} - diff --git a/services/datareporting/DataReportingService.js b/services/datareporting/DataReportingService.js deleted file mode 100644 index 826a28cc5b..0000000000 --- a/services/datareporting/DataReportingService.js +++ /dev/null @@ -1,437 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -"use strict"; - -const {classes: Cc, interfaces: Ci, utils: Cu} = Components; - -Cu.import("resource://gre/modules/Preferences.jsm"); -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource://services-common/utils.js"); -Cu.import("resource://gre/modules/Promise.jsm"); -Cu.import("resource://gre/modules/Task.jsm"); -Cu.import("resource://gre/modules/osfile.jsm"); - - -const ROOT_BRANCH = "datareporting."; -const POLICY_BRANCH = ROOT_BRANCH + "policy."; -const SESSIONS_BRANCH = ROOT_BRANCH + "sessions."; -const HEALTHREPORT_BRANCH = ROOT_BRANCH + "healthreport."; -const HEALTHREPORT_LOGGING_BRANCH = HEALTHREPORT_BRANCH + "logging."; -const DEFAULT_LOAD_DELAY_MSEC = 10 * 1000; -const DEFAULT_LOAD_DELAY_FIRST_RUN_MSEC = 60 * 1000; - -/** - * The Firefox Health Report XPCOM service. - * - * External consumers will be interested in the "reporter" property of this - * service. This property is a `HealthReporter` instance that powers the - * service. The property may be null if the Health Report service is not - * enabled. - * - * EXAMPLE USAGE - * ============= - * - * let reporter = Cc["@mozilla.org/datareporting/service;1"] - * .getService(Ci.nsISupports) - * .wrappedJSObject - * .healthReporter; - * - * if (reporter.haveRemoteData) { - * // ... - * } - * - * IMPLEMENTATION NOTES - * ==================== - * - * In order to not adversely impact application start time, the `HealthReporter` - * instance is not initialized until a few seconds after "final-ui-startup." - * The exact delay is configurable via preferences so it can be adjusted with - * a hotfix extension if the default value is ever problematic. Because of the - * overhead with the initial creation of the database, the first run is delayed - * even more than subsequent runs. This does mean that the first moments of - * browser activity may be lost by FHR. - * - * Shutdown of the `HealthReporter` instance is handled completely within the - * instance (it registers observers on initialization). See the notes on that - * type for more. - */ -this.DataReportingService = function () { - this.wrappedJSObject = this; - - this._quitting = false; - - this._os = Cc["@mozilla.org/observer-service;1"] - .getService(Ci.nsIObserverService); - - this._clientID = null; - this._loadClientIdTask = null; - this._saveClientIdTask = null; - - this._stateDir = null; - this._stateFilePath = null; - - // Used for testing only, when true results in getSessionRecorder() returning - // undefined. Controlled via simulate* methods. - this._simulateNoSessionRecorder = false; -} - -DataReportingService.prototype = Object.freeze({ - classID: Components.ID("{41f6ae36-a79f-4613-9ac3-915e70f83789}"), - - QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver, - Ci.nsISupportsWeakReference]), - - //--------------------------------------------- - // Start of policy listeners. - //--------------------------------------------- - - /** - * Called when policy requests data upload. - */ - onRequestDataUpload: function (request) { - if (!this.healthReporter) { - return; - } - - this.healthReporter.requestDataUpload(request); - }, - - onNotifyDataPolicy: function (request) { - Observers.notify("datareporting:notify-data-policy:request", request); - }, - - onRequestRemoteDelete: function (request) { - if (!this.healthReporter) { - return; - } - - this.healthReporter.deleteRemoteData(request); - }, - - //--------------------------------------------- - // End of policy listeners. - //--------------------------------------------- - - observe: function observe(subject, topic, data) { - switch (topic) { - case "app-startup": - this._os.addObserver(this, "profile-after-change", true); - break; - - case "profile-after-change": - this._os.removeObserver(this, "profile-after-change"); - - try { - this._prefs = new Preferences(HEALTHREPORT_BRANCH); - - // We don't initialize the sessions recorder unless Health Report is - // around to provide pruning of data. - // - // FUTURE consider having the SessionsRecorder always enabled and/or - // living in its own XPCOM service. - if (this._prefs.get("service.enabled", true)) { - this.sessionRecorder = new SessionRecorder(SESSIONS_BRANCH); - this.sessionRecorder.onStartup(); - } - - // We can't interact with prefs until after the profile is present. - let policyPrefs = new Preferences(POLICY_BRANCH); - this.policy = new DataReportingPolicy(policyPrefs, this._prefs, this); - - this._stateDir = OS.Path.join(OS.Constants.Path.profileDir, "datareporting"); - this._stateFilePath = OS.Path.join(this._stateDir, "state.json"); - - this._os.addObserver(this, "sessionstore-windows-restored", true); - } catch (ex) { - Cu.reportError("Exception when initializing data reporting service: " + - CommonUtils.exceptionStr(ex)); - } - break; - - case "sessionstore-windows-restored": - this._os.removeObserver(this, "sessionstore-windows-restored"); - this._os.addObserver(this, "quit-application", false); - - let policy = this.policy; - policy.startPolling(); - - // Don't initialize Firefox Health Reporter collection and submission - // service unless it is enabled. - if (!this._prefs.get("service.enabled", true)) { - return; - } - - let haveFirstRun = this._prefs.get("service.firstRun", false); - let delayInterval; - - if (haveFirstRun) { - delayInterval = this._prefs.get("service.loadDelayMsec") || - DEFAULT_LOAD_DELAY_MSEC; - } else { - delayInterval = this._prefs.get("service.loadDelayFirstRunMsec") || - DEFAULT_LOAD_DELAY_FIRST_RUN_MSEC; - } - - // Delay service loading a little more so things have an opportunity - // to cool down first. - this.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); - this.timer.initWithCallback({ - notify: function notify() { - delete this.timer; - - // There could be a race between "quit-application" firing and - // this callback being invoked. We close that door. - if (this._quitting) { - return; - } - - // Side effect: instantiates the reporter instance if not already - // accessed. - // - // The instance installs its own shutdown observers. So, we just - // fire and forget: it will clean itself up. - let reporter = this.healthReporter; - policy.ensureUserNotified(); - }.bind(this), - }, delayInterval, this.timer.TYPE_ONE_SHOT); - - break; - - case "quit-application": - this._os.removeObserver(this, "quit-application"); - this._quitting = true; - - // Shutdown doesn't clear pending timers. So, we need to explicitly - // cancel our health reporter initialization timer or else it will - // attempt initialization after shutdown has commenced. This would - // likely lead to stalls or crashes. - if (this.timer) { - this.timer.cancel(); - } - - if (this.policy) { - this.policy.stopPolling(); - } - break; - } - }, - - /** - * The HealthReporter instance associated with this service. - * - * If the service is disabled, this will return null. - * - * The obtained instance may not be fully initialized. - */ - get healthReporter() { - if (!this._prefs.get("service.enabled", true)) { - return null; - } - - if ("_healthReporter" in this) { - return this._healthReporter; - } - - try { - this._loadHealthReporter(); - } catch (ex) { - this._healthReporter = null; - Cu.reportError("Exception when obtaining health reporter: " + - CommonUtils.exceptionStr(ex)); - } - - return this._healthReporter; - }, - - _loadHealthReporter: function () { - // This should never happen. It was added to help trace down bug 924307. - if (!this.policy) { - throw new Error("this.policy not set."); - } - - let ns = {}; - // Lazy import so application startup isn't adversely affected. - - Cu.import("resource://gre/modules/Task.jsm", ns); - Cu.import("resource://gre/modules/HealthReport.jsm", ns); - Cu.import("resource://gre/modules/Log.jsm", ns); - - // How many times will we rewrite this code before rolling it up into a - // generic module? See also bug 451283. - const LOGGERS = [ - "Services.DataReporting", - "Services.HealthReport", - "Services.Metrics", - "Services.BagheeraClient", - "Sqlite.Connection.healthreport", - ]; - - let loggingPrefs = new Preferences(HEALTHREPORT_LOGGING_BRANCH); - if (loggingPrefs.get("consoleEnabled", true)) { - let level = loggingPrefs.get("consoleLevel", "Warn"); - let appender = new ns.Log.ConsoleAppender(); - appender.level = ns.Log.Level[level] || ns.Log.Level.Warn; - - for (let name of LOGGERS) { - let logger = ns.Log.repository.getLogger(name); - logger.addAppender(appender); - } - } - - if (loggingPrefs.get("dumpEnabled", false)) { - let level = loggingPrefs.get("dumpLevel", "Debug"); - let appender = new ns.Log.DumpAppender(); - appender.level = ns.Log.Level[level] || ns.Log.Level.Debug; - - for (let name of LOGGERS) { - let logger = ns.Log.repository.getLogger(name); - logger.addAppender(appender); - } - } - - this._healthReporter = new ns.HealthReporter(HEALTHREPORT_BRANCH, - this.policy, - this.sessionRecorder); - - // Wait for initialization to finish so if a shutdown occurs before init - // has finished we don't adversely affect app startup on next run. - this._healthReporter.init().then(function onInit() { - this._prefs.set("service.firstRun", true); - }.bind(this)); - }, - - _loadClientID: function () { - if (this._loadClientIdTask) { - return this._loadClientIdTask; - } - - this._loadClientIdTask = Task.spawn(function* () { - // Previously we had the stable client ID managed in FHR. - // As we want to start correlating FHR and telemetry data (and moving towards - // unifying the two), we moved the ID management to the datareporting - // service. Consequently, we try to import the FHR ID first, so we can keep - // using it. - - // Try to load the client id from the DRS state file first. - try { - let state = yield CommonUtils.readJSON(this._stateFilePath); - if (state && 'clientID' in state && typeof(state.clientID) == 'string') { - this._clientID = state.clientID; - this._loadClientIdTask = null; - return this._clientID; - } - } catch (e) { - // fall through to next option - } - - // If we dont have DRS state yet, try to import from the FHR state. - try { - let fhrStatePath = OS.Path.join(OS.Constants.Path.profileDir, "healthreport", "state.json"); - let state = yield CommonUtils.readJSON(fhrStatePath); - if (state && 'clientID' in state && typeof(state.clientID) == 'string') { - this._clientID = state.clientID; - this._loadClientIdTask = null; - this._saveClientID(); - return this._clientID; - } - } catch (e) { - // fall through to next option - } - - // We dont have an id from FHR yet, generate a new ID. - this._clientID = CommonUtils.generateUUID(); - this._loadClientIdTask = null; - this._saveClientIdTask = this._saveClientID(); - - // Wait on persisting the id. Otherwise failure to save the ID would result in - // the client creating and subsequently sending multiple IDs to the server. - // This would appear as multiple clients submitting similar data, which would - // result in orphaning. - yield this._saveClientIdTask; - - return this._clientID; - }.bind(this)); - - return this._loadClientIdTask; - }, - - _saveClientID: Task.async(function* () { - let obj = { clientID: this._clientID }; - yield OS.File.makeDir(this._stateDir); - yield CommonUtils.writeJSON(obj, this._stateFilePath); - this._saveClientIdTask = null; - }), - - /** - * This returns a promise resolving to the the stable client ID we use for - * data reporting (FHR & Telemetry). Previously exising FHR client IDs are - * migrated to this. - * - * @return Promise The stable client ID. - */ - getClientID: function() { - if (!this._clientID) { - return this._loadClientID(); - } - - return Promise.resolve(this._clientID); - }, - - /** - * Reset the stable client id. - * - * @return Promise The new client ID. - */ - resetClientID: Task.async(function* () { - yield this._loadClientIdTask; - yield this._saveClientIdTask; - - this._clientID = CommonUtils.generateUUID(); - this._saveClientIdTask = this._saveClientID(); - yield this._saveClientIdTask; - - return this._clientID; - }), - - /** - * Returns the SessionRecorder instance associated with the data reporting service. - * Returns an actual object only if FHR is enabled and after initialization, - * else returns undefined. - */ - getSessionRecorder: function() { - return this._simulateNoSessionRecorder ? undefined : this.sessionRecorder; - }, - - // These two simulate* methods below are only used for testings and control - // whether getSessionRecorder() behaves normally or forced to return undefined - simulateNoSessionRecorder() { - this._simulateNoSessionRecorder = true; - }, - - simulateRestoreSessionRecorder() { - this._simulateNoSessionRecorder = false; - }, - - /* - * Simulate a restart of the service. This is for testing only. - */ - _reset: Task.async(function* () { - yield this._loadClientIdTask; - yield this._saveClientIdTask; - this._clientID = null; - }), -}); - -this.NSGetFactory = XPCOMUtils.generateNSGetFactory([DataReportingService]); - -#define MERGED_COMPARTMENT - -#include ../common/observers.js -; -#include policy.jsm -; -#include sessions.jsm -; - diff --git a/services/datareporting/modules-testing/mocks.jsm b/services/datareporting/modules-testing/mocks.jsm deleted file mode 100644 index 1fba922511..0000000000 --- a/services/datareporting/modules-testing/mocks.jsm +++ /dev/null @@ -1,52 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -"use strict"; - -this.EXPORTED_SYMBOLS = ["MockPolicyListener"]; - -const {utils: Cu} = Components; - -Cu.import("resource://gre/modules/Log.jsm"); - - -this.MockPolicyListener = function MockPolicyListener() { - this._log = Log.repository.getLogger("Services.DataReporting.Testing.MockPolicyListener"); - this._log.level = Log.Level["Debug"]; - - this.requestDataUploadCount = 0; - this.lastDataRequest = null; - - this.requestRemoteDeleteCount = 0; - this.lastRemoteDeleteRequest = null; - - this.notifyUserCount = 0; - this.lastNotifyRequest = null; -} - -MockPolicyListener.prototype = { - onRequestDataUpload: function (request) { - this._log.info("onRequestDataUpload invoked."); - this.requestDataUploadCount++; - this.lastDataRequest = request; - }, - - onRequestRemoteDelete: function (request) { - this._log.info("onRequestRemoteDelete invoked."); - this.requestRemoteDeleteCount++; - this.lastRemoteDeleteRequest = request; - }, - - onNotifyDataPolicy: function (request, rejectMessage=null) { - this._log.info("onNotifyDataPolicy invoked."); - this.notifyUserCount++; - this.lastNotifyRequest = request; - if (rejectMessage) { - request.onUserNotifyFailed(rejectMessage); - } else { - request.onUserNotifyComplete(); - } - }, -}; - diff --git a/services/datareporting/moz.build b/services/datareporting/moz.build deleted file mode 100644 index 46468a2e13..0000000000 --- a/services/datareporting/moz.build +++ /dev/null @@ -1,24 +0,0 @@ -# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- -# vim: set filetype=python: -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -XPCSHELL_TESTS_MANIFESTS += ['tests/xpcshell/xpcshell.ini'] - -EXTRA_COMPONENTS += [ - 'DataReporting.manifest', -] - -EXTRA_PP_COMPONENTS += [ - 'DataReportingService.js', -] - -EXTRA_PP_JS_MODULES.services.datareporting += [ - 'policy.jsm', - 'sessions.jsm', -] - -TESTING_JS_MODULES.services.datareporting += [ - 'modules-testing/mocks.jsm', -] diff --git a/services/datareporting/policy.jsm b/services/datareporting/policy.jsm deleted file mode 100644 index b95d12f9c0..0000000000 --- a/services/datareporting/policy.jsm +++ /dev/null @@ -1,921 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/** - * This file is in transition. Most of its content needs to be moved under - * /services/healthreport. - */ - -#ifndef MERGED_COMPARTMENT - -"use strict"; - -this.EXPORTED_SYMBOLS = [ - "DataSubmissionRequest", // For test use only. - "DataReportingPolicy", - "DATAREPORTING_POLICY_VERSION", -]; - -const {classes: Cc, interfaces: Ci, utils: Cu} = Components; - -#endif - -Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://gre/modules/Promise.jsm"); -Cu.import("resource://gre/modules/Log.jsm"); -Cu.import("resource://services-common/utils.js"); -Cu.import("resource://gre/modules/UpdateChannel.jsm"); - -// The current policy version number. If the version number stored in the prefs -// is smaller than this, data upload will be disabled until the user is re-notified -// about the policy changes. -const DATAREPORTING_POLICY_VERSION = 1; - -const MILLISECONDS_PER_DAY = 24 * 60 * 60 * 1000; - -// Used as a sanity lower bound for dates stored in prefs. This module was -// implemented in 2012, so any earlier dates indicate an incorrect clock. -const OLDEST_ALLOWED_YEAR = 2012; - -/** - * Represents a request to display data policy. - * - * Receivers of these instances are expected to call one or more of the on* - * functions when events occur. - * - * When one of these requests is received, the first thing a callee should do - * is present notification to the user of the data policy. When the notice - * is displayed to the user, the callee should call `onUserNotifyComplete`. - * - * If for whatever reason the callee could not display a notice, - * it should call `onUserNotifyFailed`. - * - * @param policy - * (DataReportingPolicy) The policy instance this request came from. - * @param deferred - * (deferred) The promise that will be fulfilled when display occurs. - */ -function NotifyPolicyRequest(policy, deferred) { - this.policy = policy; - this.deferred = deferred; -} -NotifyPolicyRequest.prototype = Object.freeze({ - /** - * Called when the user is notified of the policy. - */ - onUserNotifyComplete: function () { - return this.deferred.resolve(); - }, - - /** - * Called when there was an error notifying the user about the policy. - * - * @param error - * (Error) Explains what went wrong. - */ - onUserNotifyFailed: function (error) { - return this.deferred.reject(error); - }, -}); - -/** - * Represents a request to submit data. - * - * Instances of this are created when the policy requests data upload or - * deletion. - * - * Receivers are expected to call one of the provided on* functions to signal - * completion of the request. - * - * Instances of this type should not be instantiated outside of this file. - * Receivers of instances of this type should not attempt to do anything with - * the instance except call one of the on* methods. - */ -this.DataSubmissionRequest = function (promise, expiresDate, isDelete) { - this.promise = promise; - this.expiresDate = expiresDate; - this.isDelete = isDelete; - - this.state = null; - this.reason = null; -} - -this.DataSubmissionRequest.prototype = Object.freeze({ - NO_DATA_AVAILABLE: "no-data-available", - SUBMISSION_SUCCESS: "success", - SUBMISSION_FAILURE_SOFT: "failure-soft", - SUBMISSION_FAILURE_HARD: "failure-hard", - UPLOAD_IN_PROGRESS: "upload-in-progress", - - /** - * No submission was attempted because no data was available. - * - * In the case of upload, this means there is no data to upload (perhaps - * it isn't available yet). In case of remote deletion, it means that there - * is no remote data to delete. - */ - onNoDataAvailable: function onNoDataAvailable() { - this.state = this.NO_DATA_AVAILABLE; - this.promise.resolve(this); - return this.promise.promise; - }, - - /** - * Data submission has completed successfully. - * - * In case of upload, this means the upload completed successfully. In case - * of deletion, the data was deleted successfully. - * - * @param date - * (Date) When data submission occurred. - */ - onSubmissionSuccess: function onSubmissionSuccess(date) { - this.state = this.SUBMISSION_SUCCESS; - this.submissionDate = date; - this.promise.resolve(this); - return this.promise.promise; - }, - - /** - * There was a recoverable failure when submitting data. - * - * Perhaps the server was down. Perhaps the network wasn't available. The - * policy may request submission again after a short delay. - * - * @param reason - * (string) Why the failure occurred. For logging purposes only. - */ - onSubmissionFailureSoft: function onSubmissionFailureSoft(reason=null) { - this.state = this.SUBMISSION_FAILURE_SOFT; - this.reason = reason; - this.promise.resolve(this); - return this.promise.promise; - }, - - /** - * There was an unrecoverable failure when submitting data. - * - * Perhaps the client is misconfigured. Perhaps the server rejected the data. - * Attempts at performing submission again will yield the same result. So, - * the policy should not try again (until the next day). - * - * @param reason - * (string) Why the failure occurred. For logging purposes only. - */ - onSubmissionFailureHard: function onSubmissionFailureHard(reason=null) { - this.state = this.SUBMISSION_FAILURE_HARD; - this.reason = reason; - this.promise.resolve(this); - return this.promise.promise; - }, - - /** - * The request was aborted because an upload was already in progress. - */ - onUploadInProgress: function (reason=null) { - this.state = this.UPLOAD_IN_PROGRESS; - this.reason = reason; - this.promise.resolve(this); - return this.promise.promise; - }, -}); - -/** - * Manages scheduling of Firefox Health Report data submission. - * - * The rules of data submission are as follows: - * - * 1. Do not submit data more than once every 24 hours. - * 2. Try to submit as close to 24 hours apart as possible. - * 3. Do not submit too soon after application startup so as to not negatively - * impact performance at startup. - * 4. Before first ever data submission, the user should be notified about - * data collection practices. - * 5. User should have opportunity to react to this notification before - * data submission. - * 6. If data submission fails, try at most 2 additional times before giving - * up on that day's submission. - * - * The listener passed into the instance must have the following properties - * (which are callbacks that will be invoked at certain key events): - * - * * onRequestDataUpload(request) - Called when the policy is requesting - * data to be submitted. The function is passed a `DataSubmissionRequest`. - * The listener should call one of the special resolving functions on that - * instance (see the documentation for that type). - * - * * onRequestRemoteDelete(request) - Called when the policy is requesting - * deletion of remotely stored data. The function is passed a - * `DataSubmissionRequest`. The listener should call one of the special - * resolving functions on that instance (just like `onRequestDataUpload`). - * - * * onNotifyDataPolicy(request) - Called when the policy is requesting the - * user to be notified that data submission will occur. The function - * receives a `NotifyPolicyRequest` instance. The callee should call one or - * more of the functions on that instance when specific events occur. See - * the documentation for that type for more. - * - * Note that the notification method is abstracted. Different applications - * can have different mechanisms by which they notify the user of data - * submission practices. - * - * @param policyPrefs - * (Preferences) Handle on preferences branch on which state will be - * queried and stored. - * @param healthReportPrefs - * (Preferences) Handle on preferences branch holding Health Report state. - * @param listener - * (object) Object with callbacks that will be invoked at certain key - * events. - */ -this.DataReportingPolicy = function (prefs, healthReportPrefs, listener) { - this._log = Log.repository.getLogger("Services.DataReporting.Policy"); - this._log.level = Log.Level["Debug"]; - - for (let handler of this.REQUIRED_LISTENERS) { - if (!listener[handler]) { - throw new Error("Passed listener does not contain required handler: " + - handler); - } - } - - this._prefs = prefs; - this._healthReportPrefs = healthReportPrefs; - this._listener = listener; - this._userNotifyPromise = null; - - this._migratePrefs(); - - if (!this.firstRunDate.getTime()) { - // If we've never run before, record the current time. - this.firstRunDate = this.now(); - } - - // Install an observer so that we can act on changes from external - // code (such as Android UI). - // Use a function because this is the only place where the Preferences - // abstraction is way less usable than nsIPrefBranch. - // - // Hang on to the observer here so that tests can reach it. - this.uploadEnabledObserver = function onUploadEnabledChanged() { - if (this.pendingDeleteRemoteData || this.healthReportUploadEnabled) { - // Nothing to do: either we're already deleting because the caller - // came through the front door (rHRUE), or they set the flag to true. - return; - } - this._log.info("uploadEnabled pref changed. Scheduling deletion."); - this.deleteRemoteData(); - }.bind(this); - - healthReportPrefs.observe("uploadEnabled", this.uploadEnabledObserver); - - // Ensure we are scheduled to submit. - if (!this.nextDataSubmissionDate.getTime()) { - this.nextDataSubmissionDate = this._futureDate(MILLISECONDS_PER_DAY); - } - - // Record when we last requested for submitted data to be sent. This is - // to avoid having multiple outstanding requests. - this._inProgressSubmissionRequest = null; -}; - -this.DataReportingPolicy.prototype = Object.freeze({ - /** - * How often to poll to see if we need to do something. - * - * The interval needs to be short enough such that short-lived applications - * have an opportunity to submit data. But, it also needs to be long enough - * to not negatively impact performance. - * - * The random bit is to ensure that other systems scheduling around the same - * interval don't all get scheduled together. - */ - POLL_INTERVAL_MSEC: (60 * 1000) + Math.floor(2.5 * 1000 * Math.random()), - - /** - * How long individual data submission requests live before expiring. - * - * Data submission requests have this long to complete before we give up on - * them and try again. - * - * We want this to be short enough that we retry frequently enough but long - * enough to give slow networks and systems time to handle it. - */ - SUBMISSION_REQUEST_EXPIRE_INTERVAL_MSEC: 10 * 60 * 1000, - - /** - * Our backoff schedule in case of submission failure. - * - * This dictates both the number of times we retry a daily submission and - * when to retry after each failure. - * - * Each element represents how long to wait after each recoverable failure. - * After the first failure, we wait the time in element 0 before trying - * again. After the second failure, we wait the time in element 1. Once - * we run out of values in this array, we give up on that day's submission - * and schedule for a day out. - */ - FAILURE_BACKOFF_INTERVALS: [ - 15 * 60 * 1000, - 60 * 60 * 1000, - ], - - REQUIRED_LISTENERS: [ - "onRequestDataUpload", - "onRequestRemoteDelete", - "onNotifyDataPolicy", - ], - - /** - * The first time the health report policy came into existence. - * - * This is used for scheduling of the initial submission. - */ - get firstRunDate() { - return CommonUtils.getDatePref(this._prefs, "firstRunTime", 0, this._log, - OLDEST_ALLOWED_YEAR); - }, - - set firstRunDate(value) { - this._log.debug("Setting first-run date: " + value); - CommonUtils.setDatePref(this._prefs, "firstRunTime", value, - OLDEST_ALLOWED_YEAR); - }, - - get dataSubmissionPolicyNotifiedDate() { - return CommonUtils.getDatePref(this._prefs, - "dataSubmissionPolicyNotifiedTime", 0, - this._log, OLDEST_ALLOWED_YEAR); - }, - - set dataSubmissionPolicyNotifiedDate(value) { - this._log.debug("Setting user notified date: " + value); - CommonUtils.setDatePref(this._prefs, "dataSubmissionPolicyNotifiedTime", - value, OLDEST_ALLOWED_YEAR); - }, - - get dataSubmissionPolicyBypassNotification() { - return this._prefs.get("dataSubmissionPolicyBypassNotification", false); - }, - - set dataSubmissionPolicyBypassNotification(value) { - return this._prefs.set("dataSubmissionPolicyBypassNotification", !!value); - }, - - /** - * Whether submission of data is allowed. - * - * This is the master switch for remote server communication. If it is - * false, we never request upload or deletion. - */ - get dataSubmissionEnabled() { - // Default is true because we are opt-out. - return this._prefs.get("dataSubmissionEnabled", true); - }, - - set dataSubmissionEnabled(value) { - this._prefs.set("dataSubmissionEnabled", !!value); - }, - - get currentPolicyVersion() { - return this._prefs.get("currentPolicyVersion", DATAREPORTING_POLICY_VERSION); - }, - - /** - * The minimum policy version which for dataSubmissionPolicyAccepted to - * to be valid. - */ - get minimumPolicyVersion() { - // First check if the current channel has an ove - let channel = UpdateChannel.get(false); - let channelPref = this._prefs.get("minimumPolicyVersion.channel-" + channel); - return channelPref !== undefined ? - channelPref : this._prefs.get("minimumPolicyVersion", 1); - }, - - get dataSubmissionPolicyAcceptedVersion() { - return this._prefs.get("dataSubmissionPolicyAcceptedVersion", 0); - }, - - set dataSubmissionPolicyAcceptedVersion(value) { - this._prefs.set("dataSubmissionPolicyAcceptedVersion", value); - }, - - /** - * Checks to see if the user has been notified about data submission - * @return {bool} - */ - get userNotifiedOfCurrentPolicy() { - return this.dataSubmissionPolicyNotifiedDate.getTime() > 0 && - this.dataSubmissionPolicyAcceptedVersion >= this.currentPolicyVersion; - }, - - /** - * When this policy last requested data submission. - * - * This is used mainly for forensics purposes and should have no bearing - * on scheduling or run-time behavior. - */ - get lastDataSubmissionRequestedDate() { - return CommonUtils.getDatePref(this._healthReportPrefs, - "lastDataSubmissionRequestedTime", 0, - this._log, OLDEST_ALLOWED_YEAR); - }, - - set lastDataSubmissionRequestedDate(value) { - CommonUtils.setDatePref(this._healthReportPrefs, - "lastDataSubmissionRequestedTime", - value, OLDEST_ALLOWED_YEAR); - }, - - /** - * When the last data submission actually occurred. - * - * This is used mainly for forensics purposes and should have no bearing on - * actual scheduling. - */ - get lastDataSubmissionSuccessfulDate() { - return CommonUtils.getDatePref(this._healthReportPrefs, - "lastDataSubmissionSuccessfulTime", 0, - this._log, OLDEST_ALLOWED_YEAR); - }, - - set lastDataSubmissionSuccessfulDate(value) { - CommonUtils.setDatePref(this._healthReportPrefs, - "lastDataSubmissionSuccessfulTime", - value, OLDEST_ALLOWED_YEAR); - }, - - /** - * When we last encountered a submission failure. - * - * This is used for forensics purposes and should have no bearing on - * scheduling. - */ - get lastDataSubmissionFailureDate() { - return CommonUtils.getDatePref(this._healthReportPrefs, - "lastDataSubmissionFailureTime", - 0, this._log, OLDEST_ALLOWED_YEAR); - }, - - set lastDataSubmissionFailureDate(value) { - CommonUtils.setDatePref(this._healthReportPrefs, - "lastDataSubmissionFailureTime", - value, OLDEST_ALLOWED_YEAR); - }, - - /** - * When the next data submission is scheduled to occur. - * - * This is maintained internally by this type. External users should not - * mutate this value. - */ - get nextDataSubmissionDate() { - return CommonUtils.getDatePref(this._healthReportPrefs, - "nextDataSubmissionTime", 0, - this._log, OLDEST_ALLOWED_YEAR); - }, - - set nextDataSubmissionDate(value) { - CommonUtils.setDatePref(this._healthReportPrefs, - "nextDataSubmissionTime", value, - OLDEST_ALLOWED_YEAR); - }, - - /** - * The number of submission failures for this day's upload. - * - * This is used to drive backoff and scheduling. - */ - get currentDaySubmissionFailureCount() { - let v = this._healthReportPrefs.get("currentDaySubmissionFailureCount", 0); - - if (!Number.isInteger(v)) { - v = 0; - } - - return v; - }, - - set currentDaySubmissionFailureCount(value) { - if (!Number.isInteger(value)) { - throw new Error("Value must be integer: " + value); - } - - this._healthReportPrefs.set("currentDaySubmissionFailureCount", value); - }, - - /** - * Whether a request to delete remote data is awaiting completion. - * - * If this is true, the policy will request that remote data be deleted. - * Furthermore, no new data will be uploaded (if it's even allowed) until - * the remote deletion is fulfilled. - */ - get pendingDeleteRemoteData() { - return !!this._healthReportPrefs.get("pendingDeleteRemoteData", false); - }, - - set pendingDeleteRemoteData(value) { - this._healthReportPrefs.set("pendingDeleteRemoteData", !!value); - }, - - /** - * Whether upload of Firefox Health Report data is enabled. - */ - get healthReportUploadEnabled() { - return !!this._healthReportPrefs.get("uploadEnabled", true); - }, - - // External callers should update this via `recordHealthReportUploadEnabled` - // to ensure appropriate side-effects are performed. - set healthReportUploadEnabled(value) { - this._healthReportPrefs.set("uploadEnabled", !!value); - }, - - /** - * Whether the FHR upload enabled setting is locked and can't be changed. - */ - get healthReportUploadLocked() { - return this._healthReportPrefs.locked("uploadEnabled"); - }, - - /** - * Record the user's intent for whether FHR should upload data. - * - * This is the preferred way for XUL applications to record a user's - * preference on whether Firefox Health Report should upload data to - * a server. - * - * If upload is disabled through this API, a request for remote data - * deletion is initiated automatically. - * - * If upload is being disabled and this operation is scheduled to - * occur immediately, a promise will be returned. This promise will be - * fulfilled when the deletion attempt finishes. If upload is being - * disabled and a promise is not returned, callers must poll - * `haveRemoteData` on the HealthReporter instance to see if remote - * data has been deleted. - * - * @param flag - * (bool) Whether data submission is enabled or disabled. - * @param reason - * (string) Why this value is being adjusted. For logging - * purposes only. - */ - recordHealthReportUploadEnabled: function (flag, reason="no-reason") { - let result = null; - if (!flag) { - result = this.deleteRemoteData(reason); - } - - this.healthReportUploadEnabled = flag; - return result; - }, - - /** - * Request that remote data be deleted. - * - * This will record an intent that previously uploaded data is to be deleted. - * The policy will eventually issue a request to the listener for data - * deletion. It will keep asking for deletion until the listener acknowledges - * that data has been deleted. - */ - deleteRemoteData: function deleteRemoteData(reason="no-reason") { - this._log.info("Remote data deletion requested: " + reason); - - this.pendingDeleteRemoteData = true; - - // We want delete deletion to occur as soon as possible. Move up any - // pending scheduled data submission and try to trigger. - this.nextDataSubmissionDate = this.now(); - return this.checkStateAndTrigger(); - }, - - /** - * Start background polling for activity. - * - * This will set up a recurring timer that will periodically check if - * activity is warranted. - * - * You typically call this function for each constructed instance. - */ - startPolling: function startPolling() { - this.stopPolling(); - - this._timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); - this._timer.initWithCallback({ - notify: function notify() { - this.checkStateAndTrigger(); - }.bind(this) - }, this.POLL_INTERVAL_MSEC, this._timer.TYPE_REPEATING_SLACK); - }, - - /** - * Stop background polling for activity. - * - * This should be called when the instance is no longer needed. - */ - stopPolling: function stopPolling() { - if (this._timer) { - this._timer.cancel(); - this._timer = null; - } - }, - - /** - * Abstraction for obtaining current time. - * - * The purpose of this is to facilitate testing. Testing code can monkeypatch - * this on instances instead of modifying the singleton Date object. - */ - now: function now() { - return new Date(); - }, - - /** - * Check state and trigger actions, if necessary. - * - * This is what enforces the submission and notification policy detailed - * above. You can think of this as the driver for health report data - * submission. - * - * Typically this function is called automatically by the background polling. - * But, it can safely be called manually as needed. - */ - checkStateAndTrigger: function checkStateAndTrigger() { - // If the master data submission kill switch is toggled, we have nothing - // to do. We don't notify about data policies because this would have - // no effect. - if (!this.dataSubmissionEnabled) { - this._log.debug("Data submission is disabled. Doing nothing."); - return; - } - - let now = this.now(); - let nowT = now.getTime(); - let nextSubmissionDate = this.nextDataSubmissionDate; - - // If the system clock were ever set to a time in the distant future, - // it's possible our next schedule date is far out as well. We know - // we shouldn't schedule for more than a day out, so we reset the next - // scheduled date appropriately. 3 days was chosen arbitrarily. - if (nextSubmissionDate.getTime() >= nowT + 3 * MILLISECONDS_PER_DAY) { - this._log.warn("Next data submission time is far away. Was the system " + - "clock recently readjusted? " + nextSubmissionDate); - - // It shouldn't really matter what we set this to. 1 day in the future - // should be pretty safe. - this._moveScheduleForward24h(); - - // Fall through since we may have other actions. - } - - // Tend to any in progress work. - if (this._processInProgressSubmission()) { - return; - } - - // Requests to delete remote data take priority above everything else. - if (this.pendingDeleteRemoteData) { - if (nowT < nextSubmissionDate.getTime()) { - this._log.debug("Deletion request is scheduled for the future: " + - nextSubmissionDate); - return; - } - - return this._dispatchSubmissionRequest("onRequestRemoteDelete", true); - } - - if (!this.healthReportUploadEnabled) { - this._log.debug("Data upload is disabled. Doing nothing."); - return; - } - - if (!this.ensureUserNotified()) { - this._log.warn("The user has not been notified about the data submission " + - "policy. Not attempting upload."); - return; - } - - // Data submission is allowed to occur. Now comes the scheduling part. - - if (nowT < nextSubmissionDate.getTime()) { - this._log.debug("Next data submission is scheduled in the future: " + - nextSubmissionDate); - return; - } - - return this._dispatchSubmissionRequest("onRequestDataUpload", false); - }, - - /** - * Ensure that the data policy notification has been displayed. - * - * This must be called before data submission. If the policy has not been - * displayed, data submission must not occur. - * - * @return bool Whether the notification has been displayed. - */ - ensureUserNotified: function () { - if (this.userNotifiedOfCurrentPolicy || this.dataSubmissionPolicyBypassNotification) { - return true; - } - - // The user has not been notified yet, but is in the process of being notified. - if (this._userNotifyPromise) { - return false; - } - - let deferred = Promise.defer(); - deferred.promise.then((function onSuccess() { - this._recordDataPolicyNotification(this.now(), this.currentPolicyVersion); - this._userNotifyPromise = null; - }).bind(this), ((error) => { - this._log.warn("Data policy notification presentation failed: " + - CommonUtils.exceptionStr(error)); - this._userNotifyPromise = null; - }).bind(this)); - - this._log.info("Requesting display of data policy."); - let request = new NotifyPolicyRequest(this, deferred); - try { - this._listener.onNotifyDataPolicy(request); - } catch (ex) { - this._log.warn("Exception when calling onNotifyDataPolicy: " + - CommonUtils.exceptionStr(ex)); - } - - this._userNotifyPromise = deferred.promise; - - return false; - }, - - _recordDataPolicyNotification: function (date, version) { - this._log.debug("Recording data policy notification to version " + version + - " on date " + date); - this.dataSubmissionPolicyNotifiedDate = date; - this.dataSubmissionPolicyAcceptedVersion = version; - }, - - _migratePrefs: function () { - // Current prefs are mostly the same than the old ones, except for some deprecated ones. - this._prefs.reset([ - "dataSubmissionPolicyAccepted", - "dataSubmissionPolicyBypassAcceptance", - "dataSubmissionPolicyResponseType", - "dataSubmissionPolicyResponseTime" - ]); - }, - - _processInProgressSubmission: function _processInProgressSubmission() { - if (!this._inProgressSubmissionRequest) { - return false; - } - - let now = this.now().getTime(); - if (this._inProgressSubmissionRequest.expiresDate.getTime() > now) { - this._log.info("Waiting on in-progress submission request to finish."); - return true; - } - - this._log.warn("Old submission request has expired from no activity."); - this._inProgressSubmissionRequest.promise.reject(new Error("Request has expired.")); - this._inProgressSubmissionRequest = null; - this._handleSubmissionFailure(); - - return false; - }, - - _dispatchSubmissionRequest: function _dispatchSubmissionRequest(handler, isDelete) { - let now = this.now(); - - // We're past our scheduled next data submission date, so let's do it! - this.lastDataSubmissionRequestedDate = now; - let deferred = Promise.defer(); - let requestExpiresDate = - this._futureDate(this.SUBMISSION_REQUEST_EXPIRE_INTERVAL_MSEC); - this._inProgressSubmissionRequest = new DataSubmissionRequest(deferred, - requestExpiresDate, - isDelete); - - let onSuccess = function onSuccess(result) { - this._inProgressSubmissionRequest = null; - this._handleSubmissionResult(result); - }.bind(this); - - let onError = function onError(error) { - this._log.error("Error when handling data submission result: " + - CommonUtils.exceptionStr(error)); - this._inProgressSubmissionRequest = null; - this._handleSubmissionFailure(); - }.bind(this); - - let chained = deferred.promise.then(onSuccess, onError); - - this._log.info("Requesting data submission. Will expire at " + - requestExpiresDate); - try { - let promise = this._listener[handler](this._inProgressSubmissionRequest); - chained = chained.then(() => promise, null); - } catch (ex) { - this._log.warn("Exception when calling " + handler + ": " + - CommonUtils.exceptionStr(ex)); - this._inProgressSubmissionRequest = null; - this._handleSubmissionFailure(); - return; - } - - return chained; - }, - - _handleSubmissionResult: function _handleSubmissionResult(request) { - let state = request.state; - let reason = request.reason || "no reason"; - this._log.info("Got submission request result: " + state); - - if (state == request.SUBMISSION_SUCCESS) { - if (request.isDelete) { - this.pendingDeleteRemoteData = false; - this._log.info("Successful data delete reported."); - } else { - this._log.info("Successful data upload reported."); - } - - this.lastDataSubmissionSuccessfulDate = request.submissionDate; - - let nextSubmissionDate = - new Date(request.submissionDate.getTime() + MILLISECONDS_PER_DAY); - - // Schedule pending deletes immediately. This has potential to overload - // the server. However, the frequency of delete requests across all - // clients should be low, so this shouldn't pose a problem. - if (this.pendingDeleteRemoteData) { - nextSubmissionDate = this.now(); - } - - this.nextDataSubmissionDate = nextSubmissionDate; - this.currentDaySubmissionFailureCount = 0; - return; - } - - if (state == request.NO_DATA_AVAILABLE) { - if (request.isDelete) { - this._log.info("Remote data delete requested but no remote data was stored."); - this.pendingDeleteRemoteData = false; - return; - } - - this._log.info("No data was available to submit. May try later."); - this._handleSubmissionFailure(); - return; - } - - // We don't special case request.isDelete for these failures because it - // likely means there was a server error. - - if (state == request.SUBMISSION_FAILURE_SOFT) { - this._log.warn("Soft error submitting data: " + reason); - this.lastDataSubmissionFailureDate = this.now(); - this._handleSubmissionFailure(); - return; - } - - if (state == request.SUBMISSION_FAILURE_HARD) { - this._log.warn("Hard error submitting data: " + reason); - this.lastDataSubmissionFailureDate = this.now(); - this._moveScheduleForward24h(); - return; - } - - throw new Error("Unknown state on DataSubmissionRequest: " + request.state); - }, - - _handleSubmissionFailure: function _handleSubmissionFailure() { - if (this.currentDaySubmissionFailureCount >= this.FAILURE_BACKOFF_INTERVALS.length) { - this._log.warn("Reached the limit of daily submission attempts. " + - "Rescheduling for tomorrow."); - this._moveScheduleForward24h(); - return false; - } - - let offset = this.FAILURE_BACKOFF_INTERVALS[this.currentDaySubmissionFailureCount]; - this.nextDataSubmissionDate = this._futureDate(offset); - this.currentDaySubmissionFailureCount++; - return true; - }, - - _moveScheduleForward24h: function _moveScheduleForward24h() { - let d = this._futureDate(MILLISECONDS_PER_DAY); - this._log.info("Setting next scheduled data submission for " + d); - - this.nextDataSubmissionDate = d; - this.currentDaySubmissionFailureCount = 0; - }, - - _futureDate: function _futureDate(offset) { - return new Date(this.now().getTime() + offset); - }, -}); - diff --git a/services/datareporting/sessions.jsm b/services/datareporting/sessions.jsm deleted file mode 100644 index d741c8e66a..0000000000 --- a/services/datareporting/sessions.jsm +++ /dev/null @@ -1,406 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef MERGED_COMPARTMENT - -"use strict"; - -this.EXPORTED_SYMBOLS = [ - "SessionRecorder", -]; - -const {classes: Cc, interfaces: Ci, utils: Cu} = Components; - -#endif - -Cu.import("resource://gre/modules/Preferences.jsm"); -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource://gre/modules/Log.jsm"); -Cu.import("resource://services-common/utils.js"); - - -// We automatically prune sessions older than this. -const MAX_SESSION_AGE_MS = 7 * 24 * 60 * 60 * 1000; // 7 days. -const STARTUP_RETRY_INTERVAL_MS = 5000; - -// Wait up to 5 minutes for startup measurements before giving up. -const MAX_STARTUP_TRIES = 300000 / STARTUP_RETRY_INTERVAL_MS; - -/** - * Records information about browser sessions. - * - * This serves as an interface to both current session information as - * well as a history of previous sessions. - * - * Typically only one instance of this will be installed in an - * application. It is typically managed by an XPCOM service. The - * instance is instantiated at application start; onStartup is called - * once the profile is installed; onShutdown is called during shutdown. - * - * We currently record state in preferences. However, this should be - * invisible to external consumers. We could easily swap in a different - * storage mechanism if desired. - * - * Please note the different semantics for storing times and dates in - * preferences. Full dates (notably the session start time) are stored - * as strings because preferences have a 32-bit limit on integer values - * and milliseconds since UNIX epoch would overflow. Many times are - * stored as integer offsets from the session start time because they - * should not overflow 32 bits. - * - * Since this records history of all sessions, there is a possibility - * for unbounded data aggregation. This is curtailed through: - * - * 1) An "idle-daily" observer which delete sessions older than - * MAX_SESSION_AGE_MS. - * 2) The creator of this instance explicitly calling - * `pruneOldSessions`. - * - * @param branch - * (string) Preferences branch on which to record state. - */ -this.SessionRecorder = function (branch) { - if (!branch) { - throw new Error("branch argument must be defined."); - } - - if (!branch.endsWith(".")) { - throw new Error("branch argument must end with '.': " + branch); - } - - this._log = Log.repository.getLogger("Services.DataReporting.SessionRecorder"); - - this._prefs = new Preferences(branch); - this._lastActivityWasInactive = false; - this._activeTicks = 0; - this.fineTotalTime = 0; - this._started = false; - this._timer = null; - this._startupFieldTries = 0; - - this._os = Cc["@mozilla.org/observer-service;1"] - .getService(Ci.nsIObserverService); - -}; - -SessionRecorder.prototype = Object.freeze({ - QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]), - - STARTUP_RETRY_INTERVAL_MS: STARTUP_RETRY_INTERVAL_MS, - - get _currentIndex() { - return this._prefs.get("currentIndex", 0); - }, - - set _currentIndex(value) { - this._prefs.set("currentIndex", value); - }, - - get _prunedIndex() { - return this._prefs.get("prunedIndex", 0); - }, - - set _prunedIndex(value) { - this._prefs.set("prunedIndex", value); - }, - - get startDate() { - return CommonUtils.getDatePref(this._prefs, "current.startTime"); - }, - - set _startDate(value) { - CommonUtils.setDatePref(this._prefs, "current.startTime", value); - }, - - get activeTicks() { - return this._prefs.get("current.activeTicks", 0); - }, - - incrementActiveTicks: function () { - this._prefs.set("current.activeTicks", ++this._activeTicks); - }, - - /** - * Total time of this session in integer seconds. - * - * See also fineTotalTime for the time in milliseconds. - */ - get totalTime() { - return this._prefs.get("current.totalTime", 0); - }, - - updateTotalTime: function () { - // We store millisecond precision internally to prevent drift from - // repeated rounding. - this.fineTotalTime = Date.now() - this.startDate; - this._prefs.set("current.totalTime", Math.floor(this.fineTotalTime / 1000)); - }, - - get main() { - return this._prefs.get("current.main", -1); - }, - - set _main(value) { - if (!Number.isInteger(value)) { - throw new Error("main time must be an integer."); - } - - this._prefs.set("current.main", value); - }, - - get firstPaint() { - return this._prefs.get("current.firstPaint", -1); - }, - - set _firstPaint(value) { - if (!Number.isInteger(value)) { - throw new Error("firstPaint must be an integer."); - } - - this._prefs.set("current.firstPaint", value); - }, - - get sessionRestored() { - return this._prefs.get("current.sessionRestored", -1); - }, - - set _sessionRestored(value) { - if (!Number.isInteger(value)) { - throw new Error("sessionRestored must be an integer."); - } - - this._prefs.set("current.sessionRestored", value); - }, - - getPreviousSessions: function () { - let result = {}; - - for (let i = this._prunedIndex; i < this._currentIndex; i++) { - let s = this.getPreviousSession(i); - if (!s) { - continue; - } - - result[i] = s; - } - - return result; - }, - - getPreviousSession: function (index) { - return this._deserialize(this._prefs.get("previous." + index)); - }, - - /** - * Prunes old, completed sessions that started earlier than the - * specified date. - */ - pruneOldSessions: function (date) { - for (let i = this._prunedIndex; i < this._currentIndex; i++) { - let s = this.getPreviousSession(i); - if (!s) { - continue; - } - - if (s.startDate >= date) { - continue; - } - - this._log.debug("Pruning session #" + i + "."); - this._prefs.reset("previous." + i); - this._prunedIndex = i; - } - }, - - recordStartupFields: function () { - let si = this._getStartupInfo(); - - if (!si.process) { - throw new Error("Startup info not available."); - } - - let missing = false; - - for (let field of ["main", "firstPaint", "sessionRestored"]) { - if (!(field in si)) { - this._log.debug("Missing startup field: " + field); - missing = true; - continue; - } - - this["_" + field] = si[field].getTime() - si.process.getTime(); - } - - if (!missing || this._startupFieldTries > MAX_STARTUP_TRIES) { - this._clearStartupTimer(); - return; - } - - // If we have missing fields, install a timer and keep waiting for - // data. - this._startupFieldTries++; - - if (!this._timer) { - this._timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); - this._timer.initWithCallback({ - notify: this.recordStartupFields.bind(this), - }, this.STARTUP_RETRY_INTERVAL_MS, this._timer.TYPE_REPEATING_SLACK); - } - }, - - _clearStartupTimer: function () { - if (this._timer) { - this._timer.cancel(); - delete this._timer; - } - }, - - /** - * Perform functionality on application startup. - * - * This is typically called in a "profile-do-change" handler. - */ - onStartup: function () { - if (this._started) { - throw new Error("onStartup has already been called."); - } - - let si = this._getStartupInfo(); - if (!si.process) { - throw new Error("Process information not available. Misconfigured app?"); - } - - this._started = true; - - this._os.addObserver(this, "profile-before-change", false); - this._os.addObserver(this, "user-interaction-active", false); - this._os.addObserver(this, "user-interaction-inactive", false); - this._os.addObserver(this, "idle-daily", false); - - // This has the side-effect of clearing current session state. - this._moveCurrentToPrevious(); - - this._startDate = si.process; - this._prefs.set("current.activeTicks", 0); - this.updateTotalTime(); - - this.recordStartupFields(); - }, - - /** - * Record application activity. - */ - onActivity: function (active) { - let updateActive = active && !this._lastActivityWasInactive; - this._lastActivityWasInactive = !active; - - this.updateTotalTime(); - - if (updateActive) { - this.incrementActiveTicks(); - } - }, - - onShutdown: function () { - this._log.info("Recording clean session shutdown."); - this._prefs.set("current.clean", true); - this.updateTotalTime(); - this._clearStartupTimer(); - - this._os.removeObserver(this, "profile-before-change"); - this._os.removeObserver(this, "user-interaction-active"); - this._os.removeObserver(this, "user-interaction-inactive"); - this._os.removeObserver(this, "idle-daily"); - }, - - _CURRENT_PREFS: [ - "current.startTime", - "current.activeTicks", - "current.totalTime", - "current.main", - "current.firstPaint", - "current.sessionRestored", - "current.clean", - ], - - // This is meant to be called only during onStartup(). - _moveCurrentToPrevious: function () { - try { - if (!this.startDate.getTime()) { - this._log.info("No previous session. Is this first app run?"); - return; - } - - let clean = this._prefs.get("current.clean", false); - - let count = this._currentIndex++; - let obj = { - s: this.startDate.getTime(), - a: this.activeTicks, - t: this.totalTime, - c: clean, - m: this.main, - fp: this.firstPaint, - sr: this.sessionRestored, - }; - - this._log.debug("Recording last sessions as #" + count + "."); - this._prefs.set("previous." + count, JSON.stringify(obj)); - } catch (ex) { - this._log.warn("Exception when migrating last session: " + - CommonUtils.exceptionStr(ex)); - } finally { - this._log.debug("Resetting prefs from last session."); - for (let pref of this._CURRENT_PREFS) { - this._prefs.reset(pref); - } - } - }, - - _deserialize: function (s) { - let o; - try { - o = JSON.parse(s); - } catch (ex) { - return null; - } - - return { - startDate: new Date(o.s), - activeTicks: o.a, - totalTime: o.t, - clean: !!o.c, - main: o.m, - firstPaint: o.fp, - sessionRestored: o.sr, - }; - }, - - // Implemented as a function to allow for monkeypatching in tests. - _getStartupInfo: function () { - return Cc["@mozilla.org/toolkit/app-startup;1"] - .getService(Ci.nsIAppStartup) - .getStartupInfo(); - }, - - observe: function (subject, topic, data) { - switch (topic) { - case "profile-before-change": - this.onShutdown(); - break; - - case "user-interaction-active": - this.onActivity(true); - break; - - case "user-interaction-inactive": - this.onActivity(false); - break; - - case "idle-daily": - this.pruneOldSessions(new Date(Date.now() - MAX_SESSION_AGE_MS)); - break; - } - }, -}); diff --git a/services/datareporting/tests/xpcshell/head.js b/services/datareporting/tests/xpcshell/head.js deleted file mode 100644 index 3e63efaf29..0000000000 --- a/services/datareporting/tests/xpcshell/head.js +++ /dev/null @@ -1,16 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -"use strict"; - -// We need to initialize the profile or OS.File may not work. See bug 810543. -do_get_profile(); - -(function initTestingInfrastructure() { - let ns = {}; - Components.utils.import("resource://testing-common/services/common/logging.js", - ns); - - ns.initTestLogging(); -}).call(this); - diff --git a/services/datareporting/tests/xpcshell/test_client_id.js b/services/datareporting/tests/xpcshell/test_client_id.js deleted file mode 100644 index d72b66b2c4..0000000000 --- a/services/datareporting/tests/xpcshell/test_client_id.js +++ /dev/null @@ -1,86 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -"use strict"; - -const {classes: Cc, interfaces: Ci, utils: Cu} = Components; - -Cu.import("resource://gre/modules/Task.jsm"); -Cu.import("resource://services-common/utils.js"); -Cu.import("resource://gre/modules/osfile.jsm"); -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); - -XPCOMUtils.defineLazyGetter(this, "gDatareportingService", - () => Cc["@mozilla.org/datareporting/service;1"] - .getService(Ci.nsISupports) - .wrappedJSObject); - -function run_test() { - do_get_profile(); - - // Send the needed startup notifications to the datareporting service - // to ensure that it has been initialized. - gDatareportingService.observe(null, "app-startup", null); - gDatareportingService.observe(null, "profile-after-change", null); - - run_next_test(); -} - -add_task(function* () { - const drsPath = gDatareportingService._stateFilePath; - const fhrDir = OS.Path.join(OS.Constants.Path.profileDir, "healthreport"); - const fhrPath = OS.Path.join(fhrDir, "state.json"); - const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i; - - yield OS.File.makeDir(fhrDir); - - // Check that we are importing the FHR client ID. - let clientID = CommonUtils.generateUUID(); - yield CommonUtils.writeJSON({clientID: clientID}, fhrPath); - Assert.equal(clientID, yield gDatareportingService.getClientID()); - - // We should persist the ID in DRS now and not pick up a differing ID from FHR. - yield gDatareportingService._reset(); - yield CommonUtils.writeJSON({clientID: CommonUtils.generateUUID()}, fhrPath); - Assert.equal(clientID, yield gDatareportingService.getClientID()); - - // We should be guarded against broken FHR data. - yield gDatareportingService._reset(); - yield OS.File.remove(drsPath); - yield CommonUtils.writeJSON({clientID: -1}, fhrPath); - clientID = yield gDatareportingService.getClientID(); - Assert.equal(typeof(clientID), 'string'); - Assert.ok(uuidRegex.test(clientID)); - - // We should be guarded against invalid FHR json. - yield gDatareportingService._reset(); - yield OS.File.remove(drsPath); - yield OS.File.writeAtomic(fhrPath, "abcd", {encoding: "utf-8", tmpPath: fhrPath + ".tmp"}); - clientID = yield gDatareportingService.getClientID(); - Assert.equal(typeof(clientID), 'string'); - Assert.ok(uuidRegex.test(clientID)); - - // We should be guarded against broken DRS data too and fall back to loading - // the FHR ID. - yield gDatareportingService._reset(); - clientID = CommonUtils.generateUUID(); - yield CommonUtils.writeJSON({clientID: clientID}, fhrPath); - yield CommonUtils.writeJSON({clientID: -1}, drsPath); - Assert.equal(clientID, yield gDatareportingService.getClientID()); - - // We should be guarded against invalid DRS json too. - yield gDatareportingService._reset(); - yield OS.File.remove(fhrPath); - yield OS.File.writeAtomic(drsPath, "abcd", {encoding: "utf-8", tmpPath: drsPath + ".tmp"}); - clientID = yield gDatareportingService.getClientID(); - Assert.equal(typeof(clientID), 'string'); - Assert.ok(uuidRegex.test(clientID)); - - // If both the FHR and DSR data are broken, we should end up with a new client ID. - yield gDatareportingService._reset(); - yield CommonUtils.writeJSON({clientID: -1}, fhrPath); - yield CommonUtils.writeJSON({clientID: -1}, drsPath); - clientID = yield gDatareportingService.getClientID(); - Assert.equal(typeof(clientID), 'string'); - Assert.ok(uuidRegex.test(clientID)); -}); diff --git a/services/datareporting/tests/xpcshell/test_policy.js b/services/datareporting/tests/xpcshell/test_policy.js deleted file mode 100644 index e2877ec31b..0000000000 --- a/services/datareporting/tests/xpcshell/test_policy.js +++ /dev/null @@ -1,689 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -"use strict"; - -const {utils: Cu} = Components; - -Cu.import("resource://gre/modules/Preferences.jsm"); -Cu.import("resource://gre/modules/services/datareporting/policy.jsm"); -Cu.import("resource://testing-common/services/datareporting/mocks.jsm"); -Cu.import("resource://gre/modules/UpdateChannel.jsm"); -Cu.import("resource://gre/modules/Task.jsm"); - -function getPolicy(name, - aCurrentPolicyVersion = 1, - aMinimumPolicyVersion = 1, - aBranchMinimumVersionOverride) { - let branch = "testing.datareporting." + name; - - // The version prefs should not be removed on reset, so set them in the - // default branch. - let defaultPolicyPrefs = new Preferences({ branch: branch + ".policy." - , defaultBranch: true }); - defaultPolicyPrefs.set("currentPolicyVersion", aCurrentPolicyVersion); - defaultPolicyPrefs.set("minimumPolicyVersion", aMinimumPolicyVersion); - let branchOverridePrefName = "minimumPolicyVersion.channel-" + UpdateChannel.get(false); - if (aBranchMinimumVersionOverride !== undefined) - defaultPolicyPrefs.set(branchOverridePrefName, aBranchMinimumVersionOverride); - else - defaultPolicyPrefs.reset(branchOverridePrefName); - - let policyPrefs = new Preferences(branch + ".policy."); - let healthReportPrefs = new Preferences(branch + ".healthreport."); - - let listener = new MockPolicyListener(); - let policy = new DataReportingPolicy(policyPrefs, healthReportPrefs, listener); - - return [policy, policyPrefs, healthReportPrefs, listener]; -} - -/** - * Ensure that the notification has been displayed to the user therefore having - * policy.ensureUserNotified() === true, which will allow for a successful - * data upload and afterwards does a call to policy.checkStateAndTrigger() - * @param {Policy} policy - * @return {Promise} - */ -function ensureUserNotifiedAndTrigger(policy) { - return Task.spawn(function* ensureUserNotifiedAndTrigger () { - policy.ensureUserNotified(); - yield policy._listener.lastNotifyRequest.deferred.promise; - do_check_true(policy.userNotifiedOfCurrentPolicy); - policy.checkStateAndTrigger(); - }); -} - -function defineNow(policy, now) { - print("Adjusting fake system clock to " + now); - Object.defineProperty(policy, "now", { - value: function customNow() { - return now; - }, - writable: true, - }); -} - -function run_test() { - run_next_test(); -} - -add_test(function test_constructor() { - let policyPrefs = new Preferences("foo.bar.policy."); - let hrPrefs = new Preferences("foo.bar.healthreport."); - let listener = { - onRequestDataUpload: function() {}, - onRequestRemoteDelete: function() {}, - onNotifyDataPolicy: function() {}, - }; - - let policy = new DataReportingPolicy(policyPrefs, hrPrefs, listener); - do_check_true(Date.now() - policy.firstRunDate.getTime() < 1000); - - let tomorrow = Date.now() + 24 * 60 * 60 * 1000; - do_check_true(tomorrow - policy.nextDataSubmissionDate.getTime() < 1000); - - do_check_eq(policy.dataSubmissionPolicyAcceptedVersion, 0); - do_check_false(policy.userNotifiedOfCurrentPolicy); - - run_next_test(); -}); - -add_test(function test_prefs() { - let [policy, policyPrefs, hrPrefs, listener] = getPolicy("prefs"); - - let now = new Date(); - let nowT = now.getTime(); - - policy.firstRunDate = now; - do_check_eq(policyPrefs.get("firstRunTime"), nowT); - do_check_eq(policy.firstRunDate.getTime(), nowT); - - policy.dataSubmissionPolicyNotifiedDate = now; - do_check_eq(policyPrefs.get("dataSubmissionPolicyNotifiedTime"), nowT); - do_check_neq(policy.dataSubmissionPolicyNotifiedDate, null); - do_check_eq(policy.dataSubmissionPolicyNotifiedDate.getTime(), nowT); - - policy.dataSubmissionEnabled = false; - do_check_false(policyPrefs.get("dataSubmissionEnabled", true)); - do_check_false(policy.dataSubmissionEnabled); - - let new_version = DATAREPORTING_POLICY_VERSION + 1; - policy.dataSubmissionPolicyAcceptedVersion = new_version; - do_check_eq(policyPrefs.get("dataSubmissionPolicyAcceptedVersion"), new_version); - - do_check_false(policy.dataSubmissionPolicyBypassNotification); - policy.dataSubmissionPolicyBypassNotification = true; - do_check_true(policy.dataSubmissionPolicyBypassNotification); - do_check_true(policyPrefs.get("dataSubmissionPolicyBypassNotification")); - - policy.lastDataSubmissionRequestedDate = now; - do_check_eq(hrPrefs.get("lastDataSubmissionRequestedTime"), nowT); - do_check_eq(policy.lastDataSubmissionRequestedDate.getTime(), nowT); - - policy.lastDataSubmissionSuccessfulDate = now; - do_check_eq(hrPrefs.get("lastDataSubmissionSuccessfulTime"), nowT); - do_check_eq(policy.lastDataSubmissionSuccessfulDate.getTime(), nowT); - - policy.lastDataSubmissionFailureDate = now; - do_check_eq(hrPrefs.get("lastDataSubmissionFailureTime"), nowT); - do_check_eq(policy.lastDataSubmissionFailureDate.getTime(), nowT); - - policy.nextDataSubmissionDate = now; - do_check_eq(hrPrefs.get("nextDataSubmissionTime"), nowT); - do_check_eq(policy.nextDataSubmissionDate.getTime(), nowT); - - policy.currentDaySubmissionFailureCount = 2; - do_check_eq(hrPrefs.get("currentDaySubmissionFailureCount", 0), 2); - do_check_eq(policy.currentDaySubmissionFailureCount, 2); - - policy.pendingDeleteRemoteData = true; - do_check_true(hrPrefs.get("pendingDeleteRemoteData")); - do_check_true(policy.pendingDeleteRemoteData); - - policy.healthReportUploadEnabled = false; - do_check_false(hrPrefs.get("uploadEnabled")); - do_check_false(policy.healthReportUploadEnabled); - - do_check_false(policy.healthReportUploadLocked); - hrPrefs.lock("uploadEnabled"); - do_check_true(policy.healthReportUploadLocked); - hrPrefs.unlock("uploadEnabled"); - do_check_false(policy.healthReportUploadLocked); - - run_next_test(); -}); - -add_task(function test_migratePrefs () { - let [policy, policyPrefs, hrPrefs, listener] = getPolicy("migratePrefs"); - let outdated_prefs = { - dataSubmissionPolicyAccepted: true, - dataSubmissionPolicyBypassAcceptance: true, - dataSubmissionPolicyResponseType: "something", - dataSubmissionPolicyResponseTime: Date.now() + "", - }; - - // Test removal of old prefs. - for (let name in outdated_prefs) { - policyPrefs.set(name, outdated_prefs[name]); - } - policy._migratePrefs(); - for (let name in outdated_prefs) { - do_check_false(policyPrefs.has(name)); - } -}); - -add_task(function test_userNotifiedOfCurrentPolicy () { - let [policy, policyPrefs, hrPrefs, listener] = getPolicy("initial_submission_notification"); - - do_check_false(policy.userNotifiedOfCurrentPolicy, - "The initial state should be unnotified."); - do_check_eq(policy.dataSubmissionPolicyNotifiedDate.getTime(), 0); - - policy.dataSubmissionPolicyAcceptedVersion = DATAREPORTING_POLICY_VERSION; - do_check_false(policy.userNotifiedOfCurrentPolicy, - "The default state of the date should have a time of 0 and it should therefore fail"); - do_check_eq(policy.dataSubmissionPolicyNotifiedDate.getTime(), 0, - "Updating the accepted version should not set a notified date."); - - policy._recordDataPolicyNotification(new Date(), DATAREPORTING_POLICY_VERSION); - do_check_true(policy.userNotifiedOfCurrentPolicy, - "Using the proper API causes user notification to report as true."); - - // It is assumed that later versions of the policy will incorporate previous - // ones, therefore this should also return true. - policy._recordDataPolicyNotification(new Date(), DATAREPORTING_POLICY_VERSION); - policy.dataSubmissionPolicyAcceptedVersion = DATAREPORTING_POLICY_VERSION + 1; - do_check_true(policy.userNotifiedOfCurrentPolicy, 'A future version of the policy should pass.'); - - policy._recordDataPolicyNotification(new Date(), DATAREPORTING_POLICY_VERSION); - policy.dataSubmissionPolicyAcceptedVersion = DATAREPORTING_POLICY_VERSION - 1; - do_check_false(policy.userNotifiedOfCurrentPolicy, 'A previous version of the policy should fail.'); -}); - -add_task(function* test_notification_displayed () { - let [policy, policyPrefs, hrPrefs, listener] = getPolicy("notification_accept_displayed"); - - do_check_eq(listener.requestDataUploadCount, 0); - do_check_eq(listener.notifyUserCount, 0); - do_check_eq(policy.dataSubmissionPolicyNotifiedDate.getTime(), 0); - - // Uploads will trigger user notifications as needed. - policy.checkStateAndTrigger(); - do_check_eq(listener.notifyUserCount, 1); - do_check_eq(listener.requestDataUploadCount, 0); - - yield ensureUserNotifiedAndTrigger(policy); - - do_check_eq(listener.notifyUserCount, 1); - do_check_true(policy.dataSubmissionPolicyNotifiedDate.getTime() > 0); - do_check_true(policy.userNotifiedOfCurrentPolicy); -}); - -add_task(function* test_submission_kill_switch() { - let [policy, policyPrefs, hrPrefs, listener] = getPolicy("submission_kill_switch"); - policy.nextDataSubmissionDate = new Date(Date.now() - 24 * 60 * 60 * 1000); - policy.checkStateAndTrigger(); - do_check_eq(listener.requestDataUploadCount, 0); - yield ensureUserNotifiedAndTrigger(policy); - do_check_eq(listener.requestDataUploadCount, 1); - - defineNow(policy, - new Date(Date.now() + policy.SUBMISSION_REQUEST_EXPIRE_INTERVAL_MSEC + 100)); - policy.dataSubmissionEnabled = false; - policy.checkStateAndTrigger(); - do_check_eq(listener.requestDataUploadCount, 1); -}); - -add_task(function* test_upload_kill_switch() { - let [policy, policyPrefs, hrPrefs, listener] = getPolicy("upload_kill_switch"); - - yield ensureUserNotifiedAndTrigger(policy); - defineNow(policy, policy.nextDataSubmissionDate); - - // So that we don't trigger deletions, which cause uploads to be delayed. - hrPrefs.ignore("uploadEnabled", policy.uploadEnabledObserver); - - policy.healthReportUploadEnabled = false; - yield policy.checkStateAndTrigger(); - do_check_eq(listener.requestDataUploadCount, 0); - policy.healthReportUploadEnabled = true; - yield ensureUserNotifiedAndTrigger(policy); - do_check_eq(listener.requestDataUploadCount, 1); -}); - -add_task(function* test_data_submission_no_data() { - let [policy, policyPrefs, hrPrefs, listener] = getPolicy("data_submission_no_data"); - - let now = new Date(policy.nextDataSubmissionDate.getTime() + 1); - defineNow(policy, now); - do_check_eq(listener.requestDataUploadCount, 0); - yield ensureUserNotifiedAndTrigger(policy); - do_check_eq(listener.requestDataUploadCount, 1); - listener.lastDataRequest.onNoDataAvailable(); - - // The next trigger should try again. - defineNow(policy, new Date(now.getTime() + 155 * 60 * 1000)); - policy.checkStateAndTrigger(); - do_check_eq(listener.requestDataUploadCount, 2); - }); - -add_task(function* test_data_submission_submit_failure_hard() { - let [policy, policyPrefs, hrPrefs, listener] = getPolicy("data_submission_submit_failure_hard"); - - let nextDataSubmissionDate = policy.nextDataSubmissionDate; - let now = new Date(policy.nextDataSubmissionDate.getTime() + 1); - defineNow(policy, now); - - yield ensureUserNotifiedAndTrigger(policy); - do_check_eq(listener.requestDataUploadCount, 1); - yield listener.lastDataRequest.onSubmissionFailureHard(); - do_check_eq(listener.lastDataRequest.state, - listener.lastDataRequest.SUBMISSION_FAILURE_HARD); - - let expected = new Date(now.getTime() + 24 * 60 * 60 * 1000); - do_check_eq(policy.nextDataSubmissionDate.getTime(), expected.getTime()); - - defineNow(policy, new Date(now.getTime() + 10)); - policy.checkStateAndTrigger(); - do_check_eq(listener.requestDataUploadCount, 1); -}); - -add_task(function* test_data_submission_submit_try_again() { - let [policy, policyPrefs, hrPrefs, listener] = getPolicy("data_submission_failure_soft"); - - let nextDataSubmissionDate = policy.nextDataSubmissionDate; - let now = new Date(policy.nextDataSubmissionDate.getTime()); - defineNow(policy, now); - yield ensureUserNotifiedAndTrigger(policy); - yield listener.lastDataRequest.onSubmissionFailureSoft(); - do_check_eq(policy.nextDataSubmissionDate.getTime(), - nextDataSubmissionDate.getTime() + 15 * 60 * 1000); -}); - -add_task(function* test_submission_daily_scheduling() { - let [policy, policyPrefs, hrPrefs, listener] = getPolicy("submission_daily_scheduling"); - - let nextDataSubmissionDate = policy.nextDataSubmissionDate; - - // Skip ahead to next submission date. We should get a submission request. - let now = new Date(policy.nextDataSubmissionDate.getTime()); - defineNow(policy, now); - yield ensureUserNotifiedAndTrigger(policy); - do_check_eq(listener.requestDataUploadCount, 1); - do_check_eq(policy.lastDataSubmissionRequestedDate.getTime(), now.getTime()); - - let finishedDate = new Date(now.getTime() + 250); - defineNow(policy, new Date(finishedDate.getTime() + 50)); - yield listener.lastDataRequest.onSubmissionSuccess(finishedDate); - do_check_eq(policy.lastDataSubmissionSuccessfulDate.getTime(), finishedDate.getTime()); - - // Next scheduled submission should be exactly 1 day after the reported - // submission success. - - let nextScheduled = new Date(finishedDate.getTime() + 24 * 60 * 60 * 1000); - do_check_eq(policy.nextDataSubmissionDate.getTime(), nextScheduled.getTime()); - - // Fast forward some arbitrary time. We shouldn't do any work yet. - defineNow(policy, new Date(now.getTime() + 40000)); - policy.checkStateAndTrigger(); - do_check_eq(listener.requestDataUploadCount, 1); - - defineNow(policy, nextScheduled); - policy.checkStateAndTrigger(); - do_check_eq(listener.requestDataUploadCount, 2); - yield listener.lastDataRequest.onSubmissionSuccess(new Date(nextScheduled.getTime() + 200)); - do_check_eq(policy.nextDataSubmissionDate.getTime(), - new Date(nextScheduled.getTime() + 24 * 60 * 60 * 1000 + 200).getTime()); -}); - -add_task(function* test_submission_far_future_scheduling() { - let [policy, policyPrefs, hrPrefs, listener] = getPolicy("submission_far_future_scheduling"); - - let now = new Date(Date.now() - 24 * 60 * 60 * 1000); - defineNow(policy, now); - yield ensureUserNotifiedAndTrigger(policy); - - let nextDate = policy._futureDate(3 * 24 * 60 * 60 * 1000 - 1); - policy.nextDataSubmissionDate = nextDate; - policy.checkStateAndTrigger(); - do_check_true(policy.dataSubmissionPolicyAcceptedVersion >= DATAREPORTING_POLICY_VERSION); - do_check_eq(listener.requestDataUploadCount, 0); - do_check_eq(policy.nextDataSubmissionDate.getTime(), nextDate.getTime()); - - policy.nextDataSubmissionDate = new Date(nextDate.getTime() + 1); - policy.checkStateAndTrigger(); - do_check_eq(listener.requestDataUploadCount, 0); - do_check_eq(policy.nextDataSubmissionDate.getTime(), - policy._futureDate(24 * 60 * 60 * 1000).getTime()); -}); - -add_task(function* test_submission_backoff() { - let [policy, policyPrefs, hrPrefs, listener] = getPolicy("submission_backoff"); - - do_check_eq(policy.FAILURE_BACKOFF_INTERVALS.length, 2); - - - let now = new Date(policy.nextDataSubmissionDate.getTime()); - defineNow(policy, now); - yield ensureUserNotifiedAndTrigger(policy); - do_check_eq(listener.requestDataUploadCount, 1); - do_check_eq(policy.currentDaySubmissionFailureCount, 0); - - now = new Date(now.getTime() + 5000); - defineNow(policy, now); - - // On first soft failure we should back off by scheduled interval. - yield listener.lastDataRequest.onSubmissionFailureSoft(); - do_check_eq(policy.currentDaySubmissionFailureCount, 1); - do_check_eq(policy.nextDataSubmissionDate.getTime(), - new Date(now.getTime() + policy.FAILURE_BACKOFF_INTERVALS[0]).getTime()); - do_check_eq(policy.lastDataSubmissionFailureDate.getTime(), now.getTime()); - - // Should not request submission until scheduled. - now = new Date(policy.nextDataSubmissionDate.getTime() - 1); - defineNow(policy, now); - policy.checkStateAndTrigger(); - do_check_eq(listener.requestDataUploadCount, 1); - - // 2nd request for submission. - now = new Date(policy.nextDataSubmissionDate.getTime()); - defineNow(policy, now); - policy.checkStateAndTrigger(); - do_check_eq(listener.requestDataUploadCount, 2); - - now = new Date(now.getTime() + 5000); - defineNow(policy, now); - - // On second failure we should back off by more. - yield listener.lastDataRequest.onSubmissionFailureSoft(); - do_check_eq(policy.currentDaySubmissionFailureCount, 2); - do_check_eq(policy.nextDataSubmissionDate.getTime(), - new Date(now.getTime() + policy.FAILURE_BACKOFF_INTERVALS[1]).getTime()); - - now = new Date(policy.nextDataSubmissionDate.getTime()); - defineNow(policy, now); - policy.checkStateAndTrigger(); - do_check_eq(listener.requestDataUploadCount, 3); - - now = new Date(now.getTime() + 5000); - defineNow(policy, now); - - // On 3rd failure we should back off by a whole day. - yield listener.lastDataRequest.onSubmissionFailureSoft(); - do_check_eq(policy.currentDaySubmissionFailureCount, 0); - do_check_eq(policy.nextDataSubmissionDate.getTime(), - new Date(now.getTime() + 24 * 60 * 60 * 1000).getTime()); -}); - -// Ensure that only one submission request can be active at a time. -add_task(function* test_submission_expiring() { - let [policy, policyPrefs, hrPrefs, listener] = getPolicy("submission_expiring"); - - let nextDataSubmission = policy.nextDataSubmissionDate; - let now = new Date(policy.nextDataSubmissionDate.getTime()); - defineNow(policy, now); - yield ensureUserNotifiedAndTrigger(policy); - do_check_eq(listener.requestDataUploadCount, 1); - defineNow(policy, new Date(now.getTime() + 500)); - policy.checkStateAndTrigger(); - do_check_eq(listener.requestDataUploadCount, 1); - - defineNow(policy, new Date(policy.now().getTime() + - policy.SUBMISSION_REQUEST_EXPIRE_INTERVAL_MSEC)); - - policy.checkStateAndTrigger(); - do_check_eq(listener.requestDataUploadCount, 2); -}); - -add_task(function* test_delete_remote_data() { - let [policy, policyPrefs, hrPrefs, listener] = getPolicy("delete_remote_data"); - - do_check_false(policy.pendingDeleteRemoteData); - let nextSubmissionDate = policy.nextDataSubmissionDate; - - let now = new Date(); - defineNow(policy, now); - - policy.deleteRemoteData(); - do_check_true(policy.pendingDeleteRemoteData); - do_check_neq(nextSubmissionDate.getTime(), - policy.nextDataSubmissionDate.getTime()); - do_check_eq(now.getTime(), policy.nextDataSubmissionDate.getTime()); - - do_check_eq(listener.requestRemoteDeleteCount, 1); - do_check_true(listener.lastRemoteDeleteRequest.isDelete); - defineNow(policy, policy._futureDate(1000)); - - yield listener.lastRemoteDeleteRequest.onSubmissionSuccess(policy.now()); - do_check_false(policy.pendingDeleteRemoteData); -}); - -// Ensure that deletion requests take priority over regular data submission. -add_task(function* test_delete_remote_data_priority() { - let [policy, policyPrefs, hrPrefs, listener] = getPolicy("delete_remote_data_priority"); - - let now = new Date(); - defineNow(policy, new Date(now.getTime() + 3 * 24 * 60 * 60 * 1000)); - - yield ensureUserNotifiedAndTrigger(policy); - do_check_eq(listener.requestDataUploadCount, 1); - policy._inProgressSubmissionRequest = null; - - policy.deleteRemoteData(); - policy.checkStateAndTrigger(); - - do_check_eq(listener.requestRemoteDeleteCount, 1); - do_check_eq(listener.requestDataUploadCount, 1); -}); - -add_test(function test_delete_remote_data_backoff() { - let [policy, policyPrefs, hrPrefs, listener] = getPolicy("delete_remote_data_backoff"); - - let now = new Date(); - defineNow(policy, now); - policy.nextDataSubmissionDate = now; - policy.deleteRemoteData(); - - policy.checkStateAndTrigger(); - do_check_eq(listener.requestRemoteDeleteCount, 1); - defineNow(policy, policy._futureDate(1000)); - policy.checkStateAndTrigger(); - do_check_eq(listener.requestDataUploadCount, 0); - do_check_eq(listener.requestRemoteDeleteCount, 1); - - defineNow(policy, policy._futureDate(500)); - listener.lastRemoteDeleteRequest.onSubmissionFailureSoft(); - defineNow(policy, policy._futureDate(50)); - - policy.checkStateAndTrigger(); - do_check_eq(listener.requestRemoteDeleteCount, 1); - - defineNow(policy, policy._futureDate(policy.FAILURE_BACKOFF_INTERVALS[0] - 50)); - policy.checkStateAndTrigger(); - do_check_eq(listener.requestRemoteDeleteCount, 2); - - run_next_test(); -}); - -// If we request delete while an upload is in progress, delete should be -// scheduled immediately after upload. -add_task(function* test_delete_remote_data_in_progress_upload() { - let [policy, policyPrefs, hrPrefs, listener] = getPolicy("delete_remote_data_in_progress_upload"); - - defineNow(policy, policy.nextDataSubmissionDate); - - yield ensureUserNotifiedAndTrigger(policy); - do_check_eq(listener.requestDataUploadCount, 1); - defineNow(policy, policy._futureDate(50 * 1000)); - - // If we request a delete during a pending request, nothing should be done. - policy.deleteRemoteData(); - policy.checkStateAndTrigger(); - do_check_eq(listener.requestDataUploadCount, 1); - do_check_eq(listener.requestRemoteDeleteCount, 0); - - // Now wait a little bit and finish the request. - defineNow(policy, policy._futureDate(10 * 1000)); - yield listener.lastDataRequest.onSubmissionSuccess(policy._futureDate(1000)); - defineNow(policy, policy._futureDate(5000)); - - policy.checkStateAndTrigger(); - do_check_eq(listener.requestDataUploadCount, 1); - do_check_eq(listener.requestRemoteDeleteCount, 1); -}); - -add_test(function test_polling() { - let [policy, policyPrefs, hrPrefs, listener] = getPolicy("polling"); - let intended = 500; - let acceptable = 250; // Because nsITimer doesn't guarantee times. - - // Ensure checkStateAndTrigger is called at a regular interval. - let then = Date.now(); - print("Starting run: " + then); - Object.defineProperty(policy, "POLL_INTERVAL_MSEC", { - value: intended, - }); - let count = 0; - - Object.defineProperty(policy, "checkStateAndTrigger", { - value: function fakeCheckStateAndTrigger() { - let now = Date.now(); - let after = now - then; - count++; - - print("Polled at " + now + " after " + after + "ms, intended " + intended); - do_check_true(after >= acceptable); - DataReportingPolicy.prototype.checkStateAndTrigger.call(policy); - - if (count >= 2) { - policy.stopPolling(); - - do_check_eq(listener.requestDataUploadCount, 0); - - run_next_test(); - } - - // "Specified timer period will be at least the time between when - // processing for last firing the callback completes and when the next - // firing occurs." - // - // That means we should set 'then' at the *end* of our handler, not - // earlier. - then = Date.now(); - } - }); - policy.startPolling(); -}); - -add_task(function* test_record_health_report_upload_enabled() { - let [policy, policyPrefs, hrPrefs, listener] = getPolicy("record_health_report_upload_enabled"); - - // Preconditions. - do_check_false(policy.pendingDeleteRemoteData); - do_check_true(policy.healthReportUploadEnabled); - do_check_eq(listener.requestRemoteDeleteCount, 0); - - // User intent to disable should immediately result in a pending - // delete request. - policy.recordHealthReportUploadEnabled(false, "testing 1 2 3"); - do_check_false(policy.healthReportUploadEnabled); - do_check_true(policy.pendingDeleteRemoteData); - do_check_eq(listener.requestRemoteDeleteCount, 1); - - // Fulfilling it should make it go away. - yield listener.lastRemoteDeleteRequest.onNoDataAvailable(); - do_check_false(policy.pendingDeleteRemoteData); - - // User intent to enable should get us back to default state. - policy.recordHealthReportUploadEnabled(true, "testing 1 2 3"); - do_check_false(policy.pendingDeleteRemoteData); - do_check_true(policy.healthReportUploadEnabled); -}); - -add_test(function test_pref_change_initiates_deletion() { - let [policy, policyPrefs, hrPrefs, listener] = getPolicy("record_health_report_upload_enabled"); - - // Preconditions. - do_check_false(policy.pendingDeleteRemoteData); - do_check_true(policy.healthReportUploadEnabled); - do_check_eq(listener.requestRemoteDeleteCount, 0); - - // User intent to disable should indirectly result in a pending - // delete request, because the policy is watching for the pref - // to change. - Object.defineProperty(policy, "deleteRemoteData", { - value: function deleteRemoteDataProxy() { - do_check_false(policy.healthReportUploadEnabled); - do_check_false(policy.pendingDeleteRemoteData); // Just called. - - run_next_test(); - }, - }); - - hrPrefs.set("uploadEnabled", false); -}); - -add_task(function* test_policy_version() { - let policy, policyPrefs, hrPrefs, listener, now, firstRunTime; - function createPolicy(shouldBeNotified = false, - currentPolicyVersion = 1, minimumPolicyVersion = 1, - branchMinimumVersionOverride) { - [policy, policyPrefs, hrPrefs, listener] = - getPolicy("policy_version_test", currentPolicyVersion, - minimumPolicyVersion, branchMinimumVersionOverride); - let firstRun = now === undefined; - if (firstRun) { - firstRunTime = policy.firstRunDate.getTime(); - do_check_true(firstRunTime > 0); - now = new Date(policy.firstRunDate.getTime()); - } - else { - // The first-run time should not be reset even after policy-version - // upgrades. - do_check_eq(policy.firstRunDate.getTime(), firstRunTime); - } - defineNow(policy, now); - do_check_eq(policy.userNotifiedOfCurrentPolicy, shouldBeNotified); - } - - function* triggerPolicyCheckAndEnsureNotified(notified = true) { - policy.checkStateAndTrigger(); - do_check_eq(listener.notifyUserCount, Number(notified)); - if (notified) { - policy.ensureUserNotified(); - yield listener.lastNotifyRequest.deferred.promise; - do_check_true(policy.userNotifiedOfCurrentPolicy); - do_check_eq(policyPrefs.get("dataSubmissionPolicyAcceptedVersion"), - policyPrefs.get("currentPolicyVersion")); - } - } - - createPolicy(); - yield triggerPolicyCheckAndEnsureNotified(); - - // We shouldn't be notified again if the current version is still valid; - createPolicy(true); - yield triggerPolicyCheckAndEnsureNotified(false); - - // Just increasing the current version isn't enough. The minimum - // version must be changed. - let currentPolicyVersion = policyPrefs.get("currentPolicyVersion"); - let minimumPolicyVersion = policyPrefs.get("minimumPolicyVersion"); - createPolicy(false, ++currentPolicyVersion, minimumPolicyVersion); - yield triggerPolicyCheckAndEnsureNotified(true); - do_check_eq(policyPrefs.get("dataSubmissionPolicyAcceptedVersion"), currentPolicyVersion); - - // Increase the minimum policy version and check if we're notified. - - createPolicy(true, currentPolicyVersion, ++minimumPolicyVersion); - do_check_true(policyPrefs.has("dataSubmissionPolicyAcceptedVersion")); - yield triggerPolicyCheckAndEnsureNotified(false); - - - // Test increasing the minimum version just on the current channel. - createPolicy(true, currentPolicyVersion, minimumPolicyVersion); - yield triggerPolicyCheckAndEnsureNotified(false); - createPolicy(false, ++currentPolicyVersion, minimumPolicyVersion, minimumPolicyVersion + 1); - yield triggerPolicyCheckAndEnsureNotified(true); -}); diff --git a/services/datareporting/tests/xpcshell/test_session_recorder.js b/services/datareporting/tests/xpcshell/test_session_recorder.js deleted file mode 100644 index 1e8b04233d..0000000000 --- a/services/datareporting/tests/xpcshell/test_session_recorder.js +++ /dev/null @@ -1,306 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -"use strict"; - -const {utils: Cu} = Components; - -Cu.import("resource://gre/modules/Promise.jsm"); -Cu.import("resource://gre/modules/services/datareporting/sessions.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://services-common/utils.js"); - - -function run_test() { - run_next_test(); -} - -function monkeypatchStartupInfo(recorder, start=new Date(), offset=500) { - Object.defineProperty(recorder, "_getStartupInfo", { - value: function _getStartupInfo() { - return { - process: start, - main: new Date(start.getTime() + offset), - firstPaint: new Date(start.getTime() + 2 * offset), - sessionRestored: new Date(start.getTime() + 3 * offset), - }; - } - }); -} - -function sleep(wait) { - let deferred = Promise.defer(); - - let timer = CommonUtils.namedTimer(function onTimer() { - deferred.resolve(); - }, wait, deferred.promise, "_sleepTimer"); - - return deferred.promise; -} - -function getRecorder(name, start, offset) { - let recorder = new SessionRecorder("testing." + name + "."); - monkeypatchStartupInfo(recorder, start, offset); - - return recorder; -} - -add_test(function test_basic() { - let recorder = getRecorder("basic"); - recorder.onStartup(); - recorder.onShutdown(); - - run_next_test(); -}); - -add_task(function test_current_properties() { - let now = new Date(); - let recorder = getRecorder("current_properties", now); - yield sleep(25); - recorder.onStartup(); - - do_check_eq(recorder.startDate.getTime(), now.getTime()); - do_check_eq(recorder.activeTicks, 0); - do_check_true(recorder.fineTotalTime > 0); - do_check_eq(recorder.main, 500); - do_check_eq(recorder.firstPaint, 1000); - do_check_eq(recorder.sessionRestored, 1500); - - recorder.incrementActiveTicks(); - do_check_eq(recorder.activeTicks, 1); - - recorder._startDate = new Date(Date.now() - 1000); - recorder.updateTotalTime(); - do_check_eq(recorder.totalTime, 1); - - recorder.onShutdown(); -}); - -// If startup info isn't present yet, we should install a timer and get -// it eventually. -add_task(function test_current_availability() { - let recorder = new SessionRecorder("testing.current_availability."); - let now = new Date(); - - Object.defineProperty(recorder, "_getStartupInfo", { - value: function _getStartupInfo() { - return { - process: now, - main: new Date(now.getTime() + 500), - firstPaint: new Date(now.getTime() + 1000), - }; - }, - writable: true, - }); - - Object.defineProperty(recorder, "STARTUP_RETRY_INTERVAL_MS", { - value: 100, - }); - - let oldRecord = recorder.recordStartupFields; - let recordCount = 0; - - Object.defineProperty(recorder, "recordStartupFields", { - value: function () { - recordCount++; - return oldRecord.call(recorder); - } - }); - - do_check_null(recorder._timer); - recorder.onStartup(); - do_check_eq(recordCount, 1); - do_check_eq(recorder.sessionRestored, -1); - do_check_neq(recorder._timer, null); - - yield sleep(125); - do_check_eq(recordCount, 2); - yield sleep(100); - do_check_eq(recordCount, 3); - do_check_eq(recorder.sessionRestored, -1); - - monkeypatchStartupInfo(recorder, now); - yield sleep(100); - do_check_eq(recordCount, 4); - do_check_eq(recorder.sessionRestored, 1500); - - // The timer should be removed and we should not fire again. - do_check_null(recorder._timer); - yield sleep(100); - do_check_eq(recordCount, 4); - - recorder.onShutdown(); -}); - -add_test(function test_timer_clear_on_shutdown() { - let recorder = new SessionRecorder("testing.timer_clear_on_shutdown."); - let now = new Date(); - - Object.defineProperty(recorder, "_getStartupInfo", { - value: function _getStartupInfo() { - return { - process: now, - main: new Date(now.getTime() + 500), - firstPaint: new Date(now.getTime() + 1000), - }; - }, - }); - - do_check_null(recorder._timer); - recorder.onStartup(); - do_check_neq(recorder._timer, null); - - recorder.onShutdown(); - do_check_null(recorder._timer); - - run_next_test(); -}); - -add_task(function test_previous_clean() { - let now = new Date(); - let recorder = getRecorder("previous_clean", now); - yield sleep(25); - recorder.onStartup(); - - recorder.incrementActiveTicks(); - recorder.incrementActiveTicks(); - - yield sleep(25); - recorder.onShutdown(); - - let total = recorder.totalTime; - - yield sleep(25); - let now2 = new Date(); - let recorder2 = getRecorder("previous_clean", now2, 100); - yield sleep(25); - recorder2.onStartup(); - - do_check_eq(recorder2.startDate.getTime(), now2.getTime()); - do_check_eq(recorder2.main, 100); - do_check_eq(recorder2.firstPaint, 200); - do_check_eq(recorder2.sessionRestored, 300); - - let sessions = recorder2.getPreviousSessions(); - do_check_eq(Object.keys(sessions).length, 1); - do_check_true(0 in sessions); - let session = sessions[0]; - do_check_true(session.clean); - do_check_eq(session.startDate.getTime(), now.getTime()); - do_check_eq(session.main, 500); - do_check_eq(session.firstPaint, 1000); - do_check_eq(session.sessionRestored, 1500); - do_check_eq(session.totalTime, total); - do_check_eq(session.activeTicks, 2); - - recorder2.onShutdown(); -}); - -add_task(function test_previous_abort() { - let now = new Date(); - let recorder = getRecorder("previous_abort", now); - yield sleep(25); - recorder.onStartup(); - recorder.incrementActiveTicks(); - yield sleep(25); - let total = recorder.totalTime; - yield sleep(25); - - let now2 = new Date(); - let recorder2 = getRecorder("previous_abort", now2); - yield sleep(25); - recorder2.onStartup(); - - let sessions = recorder2.getPreviousSessions(); - do_check_eq(Object.keys(sessions).length, 1); - do_check_true(0 in sessions); - let session = sessions[0]; - do_check_false(session.clean); - do_check_eq(session.totalTime, total); - - recorder.onShutdown(); - recorder2.onShutdown(); -}); - -add_task(function test_multiple_sessions() { - for (let i = 0; i < 10; i++) { - let recorder = getRecorder("multiple_sessions"); - yield sleep(25); - recorder.onStartup(); - for (let j = 0; j < i; j++) { - recorder.incrementActiveTicks(); - } - yield sleep(25); - recorder.onShutdown(); - yield sleep(25); - } - - let recorder = getRecorder("multiple_sessions"); - recorder.onStartup(); - - let sessions = recorder.getPreviousSessions(); - do_check_eq(Object.keys(sessions).length, 10); - - for (let [i, session] in Iterator(sessions)) { - do_check_eq(session.activeTicks, i); - - if (i > 0) { - do_check_true(session.startDate.getTime() > sessions[i-1].startDate.getTime()); - } - } - - // #6 is preserved since >=. - let threshold = sessions[6].startDate; - recorder.pruneOldSessions(threshold); - - sessions = recorder.getPreviousSessions(); - do_check_eq(Object.keys(sessions).length, 4); - - recorder.pruneOldSessions(threshold); - sessions = recorder.getPreviousSessions(); - do_check_eq(Object.keys(sessions).length, 4); - do_check_eq(recorder._prunedIndex, 5); - - recorder.onShutdown(); -}); - -add_task(function test_record_activity() { - let recorder = getRecorder("record_activity"); - yield sleep(25); - recorder.onStartup(); - let total = recorder.totalTime; - yield sleep(25); - - for (let i = 0; i < 3; i++) { - Services.obs.notifyObservers(null, "user-interaction-active", null); - yield sleep(25); - do_check_true(recorder.fineTotalTime > total); - total = recorder.fineTotalTime; - } - - do_check_eq(recorder.activeTicks, 3); - - // Now send inactive. We should increment total time but not active. - Services.obs.notifyObservers(null, "user-interaction-inactive", null); - do_check_eq(recorder.activeTicks, 3); - do_check_true(recorder.fineTotalTime > total); - total = recorder.fineTotalTime; - yield sleep(25); - - // If we send active again, this should be counted as inactive. - Services.obs.notifyObservers(null, "user-interaction-active", null); - do_check_eq(recorder.activeTicks, 3); - do_check_true(recorder.fineTotalTime > total); - total = recorder.fineTotalTime; - yield sleep(25); - - // If we send active again, this should be counted as active. - Services.obs.notifyObservers(null, "user-interaction-active", null); - do_check_eq(recorder.activeTicks, 4); - - Services.obs.notifyObservers(null, "user-interaction-active", null); - do_check_eq(recorder.activeTicks, 5); - - recorder.onShutdown(); -}); - diff --git a/services/datareporting/tests/xpcshell/xpcshell.ini b/services/datareporting/tests/xpcshell/xpcshell.ini deleted file mode 100644 index fbd88acf2b..0000000000 --- a/services/datareporting/tests/xpcshell/xpcshell.ini +++ /dev/null @@ -1,8 +0,0 @@ -[DEFAULT] -head = head.js -tail = -skip-if = toolkit == 'android' || toolkit == 'gonk' - -[test_policy.js] -[test_session_recorder.js] -[test_client_id.js] diff --git a/services/docs/datareporting.rst b/services/docs/datareporting.rst deleted file mode 100644 index 2b174fc7a7..0000000000 --- a/services/docs/datareporting.rst +++ /dev/null @@ -1,28 +0,0 @@ -.. _data_reporting_service: - -====================== -Data Reporting Service -====================== - -``/services/datareporting`` contains files related to an XPCOM service -that collects and reports data within Gecko applications. - -The important files in this directory are: - -DataReportingService.js - An XPCOM service that coordinates collection and reporting of data. - -policy.jsm - A module containing the logic for coordinating and driving collection - and upload of data. - -sessions.jsm - Records Gecko application session history. This is loaded as part of - the XPCOM service because it needs to capture state from very early in - the application lifecycle. Bug 841561 tracks implementing this in C++. - -There is other code in the tree that collects and uploads data. The -original intent of this directory and XPCOM service was to serve as a -focal point for the coordination of all this activity so that it could -all be done consistently and properly. This vision may or may not be fully -realized. diff --git a/services/docs/index.rst b/services/docs/index.rst index b1f53cd4da..a6e71ab83e 100644 --- a/services/docs/index.rst +++ b/services/docs/index.rst @@ -14,4 +14,3 @@ the directory remains. :maxdepth: 1 metrics - datareporting diff --git a/services/healthreport/HealthReport.jsm b/services/healthreport/HealthReport.jsm deleted file mode 100644 index 39fafd783c..0000000000 --- a/services/healthreport/HealthReport.jsm +++ /dev/null @@ -1,43 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -"use strict"; - -this.EXPORTED_SYMBOLS = [ - "HealthReporter", - "AddonsProvider", - "AppInfoProvider", - "CrashesProvider", - "HealthReportProvider", - "HotfixProvider", - "Metrics", - "PlacesProvider", - "ProfileMetadataProvider", - "SearchesProvider", - "SessionsProvider", - "SysInfoProvider", -]; - -const {classes: Cc, interfaces: Ci, utils: Cu} = Components; - -const MILLISECONDS_PER_DAY = 24 * 60 * 60 * 1000; - -// We concatenate the JSMs together to eliminate compartment overhead. -// This is a giant hack until compartment overhead is no longer an -// issue. -#define MERGED_COMPARTMENT - -#include ../common/async.js -; -#include ../common/bagheeraclient.js -; -#include ../metrics/Metrics.jsm -; -#include healthreporter.jsm -; -#include profile.jsm -; -#include providers.jsm -; - diff --git a/services/healthreport/HealthReportComponents.manifest b/services/healthreport/HealthReportComponents.manifest deleted file mode 100644 index 8dc77890e2..0000000000 --- a/services/healthreport/HealthReportComponents.manifest +++ /dev/null @@ -1,13 +0,0 @@ -# Register Firefox Health Report providers. -category healthreport-js-provider-default AddonsProvider resource://gre/modules/HealthReport.jsm -category healthreport-js-provider-default AppInfoProvider resource://gre/modules/HealthReport.jsm -category healthreport-js-provider-default HealthReportProvider resource://gre/modules/HealthReport.jsm -category healthreport-js-provider-default HotfixProvider resource://gre/modules/HealthReport.jsm -category healthreport-js-provider-default PlacesProvider resource://gre/modules/HealthReport.jsm -category healthreport-js-provider-default ProfileMetadataProvider resource://gre/modules/HealthReport.jsm -category healthreport-js-provider-default SearchesProvider resource://gre/modules/HealthReport.jsm -category healthreport-js-provider-default SessionsProvider resource://gre/modules/HealthReport.jsm -category healthreport-js-provider-default SysInfoProvider resource://gre/modules/HealthReport.jsm - -# No Aurora or Beta providers yet; use the categories -# "healthreport-js-provider-aurora", "healthreport-js-provider-beta". diff --git a/services/healthreport/healthreport-prefs.js b/services/healthreport/healthreport-prefs.js deleted file mode 100644 index c939b96fc7..0000000000 --- a/services/healthreport/healthreport-prefs.js +++ /dev/null @@ -1,37 +0,0 @@ -#filter substitution -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -pref("datareporting.healthreport.currentDaySubmissionFailureCount", 0); -pref("datareporting.healthreport.documentServerURI", "https://fhr.data.mozilla.com/"); -pref("datareporting.healthreport.documentServerNamespace", "metrics"); -pref("datareporting.healthreport.infoURL", "https://www.mozilla.org/legal/privacy/firefox.html#health-report"); -pref("datareporting.healthreport.logging.consoleEnabled", true); -pref("datareporting.healthreport.logging.consoleLevel", "Warn"); -pref("datareporting.healthreport.logging.dumpEnabled", false); -pref("datareporting.healthreport.logging.dumpLevel", "Debug"); -pref("datareporting.healthreport.lastDataSubmissionFailureTime", "0"); -pref("datareporting.healthreport.lastDataSubmissionRequestedTime", "0"); -pref("datareporting.healthreport.lastDataSubmissionSuccessfulTime", "0"); -pref("datareporting.healthreport.nextDataSubmissionTime", "0"); -pref("datareporting.healthreport.pendingDeleteRemoteData", false); - -// Health Report is enabled by default on all channels. -pref("datareporting.healthreport.uploadEnabled", true); - -pref("datareporting.healthreport.service.enabled", true); -pref("datareporting.healthreport.service.loadDelayMsec", 10000); -pref("datareporting.healthreport.service.loadDelayFirstRunMsec", 60000); - -pref("datareporting.healthreport.service.providerCategories", -#if MOZ_UPDATE_CHANNEL == release - "healthreport-js-provider-default" -#elif MOZ_UPDATE_CHANNEL == default - "healthreport-js-provider-default" -#else - "healthreport-js-provider-default,healthreport-js-provider-@MOZ_UPDATE_CHANNEL@" -#endif - ); - -pref("datareporting.healthreport.about.reportUrl", "https://fhr.cdn.mozilla.net/%LOCALE%/"); diff --git a/services/healthreport/healthreporter.jsm b/services/healthreport/healthreporter.jsm deleted file mode 100644 index c9fcb992a3..0000000000 --- a/services/healthreport/healthreporter.jsm +++ /dev/null @@ -1,1545 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef MERGED_COMPARTMENT - -"use strict"; - -this.EXPORTED_SYMBOLS = ["HealthReporter"]; - -const {classes: Cc, interfaces: Ci, utils: Cu} = Components; - -const MILLISECONDS_PER_DAY = 24 * 60 * 60 * 1000; - -Cu.import("resource://gre/modules/Metrics.jsm"); -Cu.import("resource://services-common/async.js"); - -Cu.import("resource://services-common/bagheeraclient.js"); -#endif - -Cu.import("resource://gre/modules/Log.jsm"); -Cu.import("resource://services-common/utils.js"); -Cu.import("resource://gre/modules/Promise.jsm"); -Cu.import("resource://gre/modules/osfile.jsm"); -Cu.import("resource://gre/modules/Preferences.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://gre/modules/Task.jsm"); -Cu.import("resource://gre/modules/TelemetryStopwatch.jsm"); -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); - -XPCOMUtils.defineLazyModuleGetter(this, "UpdateChannel", - "resource://gre/modules/UpdateChannel.jsm"); - -// Oldest year to allow in date preferences. This module was implemented in -// 2012 and no dates older than that should be encountered. -const OLDEST_ALLOWED_YEAR = 2012; - -const DAYS_IN_PAYLOAD = 180; - -const DEFAULT_DATABASE_NAME = "healthreport.sqlite"; - -const TELEMETRY_INIT = "HEALTHREPORT_INIT_MS"; -const TELEMETRY_INIT_FIRSTRUN = "HEALTHREPORT_INIT_FIRSTRUN_MS"; -const TELEMETRY_DB_OPEN = "HEALTHREPORT_DB_OPEN_MS"; -const TELEMETRY_DB_OPEN_FIRSTRUN = "HEALTHREPORT_DB_OPEN_FIRSTRUN_MS"; -const TELEMETRY_GENERATE_PAYLOAD = "HEALTHREPORT_GENERATE_JSON_PAYLOAD_MS"; -const TELEMETRY_JSON_PAYLOAD_SERIALIZE = "HEALTHREPORT_JSON_PAYLOAD_SERIALIZE_MS"; -const TELEMETRY_PAYLOAD_SIZE_UNCOMPRESSED = "HEALTHREPORT_PAYLOAD_UNCOMPRESSED_BYTES"; -const TELEMETRY_PAYLOAD_SIZE_COMPRESSED = "HEALTHREPORT_PAYLOAD_COMPRESSED_BYTES"; -const TELEMETRY_UPLOAD = "HEALTHREPORT_UPLOAD_MS"; -const TELEMETRY_COLLECT_CONSTANT = "HEALTHREPORT_COLLECT_CONSTANT_DATA_MS"; -const TELEMETRY_COLLECT_DAILY = "HEALTHREPORT_COLLECT_DAILY_MS"; -const TELEMETRY_SHUTDOWN = "HEALTHREPORT_SHUTDOWN_MS"; -const TELEMETRY_COLLECT_CHECKPOINT = "HEALTHREPORT_POST_COLLECT_CHECKPOINT_MS"; - - -/** - * Helper type to assist with management of Health Reporter state. - * - * Instances are not meant to be created outside of a HealthReporter instance. - * - * There are two types of IDs associated with clients. - * - * Since the beginning of FHR, there has existed a per-upload ID: a UUID is - * generated at upload time and associated with the state before upload starts. - * That same upload includes a request to delete all other upload IDs known by - * the client. - * - * Per-upload IDs had the unintended side-effect of creating "orphaned" - * records/upload IDs on the server. So, a stable client identifer has been - * introduced. This client identifier is generated when it's missing and sent - * as part of every upload. - * - * There is a high chance we may remove upload IDs in the future. - */ -function HealthReporterState(reporter) { - this._reporter = reporter; - - let profD = OS.Constants.Path.profileDir; - - if (!profD || !profD.length) { - throw new Error("Could not obtain profile directory. OS.File not " + - "initialized properly?"); - } - - this._log = reporter._log; - - this._stateDir = OS.Path.join(profD, "healthreport"); - - // To facilitate testing. - let leaf = reporter._stateLeaf || "state.json"; - - this._filename = OS.Path.join(this._stateDir, leaf); - this._log.debug("Storing state in " + this._filename); - this._s = null; -} - -HealthReporterState.prototype = Object.freeze({ - /** - * Persistent string identifier associated with this client. - */ - get clientID() { - return this._s.clientID; - }, - - /** - * The version associated with the client ID. - */ - get clientIDVersion() { - return this._s.clientIDVersion; - }, - - get lastPingDate() { - return new Date(this._s.lastPingTime); - }, - - get lastSubmitID() { - return this._s.remoteIDs[0]; - }, - - get remoteIDs() { - return this._s.remoteIDs; - }, - - get _lastPayloadPath() { - return OS.Path.join(this._stateDir, "lastpayload.json"); - }, - - init: function () { - return Task.spawn(function* init() { - yield OS.File.makeDir(this._stateDir); - - let drs = Cc["@mozilla.org/datareporting/service;1"] - .getService(Ci.nsISupports) - .wrappedJSObject; - let drsClientID = yield drs.getClientID(); - - let resetObjectState = function () { - this._s = { - // The payload version. This is bumped whenever there is a - // backwards-incompatible change. - v: 1, - // The persistent client identifier. - clientID: drsClientID, - // Denotes the mechanism used to generate the client identifier. - // 1: Random UUID. - clientIDVersion: 1, - // Upload IDs that might be on the server. - remoteIDs: [], - // When we last performed an uploaded. - lastPingTime: 0, - // Tracks whether we removed an outdated payload. - removedOutdatedLastpayload: false, - }; - }.bind(this); - - try { - this._s = yield CommonUtils.readJSON(this._filename); - } catch (ex if ex instanceof OS.File.Error && - ex.becauseNoSuchFile) { - this._log.warn("Saved state file does not exist."); - resetObjectState(); - } catch (ex) { - this._log.error("Exception when reading state from disk: " + - CommonUtils.exceptionStr(ex)); - resetObjectState(); - - // Don't save in case it goes away on next run. - } - - if (typeof(this._s) != "object") { - this._log.warn("Read state is not an object. Resetting state."); - resetObjectState(); - yield this.save(); - } - - if (this._s.v != 1) { - this._log.warn("Unknown version in state file: " + this._s.v); - resetObjectState(); - // We explicitly don't save here in the hopes an application re-upgrade - // comes along and fixes us. - } - - this._s.clientID = drsClientID; - - // Always look for preferences. This ensures that downgrades followed - // by reupgrades don't result in excessive data loss. - for (let promise of this._migratePrefs()) { - yield promise; - } - }.bind(this)); - }, - - save: function () { - this._log.info("Writing state file: " + this._filename); - return CommonUtils.writeJSON(this._s, this._filename); - }, - - addRemoteID: function (id) { - this._log.warn("Recording new remote ID: " + id); - this._s.remoteIDs.push(id); - return this.save(); - }, - - removeRemoteID: function (id) { - return this.removeRemoteIDs(id ? [id] : []); - }, - - removeRemoteIDs: function (ids) { - if (!ids || !ids.length) { - this._log.warn("No IDs passed for removal."); - return Promise.resolve(); - } - - this._log.warn("Removing documents from remote ID list: " + ids); - let filtered = this._s.remoteIDs.filter((x) => ids.indexOf(x) === -1); - - if (filtered.length == this._s.remoteIDs.length) { - return Promise.resolve(); - } - - this._s.remoteIDs = filtered; - return this.save(); - }, - - setLastPingDate: function (date) { - this._s.lastPingTime = date.getTime(); - - return this.save(); - }, - - updateLastPingAndRemoveRemoteID: function (date, id) { - return this.updateLastPingAndRemoveRemoteIDs(date, id ? [id] : []); - }, - - updateLastPingAndRemoveRemoteIDs: function (date, ids) { - if (!ids) { - return this.setLastPingDate(date); - } - - this._log.info("Recording last ping time and deleted remote document."); - this._s.lastPingTime = date.getTime(); - return this.removeRemoteIDs(ids); - }, - - /** - * Reset the client ID to something else. - * Returns a promise that is resolved when completed. - */ - resetClientID: Task.async(function* () { - let drs = Cc["@mozilla.org/datareporting/service;1"] - .getService(Ci.nsISupports) - .wrappedJSObject; - yield drs.resetClientID(); - this._s.clientID = yield drs.getClientID(); - this._log.info("Reset client id to " + this._s.clientID + "."); - - yield this.save(); - }), - - _migratePrefs: function () { - let prefs = this._reporter._prefs; - - let lastID = prefs.get("lastSubmitID", null); - let lastPingDate = CommonUtils.getDatePref(prefs, "lastPingTime", - 0, this._log, OLDEST_ALLOWED_YEAR); - - // If we have state from prefs, migrate and save it to a file then clear - // out old prefs. - if (lastID || (lastPingDate && lastPingDate.getTime() > 0)) { - this._log.warn("Migrating saved state from preferences."); - - if (lastID) { - this._log.info("Migrating last saved ID: " + lastID); - this._s.remoteIDs.push(lastID); - } - - let ourLast = this.lastPingDate; - - if (lastPingDate && lastPingDate.getTime() > ourLast.getTime()) { - this._log.info("Migrating last ping time: " + lastPingDate); - this._s.lastPingTime = lastPingDate.getTime(); - } - - yield this.save(); - prefs.reset(["lastSubmitID", "lastPingTime"]); - } else { - this._log.debug("No prefs data found."); - } - }, -}); - -/** - * This is the abstract base class of `HealthReporter`. It exists so that - * we can sanely divide work on platforms where control of Firefox Health - * Report is outside of Gecko (e.g., Android). - */ -function AbstractHealthReporter(branch, policy, sessionRecorder) { - if (!branch.endsWith(".")) { - throw new Error("Branch must end with a period (.): " + branch); - } - - if (!policy) { - throw new Error("Must provide policy to HealthReporter constructor."); - } - - this._log = Log.repository.getLogger("Services.HealthReport.HealthReporter"); - this._log.info("Initializing health reporter instance against " + branch); - - this._branch = branch; - this._prefs = new Preferences(branch); - - this._policy = policy; - this.sessionRecorder = sessionRecorder; - - this._dbName = this._prefs.get("dbName") || DEFAULT_DATABASE_NAME; - - this._storage = null; - this._storageInProgress = false; - this._providerManager = null; - this._providerManagerInProgress = false; - this._initializeStarted = false; - this._initialized = false; - this._initializeHadError = false; - this._initializedDeferred = Promise.defer(); - this._shutdownRequested = false; - this._shutdownInitiated = false; - this._shutdownComplete = false; - this._deferredShutdown = Promise.defer(); - this._promiseShutdown = this._deferredShutdown.promise; - - this._errors = []; - - this._lastDailyDate = null; - - // Yes, this will probably run concurrently with remaining constructor work. - let hasFirstRun = this._prefs.get("service.firstRun", false); - this._initHistogram = hasFirstRun ? TELEMETRY_INIT : TELEMETRY_INIT_FIRSTRUN; - this._dbOpenHistogram = hasFirstRun ? TELEMETRY_DB_OPEN : TELEMETRY_DB_OPEN_FIRSTRUN; - - // This is set to the name for the provider that we are currently initializing, - // shutting down or collecting data from, if any. - // This is used for AsyncShutdownTimeout diagnostics. - this._currentProviderInShutdown = null; - this._currentProviderInInit = null; - this._currentProviderInCollect = null; -} - -AbstractHealthReporter.prototype = Object.freeze({ - QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]), - - /** - * Whether the service is fully initialized and running. - * - * If this is false, it is not safe to call most functions. - */ - get initialized() { - return this._initialized; - }, - - /** - * Initialize the instance. - * - * This must be called once after object construction or the instance is - * useless. - */ - init: function () { - if (this._initializeStarted) { - throw new Error("We have already started initialization."); - } - - this._initializeStarted = true; - - return Task.spawn(function*() { - TelemetryStopwatch.start(this._initHistogram, this); - - try { - yield this._state.init(); - - if (!this._state._s.removedOutdatedLastpayload) { - yield this._deleteOldLastPayload(); - this._state._s.removedOutdatedLastpayload = true; - // Normally we should save this to a file but it directly conflicts with - // the "application re-upgrade" decision in HealthReporterState::init() - // which specifically does not save the state to a file. - } - } catch (ex) { - this._log.error("Error deleting last payload: " + - CommonUtils.exceptionStr(ex)); - } - - // As soon as we have could have storage, we need to register cleanup or - // else bad things happen on shutdown. - Services.obs.addObserver(this, "quit-application", false); - - // The database needs to be shut down by the end of shutdown - // phase profileBeforeChange. - Metrics.Storage.shutdown.addBlocker("FHR: Flushing storage shutdown", - () => { - // Workaround bug 1017706 - // Apparently, in some cases, quit-application is not triggered - // (or is triggered after profile-before-change), so we need to - // make sure that `_initiateShutdown()` is triggered at least - // once. - this._initiateShutdown(); - return this._promiseShutdown; - }, - () => ({ - shutdownInitiated: this._shutdownInitiated, - initialized: this._initialized, - shutdownRequested: this._shutdownRequested, - initializeHadError: this._initializeHadError, - providerManagerInProgress: this._providerManagerInProgress, - storageInProgress: this._storageInProgress, - hasProviderManager: !!this._providerManager, - hasStorage: !!this._storage, - shutdownComplete: this._shutdownComplete, - currentProviderInShutdown: this._currentProviderInShutdown, - currentProviderInInit: this._currentProviderInInit, - currentProviderInCollect: this._currentProviderInCollect, - })); - - try { - this._storageInProgress = true; - TelemetryStopwatch.start(this._dbOpenHistogram, this); - let storage = yield Metrics.Storage(this._dbName); - TelemetryStopwatch.finish(this._dbOpenHistogram, this); - yield this._onStorageCreated(); - - delete this._dbOpenHistogram; - this._log.info("Storage initialized."); - this._storage = storage; - this._storageInProgress = false; - - if (this._shutdownRequested) { - this._initiateShutdown(); - return null; - } - - yield this._initializeProviderManager(); - yield this._onProviderManagerInitialized(); - this._initializedDeferred.resolve(); - return this.onInit(); - } catch (ex) { - yield this._onInitError(ex); - this._initializedDeferred.reject(ex); - } - }.bind(this)); - }, - - //---------------------------------------------------- - // SERVICE CONTROL FUNCTIONS - // - // You shouldn't need to call any of these externally. - //---------------------------------------------------- - - _onInitError: function (error) { - TelemetryStopwatch.cancel(this._initHistogram, this); - TelemetryStopwatch.cancel(this._dbOpenHistogram, this); - delete this._initHistogram; - delete this._dbOpenHistogram; - - this._recordError("Error during initialization", error); - this._initializeHadError = true; - this._initiateShutdown(); - return Promise.reject(error); - - // FUTURE consider poisoning prototype's functions so calls fail with a - // useful error message. - }, - - - /** - * Removes the outdated lastpaylaod.json and lastpayload.json.tmp files - * @see Bug #867902 - * @return a promise for when all the files have been deleted - */ - _deleteOldLastPayload: function () { - let paths = [this._state._lastPayloadPath, this._state._lastPayloadPath + ".tmp"]; - return Task.spawn(function removeAllFiles () { - for (let path of paths) { - try { - OS.File.remove(path); - } catch (ex) { - if (!ex.becauseNoSuchFile) { - this._log.error("Exception when removing outdated payload files: " + - CommonUtils.exceptionStr(ex)); - } - } - } - }.bind(this)); - }, - - _initializeProviderManager: Task.async(function* _initializeProviderManager() { - if (this._collector) { - throw new Error("Provider manager has already been initialized."); - } - - this._log.info("Initializing provider manager."); - this._providerManager = new Metrics.ProviderManager(this._storage); - this._providerManager.onProviderError = this._recordError.bind(this); - this._providerManager.onProviderInit = this._initProvider.bind(this); - this._providerManagerInProgress = true; - - let catString = this._prefs.get("service.providerCategories") || ""; - if (catString.length) { - for (let category of catString.split(",")) { - yield this._providerManager.registerProvidersFromCategoryManager(category, - providerName => this._currentProviderInInit = providerName); - } - this._currentProviderInInit = null; - } - }), - - _onProviderManagerInitialized: function () { - TelemetryStopwatch.finish(this._initHistogram, this); - delete this._initHistogram; - this._log.debug("Provider manager initialized."); - this._providerManagerInProgress = false; - - if (this._shutdownRequested) { - this._initiateShutdown(); - return; - } - - this._log.info("HealthReporter started."); - this._initialized = true; - Services.obs.addObserver(this, "idle-daily", false); - - // If upload is not enabled, ensure daily collection works. If upload - // is enabled, this will be performed as part of upload. - // - // This is important because it ensures about:healthreport contains - // longitudinal data even if upload is disabled. Having about:healthreport - // provide useful info even if upload is disabled was a core launch - // requirement. - // - // We do not catch changes to the backing pref. So, if the session lasts - // many days, we may fail to collect. However, most sessions are short and - // this code will likely be refactored as part of splitting up policy to - // serve Android. So, meh. - if (!this._policy.healthReportUploadEnabled) { - this._log.info("Upload not enabled. Scheduling daily collection."); - // Since the timer manager is a singleton and there could be multiple - // HealthReporter instances, we need to encode a unique identifier in - // the timer ID. - try { - let timerName = this._branch.replace(/\./g, "-") + "lastDailyCollection"; - let tm = Cc["@mozilla.org/updates/timer-manager;1"] - .getService(Ci.nsIUpdateTimerManager); - tm.registerTimer(timerName, this.collectMeasurements.bind(this), - 24 * 60 * 60); - } catch (ex) { - this._log.error("Error registering collection timer: " + - CommonUtils.exceptionStr(ex)); - } - } - - // Clean up caches and reduce memory usage. - this._storage.compact(); - }, - - // nsIObserver to handle shutdown. - observe: function (subject, topic, data) { - switch (topic) { - case "quit-application": - Services.obs.removeObserver(this, "quit-application"); - this._initiateShutdown(); - break; - - case "idle-daily": - this._performDailyMaintenance(); - break; - } - }, - - _initiateShutdown: function () { - // Ensure we only begin the main shutdown sequence once. - if (this._shutdownInitiated) { - this._log.warn("Shutdown has already been initiated. No-op."); - return; - } - - this._log.info("Request to shut down."); - - this._initialized = false; - this._shutdownRequested = true; - - if (this._initializeHadError) { - this._log.warn("Initialization had error. Shutting down immediately."); - } else { - if (this._providerManagerInProgress) { - this._log.warn("Provider manager is in progress of initializing. " + - "Waiting to finish."); - return; - } - - // If storage is in the process of initializing, we need to wait for it - // to finish before continuing. The initialization process will call us - // again once storage has initialized. - if (this._storageInProgress) { - this._log.warn("Storage is in progress of initializing. Waiting to finish."); - return; - } - } - - this._log.warn("Initiating main shutdown procedure."); - - // Everything from here must only be performed once or else race conditions - // could occur. - - TelemetryStopwatch.start(TELEMETRY_SHUTDOWN, this); - this._shutdownInitiated = true; - - // We may not have registered the observer yet. If not, this will - // throw. - try { - Services.obs.removeObserver(this, "idle-daily"); - } catch (ex) { } - - Task.spawn(function*() { - try { - if (this._providerManager) { - this._log.info("Shutting down provider manager."); - for (let provider of this._providerManager.providers) { - try { - this._log.info("Shutting down provider: " + provider.name); - this._currentProviderInShutdown = provider.name; - yield provider.shutdown(); - } catch (ex) { - this._log.warn("Error when shutting down provider: " + - CommonUtils.exceptionStr(ex)); - } - } - this._log.info("Provider manager shut down."); - this._providerManager = null; - this._currentProviderInShutdown = null; - this._onProviderManagerShutdown(); - } - if (this._storage) { - this._log.info("Shutting down storage."); - try { - yield this._storage.close(); - yield this._onStorageClose(); - } catch (error) { - this._log.warn("Error when closing storage: " + - CommonUtils.exceptionStr(error)); - } - this._storage = null; - } - - this._log.warn("Shutdown complete."); - this._shutdownComplete = true; - } finally { - this._deferredShutdown.resolve(); - TelemetryStopwatch.finish(TELEMETRY_SHUTDOWN, this); - } - }.bind(this)); - }, - - onInit: function() { - return this._initializedDeferred.promise; - }, - - _onStorageCreated: function() { - // Do nothing. - // This method provides a hook point for the test suite. - }, - - _onStorageClose: function() { - // Do nothing. - // This method provides a hook point for the test suite. - }, - - _onProviderManagerShutdown: function() { - // Do nothing. - // This method provides a hook point for the test suite. - }, - - /** - * Convenience method to shut down the instance. - * - * This should *not* be called outside of tests. - */ - _shutdown: function () { - this._initiateShutdown(); - return this._promiseShutdown; - }, - - _performDailyMaintenance: function () { - this._log.info("Request to perform daily maintenance."); - - if (!this._initialized) { - return; - } - - let now = new Date(); - let cutoff = new Date(now.getTime() - MILLISECONDS_PER_DAY * (DAYS_IN_PAYLOAD - 1)); - - // The operation is enqueued and put in a transaction by the storage module. - this._storage.pruneDataBefore(cutoff); - }, - - //-------------------- - // Provider Management - //-------------------- - - /** - * Obtain a provider from its name. - * - * This will only return providers that are currently initialized. If - * a provider is lazy initialized (like pull-only providers) this - * will likely not return anything. - */ - getProvider: function (name) { - if (!this._providerManager) { - return null; - } - - return this._providerManager.getProvider(name); - }, - - _initProvider: function (provider) { - provider.healthReporter = this; - }, - - /** - * Record an exception for reporting in the payload. - * - * A side effect is the exception is logged. - * - * Note that callers need to be extra sensitive about ensuring personal - * or otherwise private details do not leak into this. All of the user data - * on the stack in FHR code should be limited to data we were collecting with - * the intent to submit. So, it is covered under the user's consent to use - * the feature. - * - * @param message - * (string) Human readable message describing error. - * @param ex - * (Error) The error that should be captured. - */ - _recordError: function (message, ex) { - let recordMessage = message; - let logMessage = message; - - if (ex) { - recordMessage += ": " + CommonUtils.exceptionStr(ex); - logMessage += ": " + CommonUtils.exceptionStr(ex); - } - - // Scrub out potentially identifying information from strings that could - // make the payload. - let appData = Services.dirsvc.get("UAppData", Ci.nsIFile); - let profile = Services.dirsvc.get("ProfD", Ci.nsIFile); - - let appDataURI = Services.io.newFileURI(appData); - let profileURI = Services.io.newFileURI(profile); - - // Order of operation is important here. We do the URI before the path version - // because the path may be a subset of the URI. We also have to check for the case - // where UAppData is underneath the profile directory (or vice-versa) so we - // don't substitute incomplete strings. - - // Return a /g regex that matches the provided string exactly. - function regexify(s) { - return new RegExp(s.replace(/[-\\^$*+?.()|[\]{}]/g, "\\$&"), "g"); - } - - function replace(uri, path, thing) { - // Try is because .spec can throw on invalid URI. - try { - recordMessage = recordMessage.replace(regexify(uri.spec), "<" + thing + "URI>"); - } catch (ex) { } - - recordMessage = recordMessage.replace(regexify(path), "<" + thing + "Path>"); - } - - if (appData.path.includes(profile.path)) { - replace(appDataURI, appData.path, 'AppData'); - replace(profileURI, profile.path, 'Profile'); - } else { - replace(profileURI, profile.path, 'Profile'); - replace(appDataURI, appData.path, 'AppData'); - } - - this._log.warn(logMessage); - this._errors.push(recordMessage); - }, - - /** - * Collect all measurements for all registered providers. - */ - collectMeasurements: function () { - if (!this._initialized) { - return Promise.reject(new Error("Not initialized.")); - } - - return Task.spawn(function doCollection() { - yield this._providerManager.ensurePullOnlyProvidersRegistered(); - - try { - TelemetryStopwatch.start(TELEMETRY_COLLECT_CONSTANT, this); - yield this._providerManager.collectConstantData(name => this._currentProviderInCollect = name); - this._currentProviderInCollect = null; - TelemetryStopwatch.finish(TELEMETRY_COLLECT_CONSTANT, this); - } catch (ex) { - TelemetryStopwatch.cancel(TELEMETRY_COLLECT_CONSTANT, this); - this._log.warn("Error collecting constant data: " + - CommonUtils.exceptionStr(ex)); - } - - // Daily data is collected if it hasn't yet been collected this - // application session or if it has been more than a day since the - // last collection. This means that providers could see many calls to - // collectDailyData per calendar day. However, this collection API - // makes no guarantees about limits. The alternative would involve - // recording state. The simpler implementation prevails for now. - if (!this._lastDailyDate || - Date.now() - this._lastDailyDate > MILLISECONDS_PER_DAY) { - - try { - TelemetryStopwatch.start(TELEMETRY_COLLECT_DAILY, this); - this._lastDailyDate = new Date(); - yield this._providerManager.collectDailyData(name => this._currentProviderInCollect = name); - this._currentProviderInCollect = null; - TelemetryStopwatch.finish(TELEMETRY_COLLECT_DAILY, this); - } catch (ex) { - TelemetryStopwatch.cancel(TELEMETRY_COLLECT_DAILY, this); - this._log.warn("Error collecting daily data from providers: " + - CommonUtils.exceptionStr(ex)); - } - } - - yield this._providerManager.ensurePullOnlyProvidersUnregistered(); - - // Flush gathered data to disk. This will incur an fsync. But, if - // there is ever a time we want to persist data to disk, it's - // after a massive collection. - try { - TelemetryStopwatch.start(TELEMETRY_COLLECT_CHECKPOINT, this); - yield this._storage.checkpoint(); - TelemetryStopwatch.finish(TELEMETRY_COLLECT_CHECKPOINT, this); - } catch (ex) { - TelemetryStopwatch.cancel(TELEMETRY_COLLECT_CHECKPOINT, this); - throw ex; - } - - throw new Task.Result(); - }.bind(this)); - }, - - /** - * Helper function to perform data collection and obtain the JSON payload. - * - * If you are looking for an up-to-date snapshot of FHR data that pulls in - * new data since the last upload, this is how you should obtain it. - * - * @param asObject - * (bool) Whether to resolve an object or JSON-encoded string of that - * object (the default). - * - * @return Promise - */ - collectAndObtainJSONPayload: function (asObject=false) { - if (!this._initialized) { - return Promise.reject(new Error("Not initialized.")); - } - - return Task.spawn(function collectAndObtain() { - yield this._storage.setAutoCheckpoint(0); - yield this._providerManager.ensurePullOnlyProvidersRegistered(); - - let payload; - let error; - - try { - yield this.collectMeasurements(); - payload = yield this.getJSONPayload(asObject); - } catch (ex) { - error = ex; - this._collectException("Error collecting and/or retrieving JSON payload", - ex); - } finally { - yield this._providerManager.ensurePullOnlyProvidersUnregistered(); - yield this._storage.setAutoCheckpoint(1); - - if (error) { - throw error; - } - } - - // We hold off throwing to ensure that behavior between finally - // and generators and throwing is sane. - throw new Task.Result(payload); - }.bind(this)); - }, - - - /** - * Obtain the JSON payload for currently-collected data. - * - * The payload only contains data that has been recorded to FHR. Some - * providers may have newer data available. If you want to ensure you - * have all available data, call `collectAndObtainJSONPayload` - * instead. - * - * @param asObject - * (bool) Whether to return an object or JSON encoding of that - * object (the default). - * - * @return Promise - */ - getJSONPayload: function (asObject=false) { - TelemetryStopwatch.start(TELEMETRY_GENERATE_PAYLOAD, this); - let deferred = Promise.defer(); - - Task.spawn(this._getJSONPayload.bind(this, this._now(), asObject)).then( - function onResult(result) { - TelemetryStopwatch.finish(TELEMETRY_GENERATE_PAYLOAD, this); - deferred.resolve(result); - }.bind(this), - function onError(error) { - TelemetryStopwatch.cancel(TELEMETRY_GENERATE_PAYLOAD, this); - deferred.reject(error); - }.bind(this) - ); - - return deferred.promise; - }, - - _getJSONPayload: function (now, asObject=false) { - let pingDateString = this._formatDate(now); - this._log.info("Producing JSON payload for " + pingDateString); - - // May not be present if we are generating as a result of init error. - if (this._providerManager) { - yield this._providerManager.ensurePullOnlyProvidersRegistered(); - } - - let o = { - version: 2, - clientID: this._state.clientID, - clientIDVersion: this._state.clientIDVersion, - thisPingDate: pingDateString, - geckoAppInfo: this.obtainAppInfo(this._log), - data: {last: {}, days: {}}, - }; - - let outputDataDays = o.data.days; - - // Guard here in case we don't track this (e.g., on Android). - let lastPingDate = this.lastPingDate; - if (lastPingDate && lastPingDate.getTime() > 0) { - o.lastPingDate = this._formatDate(lastPingDate); - } - - // We can still generate a payload even if we're not initialized. - // This is to facilitate error upload on init failure. - if (this._initialized) { - for (let provider of this._providerManager.providers) { - let providerName = provider.name; - - let providerEntry = { - measurements: {}, - }; - - // Measurement name to recorded version. - let lastVersions = {}; - // Day string to mapping of measurement name to recorded version. - let dayVersions = {}; - - for (let [measurementKey, measurement] of provider.measurements) { - let name = providerName + "." + measurement.name; - let version = measurement.version; - - let serializer; - try { - // The measurement is responsible for returning a serializer which - // is aware of the measurement version. - serializer = measurement.serializer(measurement.SERIALIZE_JSON); - } catch (ex) { - this._recordError("Error obtaining serializer for measurement: " + - name, ex); - continue; - } - - let data; - try { - data = yield measurement.getValues(); - } catch (ex) { - this._recordError("Error obtaining data for measurement: " + name, - ex); - continue; - } - - if (data.singular.size) { - try { - let serialized = serializer.singular(data.singular); - if (serialized) { - // Only replace the existing data if there is no data or if our - // version is newer than the old one. - if (!(name in o.data.last) || version > lastVersions[name]) { - o.data.last[name] = serialized; - lastVersions[name] = version; - } - } - } catch (ex) { - this._recordError("Error serializing singular data: " + name, - ex); - continue; - } - } - - let dataDays = data.days; - for (let i = 0; i < DAYS_IN_PAYLOAD; i++) { - let date = new Date(now.getTime() - i * MILLISECONDS_PER_DAY); - if (!dataDays.hasDay(date)) { - continue; - } - let dateFormatted = this._formatDate(date); - - try { - let serialized = serializer.daily(dataDays.getDay(date)); - if (!serialized) { - continue; - } - - if (!(dateFormatted in outputDataDays)) { - outputDataDays[dateFormatted] = {}; - } - - // This needs to be separate because dayVersions is provider - // specific and gets blown away in a loop while outputDataDays - // is persistent. - if (!(dateFormatted in dayVersions)) { - dayVersions[dateFormatted] = {}; - } - - if (!(name in outputDataDays[dateFormatted]) || - version > dayVersions[dateFormatted][name]) { - outputDataDays[dateFormatted][name] = serialized; - dayVersions[dateFormatted][name] = version; - } - } catch (ex) { - this._recordError("Error populating data for day: " + name, ex); - continue; - } - } - } - } - } else { - o.notInitialized = 1; - this._log.warn("Not initialized. Sending report with only error info."); - } - - if (this._errors.length) { - o.errors = this._errors.slice(0, 20); - } - - if (this._initialized) { - this._storage.compact(); - } - - if (!asObject) { - TelemetryStopwatch.start(TELEMETRY_JSON_PAYLOAD_SERIALIZE, this); - o = JSON.stringify(o); - TelemetryStopwatch.finish(TELEMETRY_JSON_PAYLOAD_SERIALIZE, this); - } - - if (this._providerManager) { - yield this._providerManager.ensurePullOnlyProvidersUnregistered(); - } - - throw new Task.Result(o); - }, - - _now: function _now() { - return new Date(); - }, - - // These are stolen from AppInfoProvider. - appInfoVersion: 1, - appInfoFields: { - // From nsIXULAppInfo. - vendor: "vendor", - name: "name", - id: "ID", - version: "version", - appBuildID: "appBuildID", - platformVersion: "platformVersion", - platformBuildID: "platformBuildID", - - // From nsIXULRuntime. - os: "OS", - xpcomabi: "XPCOMABI", - }, - - /** - * Statically return a bundle of app info data, a subset of that produced by - * AppInfoProvider._populateConstants. This allows us to more usefully handle - * payloads that, due to error, contain no data. - * - * Returns a very sparse object if Services.appinfo is unavailable. - */ - obtainAppInfo: function () { - let out = {"_v": this.appInfoVersion}; - try { - let ai = Services.appinfo; - for (let [k, v] in Iterator(this.appInfoFields)) { - out[k] = ai[v]; - } - } catch (ex) { - this._log.warn("Could not obtain Services.appinfo: " + - CommonUtils.exceptionStr(ex)); - } - - try { - out["updateChannel"] = UpdateChannel.get(); - } catch (ex) { - this._log.warn("Could not obtain update channel: " + - CommonUtils.exceptionStr(ex)); - } - - return out; - }, -}); - -/** - * HealthReporter and its abstract superclass coordinate collection and - * submission of health report metrics. - * - * This is the main type for Firefox Health Report on desktop. It glues all the - * lower-level components (such as collection and submission) together. - * - * An instance of this type is created as an XPCOM service. See - * DataReportingService.js and - * DataReporting.manifest/HealthReportComponents.manifest. - * - * It is theoretically possible to have multiple instances of this running - * in the application. For example, this type may one day handle submission - * of telemetry data as well. However, there is some moderate coupling between - * this type and *the* Firefox Health Report (e.g., the policy). This could - * be abstracted if needed. - * - * Note that `AbstractHealthReporter` exists to allow for Firefox Health Report - * to be more easily implemented on platforms where a separate controlling - * layer is responsible for payload upload and deletion. - * - * IMPLEMENTATION NOTES - * ==================== - * - * These notes apply to the combination of `HealthReporter` and - * `AbstractHealthReporter`. - * - * Initialization and shutdown are somewhat complicated and worth explaining - * in extra detail. - * - * The complexity is driven by the requirements of SQLite connection management. - * Once you have a SQLite connection, it isn't enough to just let the - * application shut down. If there is an open connection or if there are - * outstanding SQL statements come XPCOM shutdown time, Storage will assert. - * On debug builds you will crash. On release builds you will get a shutdown - * hang. This must be avoided! - * - * During initialization, the second we create a SQLite connection (via - * Metrics.Storage) we register observers for application shutdown. The - * "quit-application" notification initiates our shutdown procedure. The - * subsequent "profile-do-change" notification ensures it has completed. - * - * The handler for "profile-do-change" may result in event loop spinning. This - * is because of race conditions between our shutdown code and application - * shutdown. - * - * All of our shutdown routines are async. There is the potential that these - * async functions will not complete before XPCOM shutdown. If they don't - * finish in time, we could get assertions in Storage. Our solution is to - * initiate storage early in the shutdown cycle ("quit-application"). - * Hopefully all the async operations have completed by the time we reach - * "profile-do-change." If so, great. If not, we spin the event loop until - * they have completed, avoiding potential race conditions. - * - * @param branch - * (string) The preferences branch to use for state storage. The value - * must end with a period (.). - * - * @param policy - * (HealthReportPolicy) Policy driving execution of HealthReporter. - */ -this.HealthReporter = function (branch, policy, sessionRecorder, stateLeaf=null) { - this._stateLeaf = stateLeaf; - this._uploadInProgress = false; - - AbstractHealthReporter.call(this, branch, policy, sessionRecorder); - - if (!this.serverURI) { - throw new Error("No server URI defined. Did you forget to define the pref?"); - } - - if (!this.serverNamespace) { - throw new Error("No server namespace defined. Did you forget a pref?"); - } - - this._state = new HealthReporterState(this); -} - -this.HealthReporter.prototype = Object.freeze({ - __proto__: AbstractHealthReporter.prototype, - - QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]), - - get lastSubmitID() { - return this._state.lastSubmitID; - }, - - /** - * When we last successfully submitted data to the server. - * - * This is sent as part of the upload. This is redundant with similar data - * in the policy because we like the modules to be loosely coupled and the - * similar data in the policy is only used for forensic purposes. - */ - get lastPingDate() { - return this._state.lastPingDate; - }, - - /** - * The base URI of the document server to which to submit data. - * - * This is typically a Bagheera server instance. It is the URI up to but not - * including the version prefix. e.g. https://data.metrics.mozilla.com/ - */ - get serverURI() { - return this._prefs.get("documentServerURI", null); - }, - - set serverURI(value) { - if (!value) { - throw new Error("serverURI must have a value."); - } - - if (typeof(value) != "string") { - throw new Error("serverURI must be a string: " + value); - } - - this._prefs.set("documentServerURI", value); - }, - - /** - * The namespace on the document server to which we will be submitting data. - */ - get serverNamespace() { - return this._prefs.get("documentServerNamespace", "metrics"); - }, - - set serverNamespace(value) { - if (!value) { - throw new Error("serverNamespace must have a value."); - } - - if (typeof(value) != "string") { - throw new Error("serverNamespace must be a string: " + value); - } - - this._prefs.set("documentServerNamespace", value); - }, - - /** - * Whether this instance will upload data to a server. - */ - get willUploadData() { - return this._policy.userNotifiedOfCurrentPolicy && - this._policy.healthReportUploadEnabled; - }, - - /** - * Whether remote data is currently stored. - * - * @return bool - */ - haveRemoteData: function () { - return !!this._state.lastSubmitID; - }, - - /** - * Called to initiate a data upload. - * - * The passed argument is a `DataSubmissionRequest` from policy.jsm. - */ - requestDataUpload: function (request) { - if (!this._initialized) { - return Promise.reject(new Error("Not initialized.")); - } - - return Task.spawn(function doUpload() { - yield this._providerManager.ensurePullOnlyProvidersRegistered(); - try { - yield this.collectMeasurements(); - try { - yield this._uploadData(request); - } catch (ex) { - this._onSubmitDataRequestFailure(ex); - } - } finally { - yield this._providerManager.ensurePullOnlyProvidersUnregistered(); - } - }.bind(this)); - }, - - /** - * Request that server data be deleted. - * - * If deletion is scheduled to occur immediately, a promise will be returned - * that will be fulfilled when the deletion attempt finishes. Otherwise, - * callers should poll haveRemoteData() to determine when remote data is - * deleted. - */ - requestDeleteRemoteData: function (reason) { - if (!this.haveRemoteData()) { - return; - } - - return this._policy.deleteRemoteData(reason); - }, - - /** - * Override default handler to incur an upload describing the error. - */ - _onInitError: function (error) { - // Need to capture this before we call the parent else it's always - // set. - let inShutdown = this._shutdownRequested; - let result; - - try { - result = AbstractHealthReporter.prototype._onInitError.call(this, error); - } catch (ex) { - this._log.error("Error when calling _onInitError: " + - CommonUtils.exceptionStr(ex)); - } - - // This bypasses a lot of the checks in policy, such as respect for - // backoff. We should arguably not do this. However, reporting - // startup errors is important. And, they should not occur with much - // frequency in the wild. So, it shouldn't be too big of a deal. - if (!inShutdown && - this._policy.healthReportUploadEnabled && - this._policy.ensureUserNotified()) { - // We don't care about what happens to this request. It's best - // effort. - let request = { - onNoDataAvailable: function () {}, - onSubmissionSuccess: function () {}, - onSubmissionFailureSoft: function () {}, - onSubmissionFailureHard: function () {}, - onUploadInProgress: function () {}, - }; - - this._uploadData(request); - } - - return result; - }, - - _onBagheeraResult: function (request, isDelete, date, result) { - this._log.debug("Received Bagheera result."); - - return Task.spawn(function onBagheeraResult() { - let hrProvider = this.getProvider("org.mozilla.healthreport"); - - if (!result.transportSuccess) { - // The built-in provider may not be initialized if this instance failed - // to initialize fully. - if (hrProvider && !isDelete) { - try { - hrProvider.recordEvent("uploadTransportFailure", date); - } catch (ex) { - this._log.error("Error recording upload transport failure: " + - CommonUtils.exceptionStr(ex)); - } - } - - request.onSubmissionFailureSoft("Network transport error."); - throw new Task.Result(false); - } - - if (!result.serverSuccess) { - if (hrProvider && !isDelete) { - try { - hrProvider.recordEvent("uploadServerFailure", date); - } catch (ex) { - this._log.error("Error recording server failure: " + - CommonUtils.exceptionStr(ex)); - } - } - - request.onSubmissionFailureHard("Server failure."); - throw new Task.Result(false); - } - - if (hrProvider && !isDelete) { - try { - hrProvider.recordEvent("uploadSuccess", date); - } catch (ex) { - this._log.error("Error recording upload success: " + - CommonUtils.exceptionStr(ex)); - } - } - - if (isDelete) { - this._log.warn("Marking delete as successful."); - yield this._state.removeRemoteIDs([result.id]); - } else { - this._log.warn("Marking upload as successful."); - yield this._state.updateLastPingAndRemoveRemoteIDs(date, result.deleteIDs); - } - - request.onSubmissionSuccess(this._now()); - - throw new Task.Result(true); - }.bind(this)); - }, - - _onSubmitDataRequestFailure: function (error) { - this._log.error("Error processing request to submit data: " + - CommonUtils.exceptionStr(error)); - }, - - _formatDate: function (date) { - // Why, oh, why doesn't JS have a strftime() equivalent? - return date.toISOString().substr(0, 10); - }, - - _uploadData: function (request) { - // Under ideal circumstances, clients should never race to this - // function. However, server logs have observed behavior where - // racing to this function could be a cause. So, this lock was - // instituted. - if (this._uploadInProgress) { - this._log.warn("Upload requested but upload already in progress."); - let provider = this.getProvider("org.mozilla.healthreport"); - let promise = provider.recordEvent("uploadAlreadyInProgress"); - request.onUploadInProgress("Upload already in progress."); - return promise; - } - - let id = CommonUtils.generateUUID(); - - this._log.info("Uploading data to server: " + this.serverURI + " " + - this.serverNamespace + ":" + id); - let client = new BagheeraClient(this.serverURI); - let now = this._now(); - - return Task.spawn(function doUpload() { - try { - // The test for upload locking monkeypatches getJSONPayload. - // If the next two lines change, be sure to verify the test is - // accurate! - this._uploadInProgress = true; - let payload = yield this.getJSONPayload(); - - let histogram = Services.telemetry.getHistogramById(TELEMETRY_PAYLOAD_SIZE_UNCOMPRESSED); - histogram.add(payload.length); - - let lastID = this.lastSubmitID; - yield this._state.addRemoteID(id); - - let hrProvider = this.getProvider("org.mozilla.healthreport"); - if (hrProvider) { - let event = lastID ? "continuationUploadAttempt" - : "firstDocumentUploadAttempt"; - try { - hrProvider.recordEvent(event, now); - } catch (ex) { - this._log.error("Error when recording upload attempt: " + - CommonUtils.exceptionStr(ex)); - } - } - - TelemetryStopwatch.start(TELEMETRY_UPLOAD, this); - let result; - try { - let options = { - deleteIDs: this._state.remoteIDs.filter((x) => { return x != id; }), - telemetryCompressed: TELEMETRY_PAYLOAD_SIZE_COMPRESSED, - }; - result = yield client.uploadJSON(this.serverNamespace, id, payload, - options); - TelemetryStopwatch.finish(TELEMETRY_UPLOAD, this); - } catch (ex) { - TelemetryStopwatch.cancel(TELEMETRY_UPLOAD, this); - if (hrProvider) { - try { - hrProvider.recordEvent("uploadClientFailure", now); - } catch (ex) { - this._log.error("Error when recording client failure: " + - CommonUtils.exceptionStr(ex)); - } - } - throw ex; - } - - yield this._onBagheeraResult(request, false, now, result); - } finally { - this._uploadInProgress = false; - } - }.bind(this)); - }, - - /** - * Request deletion of remote data. - * - * @param request - * (DataSubmissionRequest) Tracks progress of this request. - */ - deleteRemoteData: function (request) { - if (!this._state.lastSubmitID) { - this._log.info("Received request to delete remote data but no data stored."); - request.onNoDataAvailable(); - return; - } - - this._log.warn("Deleting remote data."); - let client = new BagheeraClient(this.serverURI); - - return Task.spawn(function* doDelete() { - try { - let result = yield client.deleteDocument(this.serverNamespace, - this.lastSubmitID); - yield this._onBagheeraResult(request, true, this._now(), result); - } catch (ex) { - this._log.error("Error processing request to delete data: " + - CommonUtils.exceptionStr(error)); - } finally { - // If we don't have any remote documents left, nuke the ID. - // This is done for privacy reasons. Why preserve the ID if we - // don't need to? - if (!this.haveRemoteData()) { - yield this._state.resetClientID(); - } - } - }.bind(this)); - }, -}); - diff --git a/services/healthreport/modules-testing/utils.jsm b/services/healthreport/modules-testing/utils.jsm deleted file mode 100644 index 43280c5653..0000000000 --- a/services/healthreport/modules-testing/utils.jsm +++ /dev/null @@ -1,217 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -"use strict"; - -this.EXPORTED_SYMBOLS = [ - "getAppInfo", - "updateAppInfo", - "createFakeCrash", - "InspectedHealthReporter", - "getHealthReporter", -]; - - -const {classes: Cc, interfaces: Ci, results: Cr, utils: Cu} = Components; - -Cu.import("resource://gre/modules/Preferences.jsm"); -Cu.import("resource://gre/modules/Promise.jsm"); -Cu.import("resource://gre/modules/FileUtils.jsm"); -Cu.import("resource://gre/modules/osfile.jsm"); -Cu.import("resource://gre/modules/Task.jsm"); -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource://gre/modules/services-common/utils.js"); -Cu.import("resource://gre/modules/services/datareporting/policy.jsm"); -Cu.import("resource://gre/modules/services/healthreport/healthreporter.jsm"); -Cu.import("resource://gre/modules/services/healthreport/providers.jsm"); -Cu.import("resource://testing-common/services/datareporting/mocks.jsm"); - - -var APP_INFO = { - vendor: "Mozilla", - name: "xpcshell", - ID: "xpcshell@tests.mozilla.org", - version: "1", - appBuildID: "20121107", - platformVersion: "p-ver", - platformBuildID: "20121106", - inSafeMode: false, - logConsoleErrors: true, - OS: "XPCShell", - XPCOMABI: "noarch-spidermonkey", - QueryInterface: XPCOMUtils.generateQI([Ci.nsIXULAppInfo, Ci.nsIXULRuntime]), - invalidateCachesOnRestart: function() {}, -}; - - -/** - * Obtain a reference to the current object used to define XULAppInfo. - */ -this.getAppInfo = function () { return APP_INFO; } - -/** - * Update the current application info. - * - * If the argument is defined, it will be the object used. Else, APP_INFO is - * used. - * - * To change the current XULAppInfo, simply call this function. If there was - * a previously registered app info object, it will be unloaded and replaced. - */ -this.updateAppInfo = function (obj) { - obj = obj || APP_INFO; - APP_INFO = obj; - - let id = Components.ID("{fbfae60b-64a4-44ef-a911-08ceb70b9f31}"); - let cid = "@mozilla.org/xre/app-info;1"; - let registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar); - - // Unregister an existing factory if one exists. - try { - let existing = Components.manager.getClassObjectByContractID(cid, Ci.nsIFactory); - registrar.unregisterFactory(id, existing); - } catch (ex) {} - - let factory = { - createInstance: function (outer, iid) { - if (outer != null) { - throw Cr.NS_ERROR_NO_AGGREGATION; - } - - return obj.QueryInterface(iid); - }, - }; - - registrar.registerFactory(id, "XULAppInfo", cid, factory); -}; - -/** - * Creates a fake crash in the Crash Reports directory. - * - * Currently, we just create a dummy file. A more robust implementation would - * create something that actually resembles a crash report file. - * - * FUTURE consolidate code in a shared JSM. - */ -this.createFakeCrash = function (submitted=false, date=new Date()) { - let id = CommonUtils.generateUUID(); - let filename; - - let paths = ["Crash Reports"]; - let mode; - - if (submitted) { - paths.push("submitted"); - filename = "bp-" + id + ".txt"; - mode = OS.Constants.libc.S_IRUSR | OS.Constants.libc.S_IWUSR | - OS.Constants.libc.S_IRGRP | OS.Constants.libc.S_IROTH; - } else { - paths.push("pending"); - filename = id + ".dmp"; - mode = OS.Constants.libc.S_IRUSR | OS.Constants.libc.S_IWUSR; - } - - paths.push(filename); - - let file = FileUtils.getFile("UAppData", paths, true); - file.create(file.NORMAL_FILE_TYPE, mode); - file.lastModifiedTime = date.getTime(); - dump("Created fake crash: " + id + "\n"); - - return id; -}; - - -/** - * A HealthReporter that is probed with various callbacks and counters. - * - * The purpose of this type is to aid testing of startup and shutdown. - */ -this.InspectedHealthReporter = function (branch, policy, recorder, stateLeaf) { - HealthReporter.call(this, branch, policy, recorder, stateLeaf); - - this.onStorageCreated = null; - this.onProviderManagerInitialized = null; - this.providerManagerShutdownCount = 0; - this.storageCloseCount = 0; -} - -InspectedHealthReporter.prototype = { - __proto__: HealthReporter.prototype, - - _onStorageCreated: function (storage) { - if (this.onStorageCreated) { - this.onStorageCreated(storage); - } - - return HealthReporter.prototype._onStorageCreated.call(this, storage); - }, - - _initializeProviderManager: Task.async(function* () { - yield HealthReporter.prototype._initializeProviderManager.call(this); - - if (this.onInitializeProviderManagerFinished) { - this.onInitializeProviderManagerFinished(); - } - }), - - _onProviderManagerInitialized: function () { - if (this.onProviderManagerInitialized) { - this.onProviderManagerInitialized(); - } - - return HealthReporter.prototype._onProviderManagerInitialized.call(this); - }, - - _onProviderManagerShutdown: function () { - this.providerManagerShutdownCount++; - - return HealthReporter.prototype._onProviderManagerShutdown.call(this); - }, - - _onStorageClose: function () { - this.storageCloseCount++; - - return HealthReporter.prototype._onStorageClose.call(this); - }, -}; - -const DUMMY_URI="http://localhost:62013/"; - -this.getHealthReporter = function (name, uri=DUMMY_URI, inspected=false) { - // The healthreporters use the client id from the datareporting service, - // so we need to ensure it is initialized. - let drs = Cc["@mozilla.org/datareporting/service;1"] - .getService(Ci.nsISupports) - .wrappedJSObject; - drs.observe(null, "app-startup", null); - drs.observe(null, "profile-after-change", null); - - let branch = "healthreport.testing." + name + "."; - - let prefs = new Preferences(branch + "healthreport."); - prefs.set("documentServerURI", uri); - prefs.set("dbName", name); - - let reporter; - - let policyPrefs = new Preferences(branch + "policy."); - let listener = new MockPolicyListener(); - listener.onRequestDataUpload = function (request) { - let promise = reporter.requestDataUpload(request); - MockPolicyListener.prototype.onRequestDataUpload.call(this, request); - return promise; - } - listener.onRequestRemoteDelete = function (request) { - let promise = reporter.deleteRemoteData(request); - MockPolicyListener.prototype.onRequestRemoteDelete.call(this, request); - return promise; - } - let policy = new DataReportingPolicy(policyPrefs, prefs, listener); - let type = inspected ? InspectedHealthReporter : HealthReporter; - reporter = new type(branch + "healthreport.", policy, null, - "state-" + name + ".json"); - - return reporter; -}; diff --git a/services/healthreport/moz.build b/services/healthreport/moz.build deleted file mode 100644 index dccdd1c2db..0000000000 --- a/services/healthreport/moz.build +++ /dev/null @@ -1,27 +0,0 @@ -# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- -# vim: set filetype=python: -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -SPHINX_TREES['healthreport'] = 'docs' - -XPCSHELL_TESTS_MANIFESTS += ['tests/xpcshell/xpcshell.ini'] - -EXTRA_PP_COMPONENTS += [ - 'HealthReportComponents.manifest', -] - -EXTRA_PP_JS_MODULES += [ - 'HealthReport.jsm', -] - -EXTRA_PP_JS_MODULES.services.healthreport += [ - 'healthreporter.jsm', - 'profile.jsm', - 'providers.jsm', -] - -TESTING_JS_MODULES.services.healthreport += [ - 'modules-testing/utils.jsm', -] diff --git a/services/healthreport/profile.jsm b/services/healthreport/profile.jsm deleted file mode 100644 index 5c6717a1ee..0000000000 --- a/services/healthreport/profile.jsm +++ /dev/null @@ -1,315 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef MERGED_COMPARTMENT - -"use strict"; - -this.EXPORTED_SYMBOLS = [ - "ProfileTimesAccessor", - "ProfileMetadataProvider", -]; - -const {utils: Cu, classes: Cc, interfaces: Ci} = Components; - -const MILLISECONDS_PER_DAY = 24 * 60 * 60 * 1000; - -Cu.import("resource://gre/modules/Metrics.jsm"); - -#endif - -const DEFAULT_PROFILE_MEASUREMENT_NAME = "age"; -const DEFAULT_PROFILE_MEASUREMENT_VERSION = 2; -const REQUIRED_UINT32_TYPE = {type: "TYPE_UINT32"}; - -Cu.import("resource://gre/modules/Promise.jsm"); -Cu.import("resource://gre/modules/osfile.jsm") -Cu.import("resource://gre/modules/Task.jsm"); -Cu.import("resource://gre/modules/Log.jsm"); -Cu.import("resource://services-common/utils.js"); - -// Profile access to times.json (eg, creation/reset time). -// This is separate from the provider to simplify testing and enable extraction -// to a shared location in the future. -this.ProfileTimesAccessor = function(profile, log) { - this.profilePath = profile || OS.Constants.Path.profileDir; - if (!this.profilePath) { - throw new Error("No profile directory."); - } - this._log = log || {"debug": function (s) { dump(s + "\n"); }}; -} -this.ProfileTimesAccessor.prototype = { - /** - * There are three ways we can get our creation time: - * - * 1. From our own saved value (to avoid redundant work). - * 2. From the on-disk JSON file. - * 3. By calculating it from the filesystem. - * - * If we have to calculate, we write out the file; if we have - * to touch the file, we persist in-memory. - * - * @return a promise that resolves to the profile's creation time. - */ - get created() { - function onSuccess(times) { - if (times.created) { - return times.created; - } - return onFailure.call(this, null, times); - } - - function onFailure(err, times) { - return this.computeAndPersistCreated(times) - .then(function onSuccess(created) { - return created; - }.bind(this)); - } - - return this.getTimes() - .then(onSuccess.bind(this), - onFailure.bind(this)); - }, - - /** - * Explicitly make `file`, a filename, a full path - * relative to our profile path. - */ - getPath: function (file) { - return OS.Path.join(this.profilePath, file); - }, - - /** - * Return a promise which resolves to the JSON contents - * of the time file, using the already read value if possible. - */ - getTimes: function (file="times.json") { - if (this._times) { - return Promise.resolve(this._times); - } - return this.readTimes(file).then( - times => { - return this.times = times || {}; - } - ); - }, - - /** - * Return a promise which resolves to the JSON contents - * of the time file in this accessor's profile. - */ - readTimes: function (file="times.json") { - return CommonUtils.readJSON(this.getPath(file)); - }, - - /** - * Return a promise representing the writing of `contents` - * to `file` in the specified profile. - */ - writeTimes: function (contents, file="times.json") { - return CommonUtils.writeJSON(contents, this.getPath(file)); - }, - - /** - * Merge existing contents with a 'created' field, writing them - * to the specified file. Promise, naturally. - */ - computeAndPersistCreated: function (existingContents, file="times.json") { - let path = this.getPath(file); - function onOldest(oldest) { - let contents = existingContents || {}; - contents.created = oldest; - this._times = contents; - return this.writeTimes(contents, path) - .then(function onSuccess() { - return oldest; - }); - } - - return this.getOldestProfileTimestamp() - .then(onOldest.bind(this)); - }, - - /** - * Traverse the contents of the profile directory, finding the oldest file - * and returning its creation timestamp. - */ - getOldestProfileTimestamp: function () { - let self = this; - let oldest = Date.now() + 1000; - let iterator = new OS.File.DirectoryIterator(this.profilePath); - self._log.debug("Iterating over profile " + this.profilePath); - if (!iterator) { - throw new Error("Unable to fetch oldest profile entry: no profile iterator."); - } - - function onEntry(entry) { - function onStatSuccess(info) { - // OS.File doesn't seem to be behaving. See Bug 827148. - // Let's do the best we can. This whole function is defensive. - let date = info.winBirthDate || info.macBirthDate; - if (!date || !date.getTime()) { - // OS.File will only return file creation times of any kind on Mac - // and Windows, where birthTime is defined. - // That means we're unable to function on Linux, so we use mtime - // instead. - self._log.debug("No birth date. Using mtime."); - date = info.lastModificationDate; - } - - if (date) { - let timestamp = date.getTime(); - self._log.debug("Using date: " + entry.path + " = " + date); - if (timestamp < oldest) { - oldest = timestamp; - } - } - } - - function onStatFailure(e) { - // Never mind. - self._log.debug("Stat failure: " + CommonUtils.exceptionStr(e)); - } - - return OS.File.stat(entry.path) - .then(onStatSuccess, onStatFailure); - } - - let promise = iterator.forEach(onEntry); - - function onSuccess() { - iterator.close(); - return oldest; - } - - function onFailure(reason) { - iterator.close(); - throw new Error("Unable to fetch oldest profile entry: " + reason); - } - - return promise.then(onSuccess, onFailure); - }, - - /** - * Record (and persist) when a profile reset happened. We just store a - * single value - the timestamp of the most recent reset - but there is scope - * to keep a list of reset times should our health-reporter successor - * be able to make use of that. - * Returns a promise that is resolved once the file has been written. - */ - recordProfileReset: function (time=Date.now(), file="times.json") { - return this.getTimes(file).then( - times => { - times.reset = time; - return this.writeTimes(times, file); - } - ); - }, - - /* Returns a promise that resolves to the time the profile was reset, - * or undefined if not recorded. - */ - get reset() { - return this.getTimes().then( - times => times.reset - ); - }, -} - -/** - * Measurements pertaining to the user's profile. - */ -// This is "version 1" of the metadata measurement - it must remain, but -// it's currently unused - see bug 1063714 comment 12 for why. -function ProfileMetadataMeasurement() { - Metrics.Measurement.call(this); -} -ProfileMetadataMeasurement.prototype = { - __proto__: Metrics.Measurement.prototype, - - name: DEFAULT_PROFILE_MEASUREMENT_NAME, - version: 1, - - fields: { - // Profile creation date. Number of days since Unix epoch. - profileCreation: {type: Metrics.Storage.FIELD_LAST_NUMERIC}, - }, -}; - -// This is the current measurement - it adds the profileReset value. -function ProfileMetadataMeasurement2() { - Metrics.Measurement.call(this); -} -ProfileMetadataMeasurement2.prototype = { - __proto__: Metrics.Measurement.prototype, - - name: DEFAULT_PROFILE_MEASUREMENT_NAME, - version: DEFAULT_PROFILE_MEASUREMENT_VERSION, - - fields: { - // Profile creation date. Number of days since Unix epoch. - profileCreation: {type: Metrics.Storage.FIELD_LAST_NUMERIC}, - // Profile reset date. Number of days since Unix epoch. - profileReset: {type: Metrics.Storage.FIELD_LAST_NUMERIC}, - }, -}; - -/** - * Turn a millisecond timestamp into a day timestamp. - * - * @param msec a number of milliseconds since epoch. - * @return the number of whole days denoted by the input. - */ -function truncate(msec) { - return Math.floor(msec / MILLISECONDS_PER_DAY); -} - -/** - * A Metrics.Provider for profile metadata, such as profile creation and - * reset time. - */ -this.ProfileMetadataProvider = function() { - Metrics.Provider.call(this); -} -this.ProfileMetadataProvider.prototype = { - __proto__: Metrics.Provider.prototype, - - name: "org.mozilla.profile", - - measurementTypes: [ProfileMetadataMeasurement2], - - pullOnly: true, - - getProfileDays: Task.async(function* () { - let result = {}; - let accessor = new ProfileTimesAccessor(null, this._log); - - let created = yield accessor.created; - result["profileCreation"] = truncate(created); - let reset = yield accessor.reset; - if (reset) { - result["profileReset"] = truncate(reset); - } - return result; - }), - - collectConstantData: function () { - let m = this.getMeasurement(DEFAULT_PROFILE_MEASUREMENT_NAME, - DEFAULT_PROFILE_MEASUREMENT_VERSION); - - return Task.spawn(function* collectConstants() { - let days = yield this.getProfileDays(); - - yield this.enqueueStorageOperation(function storeDays() { - return Task.spawn(function* () { - yield m.setLastNumeric("profileCreation", days["profileCreation"]); - if (days["profileReset"]) { - yield m.setLastNumeric("profileReset", days["profileReset"]); - } - }); - }); - }.bind(this)); - }, -}; - diff --git a/services/healthreport/providers.jsm b/services/healthreport/providers.jsm deleted file mode 100644 index f67261e220..0000000000 --- a/services/healthreport/providers.jsm +++ /dev/null @@ -1,1565 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/** - * This file contains metrics data providers for the Firefox Health - * Report. Ideally each provider in this file exists in separate modules - * and lives close to the code it is querying. However, because of the - * overhead of JS compartments (which are created for each module), we - * currently have all the code in one file. When the overhead of - * compartments reaches a reasonable level, this file should be split - * up. - */ - -#ifndef MERGED_COMPARTMENT - -"use strict"; - -this.EXPORTED_SYMBOLS = [ - "AddonsProvider", - "AppInfoProvider", - "HealthReportProvider", - "HotfixProvider", - "PlacesProvider", - "SearchesProvider", - "SessionsProvider", - "SysInfoProvider", -]; - -const {classes: Cc, interfaces: Ci, utils: Cu} = Components; - -Cu.import("resource://gre/modules/Metrics.jsm"); - -#endif - -Cu.import("resource://gre/modules/Promise.jsm"); -Cu.import("resource://gre/modules/osfile.jsm"); -Cu.import("resource://gre/modules/Preferences.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://gre/modules/Task.jsm"); -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource://services-common/utils.js"); - -XPCOMUtils.defineLazyModuleGetter(this, "AddonManager", - "resource://gre/modules/AddonManager.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "UpdateChannel", - "resource://gre/modules/UpdateChannel.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "PlacesDBUtils", - "resource://gre/modules/PlacesDBUtils.jsm"); - - -const LAST_NUMERIC_FIELD = {type: Metrics.Storage.FIELD_LAST_NUMERIC}; -const LAST_TEXT_FIELD = {type: Metrics.Storage.FIELD_LAST_TEXT}; -const DAILY_DISCRETE_NUMERIC_FIELD = {type: Metrics.Storage.FIELD_DAILY_DISCRETE_NUMERIC}; -const DAILY_LAST_NUMERIC_FIELD = {type: Metrics.Storage.FIELD_DAILY_LAST_NUMERIC}; -const DAILY_LAST_TEXT_FIELD = {type: Metrics.Storage.FIELD_DAILY_LAST_TEXT}; -const DAILY_COUNTER_FIELD = {type: Metrics.Storage.FIELD_DAILY_COUNTER}; - -const TELEMETRY_PREF = "toolkit.telemetry.enabled"; -const SEARCH_COHORT_PREF = "browser.search.cohort"; - -function isTelemetryEnabled(prefs) { - return prefs.get(TELEMETRY_PREF, false); -} - -/** - * Represents basic application state. - * - * This is roughly a union of nsIXULAppInfo, nsIXULRuntime, with a few extra - * pieces thrown in. - */ -function AppInfoMeasurement() { - Metrics.Measurement.call(this); -} - -AppInfoMeasurement.prototype = Object.freeze({ - __proto__: Metrics.Measurement.prototype, - - name: "appinfo", - version: 2, - - fields: { - vendor: LAST_TEXT_FIELD, - name: LAST_TEXT_FIELD, - id: LAST_TEXT_FIELD, - version: LAST_TEXT_FIELD, - appBuildID: LAST_TEXT_FIELD, - platformVersion: LAST_TEXT_FIELD, - platformBuildID: LAST_TEXT_FIELD, - os: LAST_TEXT_FIELD, - xpcomabi: LAST_TEXT_FIELD, - updateChannel: LAST_TEXT_FIELD, - distributionID: LAST_TEXT_FIELD, - distributionVersion: LAST_TEXT_FIELD, - hotfixVersion: LAST_TEXT_FIELD, - locale: LAST_TEXT_FIELD, - isDefaultBrowser: {type: Metrics.Storage.FIELD_DAILY_LAST_NUMERIC}, - isTelemetryEnabled: {type: Metrics.Storage.FIELD_DAILY_LAST_NUMERIC}, - isBlocklistEnabled: {type: Metrics.Storage.FIELD_DAILY_LAST_NUMERIC}, - }, -}); - -/** - * Legacy version of app info before Telemetry was added. - * - * The "last" fields have all been removed. We only report the longitudinal - * field. - */ -function AppInfoMeasurement1() { - Metrics.Measurement.call(this); -} - -AppInfoMeasurement1.prototype = Object.freeze({ - __proto__: Metrics.Measurement.prototype, - - name: "appinfo", - version: 1, - - fields: { - isDefaultBrowser: {type: Metrics.Storage.FIELD_DAILY_LAST_NUMERIC}, - }, -}); - - -function AppVersionMeasurement1() { - Metrics.Measurement.call(this); -} - -AppVersionMeasurement1.prototype = Object.freeze({ - __proto__: Metrics.Measurement.prototype, - - name: "versions", - version: 1, - - fields: { - version: {type: Metrics.Storage.FIELD_DAILY_DISCRETE_TEXT}, - }, -}); - -// Version 2 added the build ID. -function AppVersionMeasurement2() { - Metrics.Measurement.call(this); -} - -AppVersionMeasurement2.prototype = Object.freeze({ - __proto__: Metrics.Measurement.prototype, - - name: "versions", - version: 2, - - fields: { - appVersion: {type: Metrics.Storage.FIELD_DAILY_DISCRETE_TEXT}, - platformVersion: {type: Metrics.Storage.FIELD_DAILY_DISCRETE_TEXT}, - appBuildID: {type: Metrics.Storage.FIELD_DAILY_DISCRETE_TEXT}, - platformBuildID: {type: Metrics.Storage.FIELD_DAILY_DISCRETE_TEXT}, - }, -}); - -/** - * Holds data on the application update functionality. - */ -function AppUpdateMeasurement1() { - Metrics.Measurement.call(this); -} - -AppUpdateMeasurement1.prototype = Object.freeze({ - __proto__: Metrics.Measurement.prototype, - - name: "update", - version: 1, - - fields: { - enabled: {type: Metrics.Storage.FIELD_DAILY_LAST_NUMERIC}, - autoDownload: {type: Metrics.Storage.FIELD_DAILY_LAST_NUMERIC}, - }, -}); - -this.AppInfoProvider = function AppInfoProvider() { - Metrics.Provider.call(this); - - this._prefs = new Preferences({defaultBranch: null}); -} -AppInfoProvider.prototype = Object.freeze({ - __proto__: Metrics.Provider.prototype, - - name: "org.mozilla.appInfo", - - measurementTypes: [ - AppInfoMeasurement, - AppInfoMeasurement1, - AppUpdateMeasurement1, - AppVersionMeasurement1, - AppVersionMeasurement2, - ], - - pullOnly: true, - - appInfoFields: { - // From nsIXULAppInfo. - vendor: "vendor", - name: "name", - id: "ID", - version: "version", - appBuildID: "appBuildID", - platformVersion: "platformVersion", - platformBuildID: "platformBuildID", - - // From nsIXULRuntime. - os: "OS", - xpcomabi: "XPCOMABI", - }, - - postInit: function () { - return Task.spawn(this._postInit.bind(this)); - }, - - _postInit: function () { - let recordEmptyAppInfo = function () { - this._setCurrentAppVersion(""); - this._setCurrentPlatformVersion(""); - this._setCurrentAppBuildID(""); - return this._setCurrentPlatformBuildID(""); - }.bind(this); - - // Services.appInfo should always be defined for any reasonably behaving - // Gecko app. If it isn't, we insert a empty string sentinel value. - let ai; - try { - ai = Services.appinfo; - } catch (ex) { - this._log.error("Could not obtain Services.appinfo: " + - CommonUtils.exceptionStr(ex)); - yield recordEmptyAppInfo(); - return; - } - - if (!ai) { - this._log.error("Services.appinfo is unavailable."); - yield recordEmptyAppInfo(); - return; - } - - let currentAppVersion = ai.version; - let currentPlatformVersion = ai.platformVersion; - let currentAppBuildID = ai.appBuildID; - let currentPlatformBuildID = ai.platformBuildID; - - // State's name doesn't contain "app" for historical compatibility. - let lastAppVersion = yield this.getState("lastVersion"); - let lastPlatformVersion = yield this.getState("lastPlatformVersion"); - let lastAppBuildID = yield this.getState("lastAppBuildID"); - let lastPlatformBuildID = yield this.getState("lastPlatformBuildID"); - - if (currentAppVersion != lastAppVersion) { - yield this._setCurrentAppVersion(currentAppVersion); - } - - if (currentPlatformVersion != lastPlatformVersion) { - yield this._setCurrentPlatformVersion(currentPlatformVersion); - } - - if (currentAppBuildID != lastAppBuildID) { - yield this._setCurrentAppBuildID(currentAppBuildID); - } - - if (currentPlatformBuildID != lastPlatformBuildID) { - yield this._setCurrentPlatformBuildID(currentPlatformBuildID); - } - }, - - _setCurrentAppVersion: function (version) { - this._log.info("Recording new application version: " + version); - let m = this.getMeasurement("versions", 2); - m.addDailyDiscreteText("appVersion", version); - - // "app" not encoded in key for historical compatibility. - return this.setState("lastVersion", version); - }, - - _setCurrentPlatformVersion: function (version) { - this._log.info("Recording new platform version: " + version); - let m = this.getMeasurement("versions", 2); - m.addDailyDiscreteText("platformVersion", version); - return this.setState("lastPlatformVersion", version); - }, - - _setCurrentAppBuildID: function (build) { - this._log.info("Recording new application build ID: " + build); - let m = this.getMeasurement("versions", 2); - m.addDailyDiscreteText("appBuildID", build); - return this.setState("lastAppBuildID", build); - }, - - _setCurrentPlatformBuildID: function (build) { - this._log.info("Recording new platform build ID: " + build); - let m = this.getMeasurement("versions", 2); - m.addDailyDiscreteText("platformBuildID", build); - return this.setState("lastPlatformBuildID", build); - }, - - - collectConstantData: function () { - return this.storage.enqueueTransaction(this._populateConstants.bind(this)); - }, - - _populateConstants: function () { - let m = this.getMeasurement(AppInfoMeasurement.prototype.name, - AppInfoMeasurement.prototype.version); - - let ai; - try { - ai = Services.appinfo; - } catch (ex) { - this._log.warn("Could not obtain Services.appinfo: " + - CommonUtils.exceptionStr(ex)); - throw ex; - } - - if (!ai) { - this._log.warn("Services.appinfo is unavailable."); - throw ex; - } - - for (let [k, v] in Iterator(this.appInfoFields)) { - try { - yield m.setLastText(k, ai[v]); - } catch (ex) { - this._log.warn("Error obtaining Services.appinfo." + v); - } - } - - try { - yield m.setLastText("updateChannel", UpdateChannel.get()); - } catch (ex) { - this._log.warn("Could not obtain update channel: " + - CommonUtils.exceptionStr(ex)); - } - - yield m.setLastText("distributionID", this._prefs.get("distribution.id", "")); - yield m.setLastText("distributionVersion", this._prefs.get("distribution.version", "")); - yield m.setLastText("hotfixVersion", this._prefs.get("extensions.hotfix.lastVersion", "")); - - try { - let locale = Cc["@mozilla.org/chrome/chrome-registry;1"] - .getService(Ci.nsIXULChromeRegistry) - .getSelectedLocale("global"); - yield m.setLastText("locale", locale); - } catch (ex) { - this._log.warn("Could not obtain application locale: " + - CommonUtils.exceptionStr(ex)); - } - - // FUTURE this should be retrieved periodically or at upload time. - yield this._recordIsTelemetryEnabled(m); - yield this._recordIsBlocklistEnabled(m); - yield this._recordDefaultBrowser(m); - }, - - _recordIsTelemetryEnabled: function (m) { - let enabled = isTelemetryEnabled(this._prefs); - this._log.debug("Recording telemetry enabled (" + TELEMETRY_PREF + "): " + enabled); - yield m.setDailyLastNumeric("isTelemetryEnabled", enabled ? 1 : 0); - }, - - _recordIsBlocklistEnabled: function (m) { - let enabled = this._prefs.get("extensions.blocklist.enabled", false); - this._log.debug("Recording blocklist enabled: " + enabled); - yield m.setDailyLastNumeric("isBlocklistEnabled", enabled ? 1 : 0); - }, - - _recordDefaultBrowser: function (m) { - let shellService; - try { - shellService = Cc["@mozilla.org/browser/shell-service;1"] - .getService(Ci.nsIShellService); - } catch (ex) { - this._log.warn("Could not obtain shell service: " + - CommonUtils.exceptionStr(ex)); - } - - let isDefault = -1; - - if (shellService) { - try { - // This uses the same set of flags used by the pref pane. - isDefault = shellService.isDefaultBrowser(false, true) ? 1 : 0; - } catch (ex) { - this._log.warn("Could not determine if default browser: " + - CommonUtils.exceptionStr(ex)); - } - } - - return m.setDailyLastNumeric("isDefaultBrowser", isDefault); - }, - - collectDailyData: function () { - return this.storage.enqueueTransaction(function getDaily() { - let m = this.getMeasurement(AppUpdateMeasurement1.prototype.name, - AppUpdateMeasurement1.prototype.version); - - let enabled = this._prefs.get("app.update.enabled", false); - yield m.setDailyLastNumeric("enabled", enabled ? 1 : 0); - - let auto = this._prefs.get("app.update.auto", false); - yield m.setDailyLastNumeric("autoDownload", auto ? 1 : 0); - }.bind(this)); - }, -}); - - -function SysInfoMeasurement() { - Metrics.Measurement.call(this); -} - -SysInfoMeasurement.prototype = Object.freeze({ - __proto__: Metrics.Measurement.prototype, - - name: "sysinfo", - version: 2, - - fields: { - cpuCount: {type: Metrics.Storage.FIELD_LAST_NUMERIC}, - memoryMB: {type: Metrics.Storage.FIELD_LAST_NUMERIC}, - manufacturer: LAST_TEXT_FIELD, - device: LAST_TEXT_FIELD, - hardware: LAST_TEXT_FIELD, - name: LAST_TEXT_FIELD, - version: LAST_TEXT_FIELD, - architecture: LAST_TEXT_FIELD, - isWow64: LAST_NUMERIC_FIELD, - }, -}); - - -this.SysInfoProvider = function SysInfoProvider() { - Metrics.Provider.call(this); -}; - -SysInfoProvider.prototype = Object.freeze({ - __proto__: Metrics.Provider.prototype, - - name: "org.mozilla.sysinfo", - - measurementTypes: [SysInfoMeasurement], - - pullOnly: true, - - sysInfoFields: { - cpucount: "cpuCount", - memsize: "memoryMB", - manufacturer: "manufacturer", - device: "device", - hardware: "hardware", - name: "name", - version: "version", - arch: "architecture", - isWow64: "isWow64", - }, - - collectConstantData: function () { - return this.storage.enqueueTransaction(this._populateConstants.bind(this)); - }, - - _populateConstants: function () { - let m = this.getMeasurement(SysInfoMeasurement.prototype.name, - SysInfoMeasurement.prototype.version); - - let si = Cc["@mozilla.org/system-info;1"] - .getService(Ci.nsIPropertyBag2); - - for (let [k, v] in Iterator(this.sysInfoFields)) { - try { - if (!si.hasKey(k)) { - this._log.debug("Property not available: " + k); - continue; - } - - let value = si.getProperty(k); - let method = "setLastText"; - - if (["cpucount", "memsize"].indexOf(k) != -1) { - let converted = parseInt(value, 10); - if (Number.isNaN(converted)) { - continue; - } - - value = converted; - method = "setLastNumeric"; - } - - switch (k) { - case "memsize": - // Round memory to mebibytes. - value = Math.round(value / 1048576); - break; - case "isWow64": - // Property is only present on Windows. hasKey() skipping from - // above ensures undefined or null doesn't creep in here. - value = value ? 1 : 0; - method = "setLastNumeric"; - break; - } - - yield m[method](v, value); - } catch (ex) { - this._log.warn("Error obtaining system info field: " + k + " " + - CommonUtils.exceptionStr(ex)); - } - } - }, -}); - - -/** - * Holds information about the current/active session. - * - * The fields within the current session are moved to daily session fields when - * the application is shut down. - * - * This measurement is backed by the SessionRecorder, not the database. - */ -function CurrentSessionMeasurement() { - Metrics.Measurement.call(this); -} - -CurrentSessionMeasurement.prototype = Object.freeze({ - __proto__: Metrics.Measurement.prototype, - - name: "current", - version: 3, - - // Storage is in preferences. - fields: {}, - - /** - * All data is stored in prefs, so we have a custom implementation. - */ - getValues: function () { - let sessions = this.provider.healthReporter.sessionRecorder; - - let fields = new Map(); - let now = new Date(); - fields.set("startDay", [now, Metrics.dateToDays(sessions.startDate)]); - fields.set("activeTicks", [now, sessions.activeTicks]); - fields.set("totalTime", [now, sessions.totalTime]); - fields.set("main", [now, sessions.main]); - fields.set("firstPaint", [now, sessions.firstPaint]); - fields.set("sessionRestored", [now, sessions.sessionRestored]); - - return CommonUtils.laterTickResolvingPromise({ - days: new Metrics.DailyValues(), - singular: fields, - }); - }, - - _serializeJSONSingular: function (data) { - let result = {"_v": this.version}; - - for (let [field, value] of data) { - result[field] = value[1]; - } - - return result; - }, -}); - -/** - * Records a history of all application sessions. - */ -function PreviousSessionsMeasurement() { - Metrics.Measurement.call(this); -} - -PreviousSessionsMeasurement.prototype = Object.freeze({ - __proto__: Metrics.Measurement.prototype, - - name: "previous", - version: 3, - - fields: { - // Milliseconds of sessions that were properly shut down. - cleanActiveTicks: DAILY_DISCRETE_NUMERIC_FIELD, - cleanTotalTime: DAILY_DISCRETE_NUMERIC_FIELD, - - // Milliseconds of sessions that were not properly shut down. - abortedActiveTicks: DAILY_DISCRETE_NUMERIC_FIELD, - abortedTotalTime: DAILY_DISCRETE_NUMERIC_FIELD, - - // Startup times in milliseconds. - main: DAILY_DISCRETE_NUMERIC_FIELD, - firstPaint: DAILY_DISCRETE_NUMERIC_FIELD, - sessionRestored: DAILY_DISCRETE_NUMERIC_FIELD, - }, -}); - - -/** - * Records information about the current browser session. - * - * A browser session is defined as an application/process lifetime. We - * start a new session when the application starts (essentially when - * this provider is instantiated) and end the session on shutdown. - * - * As the application runs, we record basic information about the - * "activity" of the session. Activity is defined by the presence of - * physical input into the browser (key press, mouse click, touch, etc). - * - * We differentiate between regular sessions and "aborted" sessions. An - * aborted session is one that does not end expectedly. This is often the - * result of a crash. We detect aborted sessions by storing the current - * session separate from completed sessions. We normally move the - * current session to completed sessions on application shutdown. If a - * current session is present on application startup, that means that - * the previous session was aborted. - */ -this.SessionsProvider = function () { - Metrics.Provider.call(this); -}; - -SessionsProvider.prototype = Object.freeze({ - __proto__: Metrics.Provider.prototype, - - name: "org.mozilla.appSessions", - - measurementTypes: [CurrentSessionMeasurement, PreviousSessionsMeasurement], - - pullOnly: true, - - collectConstantData: function () { - let previous = this.getMeasurement("previous", 3); - - return this.storage.enqueueTransaction(this._recordAndPruneSessions.bind(this)); - }, - - _recordAndPruneSessions: function () { - this._log.info("Moving previous sessions from session recorder to storage."); - let recorder = this.healthReporter.sessionRecorder; - let sessions = recorder.getPreviousSessions(); - this._log.debug("Found " + Object.keys(sessions).length + " previous sessions."); - - let daily = this.getMeasurement("previous", 3); - - // Please note the coupling here between the session recorder and our state. - // If the pruned index or the current index of the session recorder is ever - // deleted or reset to 0, our stored state of a later index would mean that - // new sessions would never be captured by this provider until the session - // recorder index catches up to our last session ID. This should not happen - // under normal circumstances, so we don't worry too much about it. We - // should, however, consider this as part of implementing bug 841561. - let lastRecordedSession = yield this.getState("lastSession"); - if (lastRecordedSession === null) { - lastRecordedSession = -1; - } - this._log.debug("The last recorded session was #" + lastRecordedSession); - - for (let [index, session] in Iterator(sessions)) { - if (index <= lastRecordedSession) { - this._log.warn("Already recorded session " + index + ". Did the last " + - "session crash or have an issue saving the prefs file?"); - continue; - } - - let type = session.clean ? "clean" : "aborted"; - let date = session.startDate; - yield daily.addDailyDiscreteNumeric(type + "ActiveTicks", session.activeTicks, date); - yield daily.addDailyDiscreteNumeric(type + "TotalTime", session.totalTime, date); - - for (let field of ["main", "firstPaint", "sessionRestored"]) { - yield daily.addDailyDiscreteNumeric(field, session[field], date); - } - - lastRecordedSession = index; - } - - yield this.setState("lastSession", "" + lastRecordedSession); - recorder.pruneOldSessions(new Date()); - }, -}); - -/** - * Stores the set of active addons in storage. - * - * We do things a little differently than most other measurements. Because - * addons are difficult to shoehorn into distinct fields, we simply store a - * JSON blob in storage in a text field. - */ -function ActiveAddonsMeasurement() { - Metrics.Measurement.call(this); - - this._serializers = {}; - this._serializers[this.SERIALIZE_JSON] = { - singular: this._serializeJSONSingular.bind(this), - // We don't need a daily serializer because we have none of this data. - }; -} - -ActiveAddonsMeasurement.prototype = Object.freeze({ - __proto__: Metrics.Measurement.prototype, - - name: "addons", - version: 2, - - fields: { - addons: LAST_TEXT_FIELD, - }, - - _serializeJSONSingular: function (data) { - if (!data.has("addons")) { - this._log.warn("Don't have addons info. Weird."); - return null; - } - - // Exceptions are caught in the caller. - let result = JSON.parse(data.get("addons")[1]); - result._v = this.version; - return result; - }, -}); - -/** - * Stores the set of active plugins in storage. - * - * This stores the data in a JSON blob in a text field similar to the - * ActiveAddonsMeasurement. - */ -function ActivePluginsMeasurement() { - Metrics.Measurement.call(this); - - this._serializers = {}; - this._serializers[this.SERIALIZE_JSON] = { - singular: this._serializeJSONSingular.bind(this), - // We don't need a daily serializer because we have none of this data. - }; -} - -ActivePluginsMeasurement.prototype = Object.freeze({ - __proto__: Metrics.Measurement.prototype, - - name: "plugins", - version: 1, - - fields: { - plugins: LAST_TEXT_FIELD, - }, - - _serializeJSONSingular: function (data) { - if (!data.has("plugins")) { - this._log.warn("Don't have plugins info. Weird."); - return null; - } - - // Exceptions are caught in the caller. - let result = JSON.parse(data.get("plugins")[1]); - result._v = this.version; - return result; - }, -}); - -function ActiveGMPluginsMeasurement() { - Metrics.Measurement.call(this); - - this._serializers = {}; - this._serializers[this.SERIALIZE_JSON] = { - singular: this._serializeJSONSingular.bind(this), - }; -} - -ActiveGMPluginsMeasurement.prototype = Object.freeze({ - __proto__: Metrics.Measurement.prototype, - - name: "gm-plugins", - version: 1, - - fields: { - "gm-plugins": LAST_TEXT_FIELD, - }, - - _serializeJSONSingular: function (data) { - if (!data.has("gm-plugins")) { - this._log.warn("Don't have GM plugins info. Weird."); - return null; - } - - let result = JSON.parse(data.get("gm-plugins")[1]); - result._v = this.version; - return result; - }, -}); - -function AddonCountsMeasurement() { - Metrics.Measurement.call(this); -} - -AddonCountsMeasurement.prototype = Object.freeze({ - __proto__: Metrics.Measurement.prototype, - - name: "counts", - version: 2, - - fields: { - theme: DAILY_LAST_NUMERIC_FIELD, - lwtheme: DAILY_LAST_NUMERIC_FIELD, - plugin: DAILY_LAST_NUMERIC_FIELD, - extension: DAILY_LAST_NUMERIC_FIELD, - service: DAILY_LAST_NUMERIC_FIELD, - }, -}); - - -/** - * Legacy version of addons counts before services was added. - */ -function AddonCountsMeasurement1() { - Metrics.Measurement.call(this); -} - -AddonCountsMeasurement1.prototype = Object.freeze({ - __proto__: Metrics.Measurement.prototype, - - name: "counts", - version: 1, - - fields: { - theme: DAILY_LAST_NUMERIC_FIELD, - lwtheme: DAILY_LAST_NUMERIC_FIELD, - plugin: DAILY_LAST_NUMERIC_FIELD, - extension: DAILY_LAST_NUMERIC_FIELD, - }, -}); - - -this.AddonsProvider = function () { - Metrics.Provider.call(this); - - this._prefs = new Preferences({defaultBranch: null}); -}; - -AddonsProvider.prototype = Object.freeze({ - __proto__: Metrics.Provider.prototype, - - // Whenever these AddonListener callbacks are called, we repopulate - // and store the set of addons. Note that these events will only fire - // for restartless add-ons. For actions that require a restart, we - // will catch the change after restart. The alternative is a lot of - // state tracking here, which isn't desirable. - ADDON_LISTENER_CALLBACKS: [ - "onEnabled", - "onDisabled", - "onInstalled", - "onUninstalled", - ], - - // Add-on types for which full details are uploaded in the - // ActiveAddonsMeasurement. All other types are ignored. - FULL_DETAIL_TYPES: [ - "extension", - "service", - ], - - name: "org.mozilla.addons", - - measurementTypes: [ - ActiveAddonsMeasurement, - ActivePluginsMeasurement, - ActiveGMPluginsMeasurement, - AddonCountsMeasurement1, - AddonCountsMeasurement, - ], - - postInit: function () { - let listener = {}; - - for (let method of this.ADDON_LISTENER_CALLBACKS) { - listener[method] = this._collectAndStoreAddons.bind(this); - } - - this._listener = listener; - AddonManager.addAddonListener(this._listener); - - return CommonUtils.laterTickResolvingPromise(); - }, - - onShutdown: function () { - AddonManager.removeAddonListener(this._listener); - this._listener = null; - - return CommonUtils.laterTickResolvingPromise(); - }, - - collectConstantData: function () { - return this._collectAndStoreAddons(); - }, - - _collectAndStoreAddons: function () { - let deferred = Promise.defer(); - - AddonManager.getAllAddons(function onAllAddons(allAddons) { - let data; - let addonsField; - let pluginsField; - let gmPluginsField; - try { - data = this._createDataStructure(allAddons); - addonsField = JSON.stringify(data.addons); - pluginsField = JSON.stringify(data.plugins); - gmPluginsField = JSON.stringify(data.gmPlugins); - } catch (ex) { - this._log.warn("Exception when populating add-ons data structure: " + - CommonUtils.exceptionStr(ex)); - deferred.reject(ex); - return; - } - - let now = new Date(); - let addons = this.getMeasurement("addons", 2); - let plugins = this.getMeasurement("plugins", 1); - let gmPlugins = this.getMeasurement("gm-plugins", 1); - let counts = this.getMeasurement(AddonCountsMeasurement.prototype.name, - AddonCountsMeasurement.prototype.version); - - this.enqueueStorageOperation(function storageAddons() { - for (let type in data.counts) { - try { - counts.fieldID(type); - } catch (ex) { - this._log.warn("Add-on type without field: " + type); - continue; - } - - counts.setDailyLastNumeric(type, data.counts[type], now); - } - - return addons.setLastText("addons", addonsField).then( - function onSuccess() { - return plugins.setLastText("plugins", pluginsField).then( - function onSuccess() { - return gmPlugins.setLastText("gm-plugins", gmPluginsField).then( - function onSuccess() { - deferred.resolve(); - }, - function onError(error) { - deferred.reject(error); - }); - }, - function onError(error) { deferred.reject(error); } - ); - }, - function onError(error) { deferred.reject(error); } - ); - }.bind(this)); - }.bind(this)); - - return deferred.promise; - }, - - COPY_ADDON_FIELDS: [ - "userDisabled", - "appDisabled", - "name", - "version", - "type", - "scope", - "description", - "foreignInstall", - "hasBinaryComponents", - ], - - COPY_PLUGIN_FIELDS: [ - "name", - "version", - "description", - "blocklisted", - "disabled", - "clicktoplay", - ], - - _createDataStructure: function (addons) { - let data = { - addons: {}, - plugins: {}, - gmPlugins: {}, - counts: {} - }; - - for (let addon of addons) { - let type = addon.type; - - // We count plugins separately below. - if (addon.type == "plugin") { - if (addon.isGMPlugin) { - data.gmPlugins[addon.id] = { - version: addon.version, - userDisabled: addon.userDisabled, - applyBackgroundUpdates: addon.applyBackgroundUpdates, - }; - } - continue; - } - - data.counts[type] = (data.counts[type] || 0) + 1; - - if (this.FULL_DETAIL_TYPES.indexOf(addon.type) == -1) { - continue; - } - - let obj = {}; - for (let field of this.COPY_ADDON_FIELDS) { - obj[field] = addon[field]; - } - - if (addon.installDate) { - obj.installDay = this._dateToDays(addon.installDate); - } - - if (addon.updateDate) { - obj.updateDay = this._dateToDays(addon.updateDate); - } - - data.addons[addon.id] = obj; - } - - let pluginTags = Cc["@mozilla.org/plugin/host;1"]. - getService(Ci.nsIPluginHost). - getPluginTags({}); - - for (let tag of pluginTags) { - let obj = { - mimeTypes: tag.getMimeTypes({}), - }; - - for (let field of this.COPY_PLUGIN_FIELDS) { - obj[field] = tag[field]; - } - - // Plugins need to have a filename and a name, so this can't be empty. - let id = tag.filename + ":" + tag.name + ":" + tag.version + ":" - + tag.description; - data.plugins[id] = obj; - } - - data.counts["plugin"] = pluginTags.length; - - return data; - }, -}); - -/** - * Records data from update hotfixes. - * - * This measurement has dynamic fields. Field names are of the form - * . where is the hotfix version that produced - * the data. e.g. "v20140527". The sub-version of the hotfix is omitted - * because hotfixes can go through multiple minor versions during development - * and we don't want to introduce more fields than necessary. Furthermore, - * the subsequent dots make parsing field names slightly harder. By stripping, - * we can just split on the first dot. - */ -function UpdateHotfixMeasurement1() { - Metrics.Measurement.call(this); -} - -UpdateHotfixMeasurement1.prototype = Object.freeze({ - __proto__: Metrics.Measurement.prototype, - - name: "update", - version: 1, - - hotfixFieldTypes: { - "upgradedFrom": Metrics.Storage.FIELD_LAST_TEXT, - "uninstallReason": Metrics.Storage.FIELD_LAST_TEXT, - "downloadAttempts": Metrics.Storage.FIELD_LAST_NUMERIC, - "downloadFailures": Metrics.Storage.FIELD_LAST_NUMERIC, - "installAttempts": Metrics.Storage.FIELD_LAST_NUMERIC, - "installFailures": Metrics.Storage.FIELD_LAST_NUMERIC, - "notificationsShown": Metrics.Storage.FIELD_LAST_NUMERIC, - }, - - fields: { }, - - // Our fields have dynamic names from the hotfix version that supplied them. - // We need to override the default behavior to deal with unknown fields. - shouldIncludeField: function (name) { - return name.includes("."); - }, - - fieldType: function (name) { - for (let known in this.hotfixFieldTypes) { - if (name.endsWith(known)) { - return this.hotfixFieldTypes[known]; - } - } - - return Metrics.Measurement.prototype.fieldType.call(this, name); - }, -}); - -this.HotfixProvider = function () { - Metrics.Provider.call(this); -}; - -HotfixProvider.prototype = Object.freeze({ - __proto__: Metrics.Provider.prototype, - - name: "org.mozilla.hotfix", - measurementTypes: [ - UpdateHotfixMeasurement1, - ], - - pullOnly: true, - - collectDailyData: function () { - return this.storage.enqueueTransaction(this._populateHotfixData.bind(this)); - }, - - _populateHotfixData: function* () { - let m = this.getMeasurement("update", 1); - - // The update hotfix retains its JSON state file after uninstall. - // The initial update hotfix had a hard-coded filename. We treat it - // specially. Subsequent update hotfixes named their files in a - // recognizeable pattern so we don't need to update this probe code to - // know about them. - let files = [ - ["v20140527", OS.Path.join(OS.Constants.Path.profileDir, - "hotfix.v20140527.01.json")], - ]; - - let it = new OS.File.DirectoryIterator(OS.Constants.Path.profileDir); - try { - yield it.forEach((e, index, it) => { - let m = e.name.match(/^updateHotfix\.([a-zA-Z0-9]+)\.json$/); - if (m) { - files.push([m[1], e.path]); - } - }); - } finally { - it.close(); - } - - let decoder = new TextDecoder(); - for (let e of files) { - let [version, path] = e; - let p; - try { - let data = yield OS.File.read(path); - p = JSON.parse(decoder.decode(data)); - } catch (ex if ex instanceof OS.File.Error && ex.becauseNoSuchFile) { - continue; - } catch (ex) { - this._log.warn("Error loading update hotfix payload: " + ex.message); - } - - // Wrap just in case. - try { - for (let k in m.hotfixFieldTypes) { - if (!(k in p)) { - continue; - } - - let value = p[k]; - if (value === null && k == "uninstallReason") { - value = "STILL_INSTALLED"; - } - - let field = version + "." + k; - let fieldType; - let storageOp; - switch (typeof(value)) { - case "string": - fieldType = this.storage.FIELD_LAST_TEXT; - storageOp = "setLastTextFromFieldID"; - break; - case "number": - fieldType = this.storage.FIELD_LAST_NUMERIC; - storageOp = "setLastNumericFromFieldID"; - break; - default: - this._log.warn("Unknown value in hotfix state: " + k + "=" + value); - continue; - } - - if (this.storage.hasFieldFromMeasurement(m.id, field, fieldType)) { - let fieldID = this.storage.fieldIDFromMeasurement(m.id, field); - yield this.storage[storageOp](fieldID, value); - } else { - let fieldID = yield this.storage.registerField(m.id, field, - fieldType); - yield this.storage[storageOp](fieldID, value); - } - } - - } catch (ex) { - this._log.warn("Error processing update hotfix data: " + ex); - } - } - }, -}); - -/** - * Holds basic statistics about the Places database. - */ -function PlacesMeasurement() { - Metrics.Measurement.call(this); -} - -PlacesMeasurement.prototype = Object.freeze({ - __proto__: Metrics.Measurement.prototype, - - name: "places", - version: 1, - - fields: { - pages: DAILY_LAST_NUMERIC_FIELD, - bookmarks: DAILY_LAST_NUMERIC_FIELD, - }, -}); - - -/** - * Collects information about Places. - */ -this.PlacesProvider = function () { - Metrics.Provider.call(this); -}; - -PlacesProvider.prototype = Object.freeze({ - __proto__: Metrics.Provider.prototype, - - name: "org.mozilla.places", - - measurementTypes: [PlacesMeasurement], - - collectDailyData: function () { - return this.storage.enqueueTransaction(this._collectData.bind(this)); - }, - - _collectData: function () { - let now = new Date(); - let data = yield this._getDailyValues(); - - let m = this.getMeasurement("places", 1); - - yield m.setDailyLastNumeric("pages", data.PLACES_PAGES_COUNT); - yield m.setDailyLastNumeric("bookmarks", data.PLACES_BOOKMARKS_COUNT); - }, - - _getDailyValues: function () { - let deferred = Promise.defer(); - - PlacesDBUtils.telemetry(null, function onResult(data) { - deferred.resolve(data); - }); - - return deferred.promise; - }, -}); - -function SearchCountMeasurement1() { - Metrics.Measurement.call(this); -} - -SearchCountMeasurement1.prototype = Object.freeze({ - __proto__: Metrics.Measurement.prototype, - - name: "counts", - version: 1, - - // We only record searches for search engines that have partner agreements - // with Mozilla. - fields: { - "amazon.com.abouthome": DAILY_COUNTER_FIELD, - "amazon.com.contextmenu": DAILY_COUNTER_FIELD, - "amazon.com.searchbar": DAILY_COUNTER_FIELD, - "amazon.com.urlbar": DAILY_COUNTER_FIELD, - "bing.abouthome": DAILY_COUNTER_FIELD, - "bing.contextmenu": DAILY_COUNTER_FIELD, - "bing.searchbar": DAILY_COUNTER_FIELD, - "bing.urlbar": DAILY_COUNTER_FIELD, - "google.abouthome": DAILY_COUNTER_FIELD, - "google.contextmenu": DAILY_COUNTER_FIELD, - "google.searchbar": DAILY_COUNTER_FIELD, - "google.urlbar": DAILY_COUNTER_FIELD, - "yahoo.abouthome": DAILY_COUNTER_FIELD, - "yahoo.contextmenu": DAILY_COUNTER_FIELD, - "yahoo.searchbar": DAILY_COUNTER_FIELD, - "yahoo.urlbar": DAILY_COUNTER_FIELD, - "other.abouthome": DAILY_COUNTER_FIELD, - "other.contextmenu": DAILY_COUNTER_FIELD, - "other.searchbar": DAILY_COUNTER_FIELD, - "other.urlbar": DAILY_COUNTER_FIELD, - }, -}); - -/** - * Records search counts per day per engine and where search initiated. - * - * We want to record granular details for individual locale-specific search - * providers, but only if they're Mozilla partners. In order to do this, we - * track the nsISearchEngine identifier, which denotes shipped search engines, - * and intersect those with our partner list. - * - * We don't use the search engine name directly, because it is shared across - * locales; e.g., eBay-de and eBay both share the name "eBay". - */ -function SearchCountMeasurementBase() { - this._fieldSpecs = {}; - Metrics.Measurement.call(this); -} - -SearchCountMeasurementBase.prototype = Object.freeze({ - __proto__: Metrics.Measurement.prototype, - - - // Our fields are dynamic. - get fields() { - return this._fieldSpecs; - }, - - /** - * Override the default behavior: serializers should include every counter - * field from the DB, even if we don't currently have it registered. - * - * Do this so we don't have to register several hundred fields to match - * various Firefox locales. - * - * We use the "provider.type" syntax as a rudimentary check for validity. - * - * We trust that measurement versioning is sufficient to exclude old provider - * data. - */ - shouldIncludeField: function (name) { - return name.contains("."); - }, - - /** - * The measurement type mechanism doesn't introspect the DB. Override it - * so that we can assume all unknown fields are counters. - */ - fieldType: function (name) { - if (name in this.fields) { - return this.fields[name].type; - } - - // Default to a counter. - return Metrics.Storage.FIELD_DAILY_COUNTER; - }, - - SOURCES: [ - "abouthome", - "contextmenu", - "newtab", - "searchbar", - "urlbar", - ], -}); - -function SearchCountMeasurement2() { - SearchCountMeasurementBase.call(this); -} - -SearchCountMeasurement2.prototype = Object.freeze({ - __proto__: SearchCountMeasurementBase.prototype, - name: "counts", - version: 2, -}); - -function SearchCountMeasurement3() { - SearchCountMeasurementBase.call(this); -} - -SearchCountMeasurement3.prototype = Object.freeze({ - __proto__: SearchCountMeasurementBase.prototype, - name: "counts", - version: 3, - - getEngines: function () { - return Services.search.getEngines(); - }, - - getEngineID: function (engine) { - if (!engine) { - return "other"; - } - if (engine.identifier) { - return engine.identifier; - } - return "other-" + engine.name; - }, -}); - -function SearchEnginesMeasurement1() { - Metrics.Measurement.call(this); -} - -SearchEnginesMeasurement1.prototype = Object.freeze({ - __proto__: Metrics.Measurement.prototype, - - name: "engines", - version: 2, - - fields: { - default: DAILY_LAST_TEXT_FIELD, - cohort: DAILY_LAST_TEXT_FIELD, - }, -}); - -this.SearchesProvider = function () { - Metrics.Provider.call(this); - - this._prefs = new Preferences({defaultBranch: null}); -}; - -this.SearchesProvider.prototype = Object.freeze({ - __proto__: Metrics.Provider.prototype, - - name: "org.mozilla.searches", - measurementTypes: [ - SearchCountMeasurement1, - SearchCountMeasurement2, - SearchCountMeasurement3, - SearchEnginesMeasurement1, - ], - - /** - * Initialize the search service before our measurements are touched. - */ - preInit: function (storage) { - // Initialize search service. - let deferred = Promise.defer(); - Services.search.init(function onInitComplete () { - deferred.resolve(); - }); - return deferred.promise; - }, - - collectDailyData: function () { - return this.storage.enqueueTransaction(function getDaily() { - let m = this.getMeasurement(SearchEnginesMeasurement1.prototype.name, - SearchEnginesMeasurement1.prototype.version); - - let engine; - try { - engine = Services.search.defaultEngine; - } catch (e) {} - let name; - - if (!engine) { - name = "NONE"; - } else if (engine.identifier) { - name = engine.identifier; - } else if (engine.name) { - name = "other-" + engine.name; - } else { - name = "UNDEFINED"; - } - - yield m.setDailyLastText("default", name); - - if (Services.prefs.prefHasUserValue(SEARCH_COHORT_PREF)) - yield m.setDailyLastText("cohort", Services.prefs.getCharPref(SEARCH_COHORT_PREF)); - }.bind(this)); - }, - - /** - * Record that a search occurred. - * - * @param engine - * (nsISearchEngine) The search engine used. - * @param source - * (string) Where the search was initiated from. Must be one of the - * SearchCountMeasurement2.SOURCES values. - * - * @return Promise<> - * The promise is resolved when the storage operation completes. - */ - recordSearch: function (engine, source) { - let m = this.getMeasurement("counts", 3); - - if (m.SOURCES.indexOf(source) == -1) { - throw new Error("Unknown source for search: " + source); - } - - let field = m.getEngineID(engine) + "." + source; - if (this.storage.hasFieldFromMeasurement(m.id, field, - this.storage.FIELD_DAILY_COUNTER)) { - let fieldID = this.storage.fieldIDFromMeasurement(m.id, field); - return this.enqueueStorageOperation(function recordSearchKnownField() { - return this.storage.incrementDailyCounterFromFieldID(fieldID); - }.bind(this)); - } - - // Otherwise, we first need to create the field. - return this.enqueueStorageOperation(function recordFieldAndSearch() { - // This function has to return a promise. - return Task.spawn(function () { - let fieldID = yield this.storage.registerField(m.id, field, - this.storage.FIELD_DAILY_COUNTER); - yield this.storage.incrementDailyCounterFromFieldID(fieldID); - }.bind(this)); - }.bind(this)); - }, -}); - -function HealthReportSubmissionMeasurement1() { - Metrics.Measurement.call(this); -} - -HealthReportSubmissionMeasurement1.prototype = Object.freeze({ - __proto__: Metrics.Measurement.prototype, - - name: "submissions", - version: 1, - - fields: { - firstDocumentUploadAttempt: DAILY_COUNTER_FIELD, - continuationUploadAttempt: DAILY_COUNTER_FIELD, - uploadSuccess: DAILY_COUNTER_FIELD, - uploadTransportFailure: DAILY_COUNTER_FIELD, - uploadServerFailure: DAILY_COUNTER_FIELD, - uploadClientFailure: DAILY_COUNTER_FIELD, - }, -}); - -function HealthReportSubmissionMeasurement2() { - Metrics.Measurement.call(this); -} - -HealthReportSubmissionMeasurement2.prototype = Object.freeze({ - __proto__: Metrics.Measurement.prototype, - - name: "submissions", - version: 2, - - fields: { - firstDocumentUploadAttempt: DAILY_COUNTER_FIELD, - continuationUploadAttempt: DAILY_COUNTER_FIELD, - uploadSuccess: DAILY_COUNTER_FIELD, - uploadTransportFailure: DAILY_COUNTER_FIELD, - uploadServerFailure: DAILY_COUNTER_FIELD, - uploadClientFailure: DAILY_COUNTER_FIELD, - uploadAlreadyInProgress: DAILY_COUNTER_FIELD, - }, -}); - -this.HealthReportProvider = function () { - Metrics.Provider.call(this); -} - -HealthReportProvider.prototype = Object.freeze({ - __proto__: Metrics.Provider.prototype, - - name: "org.mozilla.healthreport", - - measurementTypes: [ - HealthReportSubmissionMeasurement1, - HealthReportSubmissionMeasurement2, - ], - - recordEvent: function (event, date=new Date()) { - let m = this.getMeasurement("submissions", 2); - return this.enqueueStorageOperation(function recordCounter() { - return m.incrementDailyCounter(event, date); - }); - }, -}); diff --git a/services/healthreport/tests/xpcshell/head.js b/services/healthreport/tests/xpcshell/head.js deleted file mode 100644 index 9bb336e0b4..0000000000 --- a/services/healthreport/tests/xpcshell/head.js +++ /dev/null @@ -1,21 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -"use strict"; - -// We need to initialize the profile or OS.File may not work. See bug 810543. -do_get_profile(); - -(function initMetricsTestingInfrastructure() { - let ns = {}; - Components.utils.import("resource://testing-common/services/common/logging.js", - ns); - - ns.initTestLogging(); -}).call(this); - -(function createAppInfo() { - let ns = {}; - Components.utils.import("resource://testing-common/services/healthreport/utils.jsm", ns); - ns.updateAppInfo(); -}).call(this); diff --git a/services/healthreport/tests/xpcshell/test_healthreporter.js b/services/healthreport/tests/xpcshell/test_healthreporter.js deleted file mode 100644 index 8057639030..0000000000 --- a/services/healthreport/tests/xpcshell/test_healthreporter.js +++ /dev/null @@ -1,1284 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -"use strict"; - -var {classes: Cc, interfaces: Ci, utils: Cu} = Components; - -Cu.import("resource://services-common/observers.js"); -Cu.import("resource://services-common/utils.js"); -Cu.import("resource://gre/modules/Promise.jsm"); -Cu.import("resource://gre/modules/Metrics.jsm"); -Cu.import("resource://gre/modules/osfile.jsm"); -Cu.import("resource://gre/modules/Preferences.jsm"); -var bsp = Cu.import("resource://gre/modules/services/healthreport/healthreporter.jsm"); -Cu.import("resource://gre/modules/services/healthreport/providers.jsm"); -Cu.import("resource://gre/modules/services/datareporting/policy.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://gre/modules/Task.jsm"); -Cu.import("resource://testing-common/httpd.js"); -Cu.import("resource://testing-common/services/common/bagheeraserver.js"); -Cu.import("resource://testing-common/services/metrics/mocks.jsm"); -Cu.import("resource://testing-common/services/healthreport/utils.jsm"); -Cu.import("resource://testing-common/AppData.jsm"); -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); - -XPCOMUtils.defineLazyGetter(this, "gDatareportingService", - () => Cc["@mozilla.org/datareporting/service;1"] - .getService(Ci.nsISupports) - .wrappedJSObject); - - -const DUMMY_URI = "http://localhost:62013/"; -const MILLISECONDS_PER_DAY = 24 * 60 * 60 * 1000; - -const HealthReporterState = bsp.HealthReporterState; - - -function defineNow(policy, now) { - print("Adjusting fake system clock to " + now); - Object.defineProperty(policy, "now", { - value: function customNow() { - return now; - }, - writable: true, - }); -} - -function getReporter(name, uri, inspected) { - return Task.spawn(function init() { - let reporter = getHealthReporter(name, uri, inspected); - yield reporter.init(); - - yield reporter._providerManager.registerProviderFromType( - HealthReportProvider); - - throw new Task.Result(reporter); - }); -} - -function getReporterAndServer(name, namespace="test") { - return Task.spawn(function get() { - let server = new BagheeraServer(); - server.createNamespace(namespace); - server.start(); - - let reporter = yield getReporter(name, server.serverURI); - reporter.serverNamespace = namespace; - - throw new Task.Result([reporter, server]); - }); -} - -function shutdownServer(server) { - let deferred = Promise.defer(); - server.stop(deferred.resolve.bind(deferred)); - - return deferred.promise; -} - -function getHealthReportProviderValues(reporter, day=null) { - return Task.spawn(function getValues() { - let p = reporter.getProvider("org.mozilla.healthreport"); - do_check_neq(p, null); - let m = p.getMeasurement("submissions", 2); - do_check_neq(m, null); - - let data = yield reporter._storage.getMeasurementValues(m.id); - if (!day) { - throw new Task.Result(data); - } - - do_check_true(data.days.hasDay(day)); - let serializer = m.serializer(m.SERIALIZE_JSON) - let json = serializer.daily(data.days.getDay(day)); - do_check_eq(json._v, 2); - - throw new Task.Result(json); - }); -} - -/* - * Ensure that the notification has been displayed to the user therefore having - * reporter._policy.userNotifiedOfCurrentPolicy === true, which will allow for a - * successful data upload. - * @param {HealthReporter} reporter - * @return {Promise} - */ -function ensureUserNotified (reporter) { - return Task.spawn(function* ensureUserNotified () { - reporter._policy.ensureUserNotified(); - yield reporter._policy._listener.lastNotifyRequest.deferred.promise; - do_check_true(reporter._policy.userNotifiedOfCurrentPolicy); - }); -} - -function run_test() { - do_get_profile(); - - // Send the needed startup notifications to the datareporting service - // to ensure that it has been initialized. - gDatareportingService.observe(null, "app-startup", null); - gDatareportingService.observe(null, "profile-after-change", null); - - run_next_test(); -} - -// run_test() needs to finish synchronously, so we do async init here. -add_task(function test_init() { - yield makeFakeAppDir(); -}); - -add_task(function test_constructor() { - let reporter = yield getReporter("constructor"); - try { - do_check_eq(reporter.lastPingDate.getTime(), 0); - do_check_null(reporter.lastSubmitID); - do_check_eq(typeof(reporter._state), "object"); - do_check_eq(reporter._state.lastPingDate.getTime(), 0); - do_check_eq(reporter._state.remoteIDs.length, 0); - do_check_eq(reporter._state.clientIDVersion, 1); - do_check_neq(reporter._state.clientID, null); - - let failed = false; - try { - new HealthReporter("foo.bar"); - } catch (ex) { - failed = true; - do_check_true(ex.message.startsWith("Branch must end")); - } finally { - do_check_true(failed); - failed = false; - } - } finally { - yield reporter._shutdown(); - } -}); - -add_task(function test_shutdown_normal() { - let reporter = yield getReporter("shutdown_normal"); - - // We can't send "quit-application" notification because the xpcshell runner - // will shut down! - reporter._initiateShutdown(); - yield reporter._promiseShutdown; -}); - -add_task(function test_shutdown_storage_in_progress() { - let reporter = yield getHealthReporter("shutdown_storage_in_progress", DUMMY_URI, true); - - reporter.onStorageCreated = function () { - print("Faking shutdown during storage initialization."); - reporter._initiateShutdown(); - }; - - reporter.init(); - - yield reporter._promiseShutdown; - do_check_eq(reporter.providerManagerShutdownCount, 0); - do_check_eq(reporter.storageCloseCount, 1); -}); - -// Ensure that a shutdown triggered while provider manager is initializing -// results in shutdown and storage closure. -add_task(function test_shutdown_provider_manager_in_progress() { - let reporter = yield getHealthReporter("shutdown_provider_manager_in_progress", - DUMMY_URI, true); - - reporter.onProviderManagerInitialized = function () { - print("Faking shutdown during provider manager initialization."); - reporter._initiateShutdown(); - }; - - reporter.init(); - - // This will hang if shutdown logic is busted. - yield reporter._promiseShutdown; - do_check_eq(reporter.providerManagerShutdownCount, 1); - do_check_eq(reporter.storageCloseCount, 1); -}); - -// Simulates an error during provider manager initialization and verifies we shut down. -add_task(function test_shutdown_when_provider_manager_errors() { - let reporter = yield getHealthReporter("shutdown_when_provider_manager_errors", - DUMMY_URI, true); - - let error = new Error("Fake error during provider manager initialization."); - reporter.onInitializeProviderManagerFinished = function () { - print("Throwing fake error."); - throw error; - }; - - try { - yield reporter.init(); - do_throw("The error was not reported by init()"); - } catch (ex if ex == error) { - do_print("The error was reported by init()"); - } - - // This will hang if shutdown logic is busted. - yield reporter._promiseShutdown; - do_check_eq(reporter.providerManagerShutdownCount, 1); - do_check_eq(reporter.storageCloseCount, 1); -}); - -// Pull-only providers are only initialized at collect time. -add_task(function test_pull_only_providers() { - const category = "healthreporter-constant-only"; - - let cm = Cc["@mozilla.org/categorymanager;1"] - .getService(Ci.nsICategoryManager); - cm.addCategoryEntry(category, "DummyProvider", - "resource://testing-common/services/metrics/mocks.jsm", - false, true); - cm.addCategoryEntry(category, "DummyConstantProvider", - "resource://testing-common/services/metrics/mocks.jsm", - false, true); - - let reporter = yield getReporter("constant_only_providers"); - try { - let initCount = reporter._providerManager.providers.length; - yield reporter._providerManager.registerProvidersFromCategoryManager(category); - do_check_eq(reporter._providerManager._providers.size, initCount + 1); - do_check_true(reporter._storage.hasProvider("DummyProvider")); - do_check_false(reporter._storage.hasProvider("DummyConstantProvider")); - do_check_neq(reporter.getProvider("DummyProvider"), null); - do_check_null(reporter.getProvider("DummyConstantProvider")); - - yield reporter.collectMeasurements(); - - do_check_eq(reporter._providerManager._providers.size, initCount + 1); - do_check_true(reporter._storage.hasProvider("DummyConstantProvider")); - - let mID = reporter._storage.measurementID("DummyConstantProvider", "DummyMeasurement", 1); - let values = yield reporter._storage.getMeasurementValues(mID); - do_check_true(values.singular.size > 0); - } finally { - yield reporter._shutdown(); - } -}); - -add_task(function test_collect_daily() { - let reporter = yield getReporter("collect_daily"); - - try { - let now = new Date(); - let provider = new DummyProvider(); - yield reporter._providerManager.registerProvider(provider); - yield reporter.collectMeasurements(); - - do_check_eq(provider.collectConstantCount, 1); - do_check_eq(provider.collectDailyCount, 1); - - yield reporter.collectMeasurements(); - do_check_eq(provider.collectConstantCount, 1); - do_check_eq(provider.collectDailyCount, 1); - - yield reporter.collectMeasurements(); - do_check_eq(provider.collectDailyCount, 1); // Too soon. - - reporter._lastDailyDate = now.getTime() - MILLISECONDS_PER_DAY - 1; - yield reporter.collectMeasurements(); - do_check_eq(provider.collectDailyCount, 2); - - reporter._lastDailyDate = null; - yield reporter.collectMeasurements(); - do_check_eq(provider.collectDailyCount, 3); - } finally { - yield reporter._shutdown(); - } -}); - -add_task(function test_remove_old_lastpayload() { - let reporter = getHealthReporter("remove-old-lastpayload"); - let lastPayloadPath = reporter._state._lastPayloadPath; - let paths = [lastPayloadPath, lastPayloadPath + ".tmp"]; - let createFiles = function () { - return Task.spawn(function createFiles() { - for (let path of paths) { - yield OS.File.writeAtomic(path, "delete-me", {tmpPath: path + ".tmp"}); - do_check_true(yield OS.File.exists(path)); - } - }); - }; - try { - do_check_true(!reporter._state.removedOutdatedLastpayload); - yield createFiles(); - yield reporter.init(); - for (let path of paths) { - do_check_false(yield OS.File.exists(path)); - } - yield reporter._state.save(); - yield reporter._shutdown(); - - let o = yield CommonUtils.readJSON(reporter._state._filename); - do_check_true(o.removedOutdatedLastpayload); - - yield createFiles(); - reporter = getHealthReporter("remove-old-lastpayload"); - yield reporter.init(); - for (let path of paths) { - do_check_true(yield OS.File.exists(path)); - } - } finally { - yield reporter._shutdown(); - } -}); - -add_task(function test_json_payload_simple() { - let reporter = yield getReporter("json_payload_simple"); - - let clientID = reporter._state.clientID; - do_check_neq(clientID, null); - - try { - let now = new Date(); - let payload = yield reporter.getJSONPayload(); - do_check_eq(typeof payload, "string"); - let original = JSON.parse(payload); - - do_check_eq(original.version, 2); - do_check_eq(original.thisPingDate, reporter._formatDate(now)); - do_check_eq(original.clientID, clientID); - do_check_eq(original.clientIDVersion, reporter._state.clientIDVersion); - do_check_eq(original.clientIDVersion, 1); - do_check_eq(Object.keys(original.data.last).length, 0); - do_check_eq(Object.keys(original.data.days).length, 0); - do_check_false("notInitialized" in original); - - yield reporter._state.setLastPingDate( - new Date(now.getTime() - 24 * 60 * 60 * 1000 - 10)); - - original = JSON.parse(yield reporter.getJSONPayload()); - do_check_eq(original.lastPingDate, reporter._formatDate(reporter.lastPingDate)); - do_check_eq(original.clientID, clientID); - - // This could fail if we cross UTC day boundaries at the exact instance the - // test is executed. Let's tempt fate. - do_check_eq(original.thisPingDate, reporter._formatDate(now)); - - payload = yield reporter.getJSONPayload(true); - do_check_eq(typeof payload, "object"); - } finally { - yield reporter._shutdown(); - } -}); - -add_task(function test_json_payload_dummy_provider() { - let reporter = yield getReporter("json_payload_dummy_provider"); - - try { - yield reporter._providerManager.registerProvider(new DummyProvider()); - yield reporter.collectMeasurements(); - let payload = yield reporter.getJSONPayload(); - print(payload); - let o = JSON.parse(payload); - - let name = "DummyProvider.DummyMeasurement"; - do_check_eq(Object.keys(o.data.last).length, 1); - do_check_true(name in o.data.last); - do_check_eq(o.data.last[name]._v, 1); - } finally { - yield reporter._shutdown(); - } -}); - -add_task(function test_collect_and_obtain_json_payload() { - let reporter = yield getReporter("collect_and_obtain_json_payload"); - - try { - yield reporter._providerManager.registerProvider(new DummyProvider()); - let payload = yield reporter.collectAndObtainJSONPayload(); - do_check_eq(typeof payload, "string"); - - let o = JSON.parse(payload); - do_check_true("DummyProvider.DummyMeasurement" in o.data.last); - - payload = yield reporter.collectAndObtainJSONPayload(true); - do_check_eq(typeof payload, "object"); - } finally { - yield reporter._shutdown(); - } -}); - -// Ensure constant-only providers make their way into the JSON payload. -add_task(function test_constant_only_providers_in_json_payload() { - const category = "healthreporter-constant-only-in-payload"; - - let cm = Cc["@mozilla.org/categorymanager;1"] - .getService(Ci.nsICategoryManager); - cm.addCategoryEntry(category, "DummyProvider", - "resource://testing-common/services/metrics/mocks.jsm", - false, true); - cm.addCategoryEntry(category, "DummyConstantProvider", - "resource://testing-common/services/metrics/mocks.jsm", - false, true); - - let reporter = yield getReporter("constant_only_providers_in_json_payload"); - try { - let initCount = reporter._providerManager.providers.length; - yield reporter._providerManager.registerProvidersFromCategoryManager(category); - - let payload = yield reporter.collectAndObtainJSONPayload(); - let o = JSON.parse(payload); - do_check_true("DummyProvider.DummyMeasurement" in o.data.last); - do_check_true("DummyConstantProvider.DummyMeasurement" in o.data.last); - - let providers = reporter._providerManager.providers; - do_check_eq(providers.length, initCount + 1); - - // Do it again for good measure. - payload = yield reporter.collectAndObtainJSONPayload(); - o = JSON.parse(payload); - do_check_true("DummyProvider.DummyMeasurement" in o.data.last); - do_check_true("DummyConstantProvider.DummyMeasurement" in o.data.last); - - providers = reporter._providerManager.providers; - do_check_eq(providers.length, initCount + 1); - - // Ensure throwing getJSONPayload is handled properly. - Object.defineProperty(reporter, "_getJSONPayload", { - value: function () { - throw new Error("Silly error."); - }, - }); - - let deferred = Promise.defer(); - - reporter.collectAndObtainJSONPayload().then(do_throw, function onError() { - providers = reporter._providerManager.providers; - do_check_eq(providers.length, initCount + 1); - deferred.resolve(); - }); - - yield deferred.promise; - } finally { - yield reporter._shutdown(); - } -}); - -add_task(function test_json_payload_multiple_days() { - let reporter = yield getReporter("json_payload_multiple_days"); - - try { - let provider = new DummyProvider(); - yield reporter._providerManager.registerProvider(provider); - - let now = new Date(); - - let m = provider.getMeasurement("DummyMeasurement", 1); - for (let i = 0; i < 200; i++) { - let date = new Date(now.getTime() - i * MILLISECONDS_PER_DAY); - yield m.incrementDailyCounter("daily-counter", date); - } - - // This test could fail if we cross a UTC day boundary when running. So, - // we ensure this doesn't occur. - Object.defineProperty(reporter, "_now", { - value: function () { - return now; - }, - }); - - let payload = yield reporter.getJSONPayload(); - print(payload); - let o = JSON.parse(payload); - - do_check_eq(Object.keys(o.data.days).length, 180); - let today = reporter._formatDate(now); - do_check_true(today in o.data.days); - } finally { - yield reporter._shutdown(); - } -}); - -add_task(function test_json_payload_newer_version_overwrites() { - let reporter = yield getReporter("json_payload_newer_version_overwrites"); - - try { - let now = new Date(); - // Instead of hacking up the internals to ensure consistent order in Map - // iteration (which would be difficult), we instead opt to generate a lot - // of measurements of different versions and verify their iterable order - // is not increasing. - let versions = [1, 6, 3, 9, 2, 3, 7, 4, 10, 8]; - let protos = []; - for (let version of versions) { - let m = function () { - Metrics.Measurement.call(this); - }; - m.prototype = { - __proto__: DummyMeasurement.prototype, - name: "DummyMeasurement", - version: version, - }; - - protos.push(m); - } - - let ctor = function () { - Metrics.Provider.call(this); - }; - ctor.prototype = { - __proto__: DummyProvider.prototype, - - name: "MultiMeasurementProvider", - measurementTypes: protos, - }; - - let provider = new ctor(); - - yield reporter._providerManager.registerProvider(provider); - - let haveUnordered = false; - let last = -1; - let highestVersion = -1; - for (let [key, measurement] of provider.measurements) { - yield measurement.setDailyLastNumeric("daily-last-numeric", - measurement.version, now); - yield measurement.setLastNumeric("last-numeric", - measurement.version, now); - - if (measurement.version > highestVersion) { - highestVersion = measurement.version; - } - - if (measurement.version < last) { - haveUnordered = true; - } - - last = measurement.version; - } - - // Ensure Map traversal isn't ordered. If this ever fails, then we'll need - // to monkeypatch. - do_check_true(haveUnordered); - - let payload = yield reporter.getJSONPayload(); - let o = JSON.parse(payload); - do_check_true("MultiMeasurementProvider.DummyMeasurement" in o.data.last); - do_check_eq(o.data.last["MultiMeasurementProvider.DummyMeasurement"]._v, highestVersion); - - let day = reporter._formatDate(now); - do_check_true(day in o.data.days); - do_check_true("MultiMeasurementProvider.DummyMeasurement" in o.data.days[day]); - do_check_eq(o.data.days[day]["MultiMeasurementProvider.DummyMeasurement"]._v, highestVersion); - - } finally { - yield reporter._shutdown(); - } -}); - -add_task(function test_idle_daily() { - let reporter = yield getReporter("idle_daily"); - try { - let provider = new DummyProvider(); - yield reporter._providerManager.registerProvider(provider); - - let now = new Date(); - let m = provider.getMeasurement("DummyMeasurement", 1); - for (let i = 0; i < 200; i++) { - let date = new Date(now.getTime() - i * MILLISECONDS_PER_DAY); - yield m.incrementDailyCounter("daily-counter", date); - } - - let values = yield m.getValues(); - do_check_eq(values.days.size, 200); - - Services.obs.notifyObservers(null, "idle-daily", null); - - values = yield m.getValues(); - do_check_eq(values.days.size, 180); - } finally { - yield reporter._shutdown(); - } -}); - -add_task(function test_data_submission_transport_failure() { - let reporter = yield getReporter("data_submission_transport_failure"); - try { - reporter.serverURI = DUMMY_URI; - reporter.serverNamespace = "test00"; - - let deferred = Promise.defer(); - let request = new DataSubmissionRequest(deferred, new Date(Date.now + 30000)); - reporter.requestDataUpload(request); - - yield deferred.promise; - do_check_eq(request.state, request.SUBMISSION_FAILURE_SOFT); - - let data = yield getHealthReportProviderValues(reporter, new Date()); - do_check_eq(data.firstDocumentUploadAttempt, 1); - do_check_eq(data.uploadTransportFailure, 1); - do_check_eq(Object.keys(data).length, 3); - } finally { - reporter._shutdown(); - } -}); - -add_task(function test_data_submission_server_failure() { - let [reporter, server] = yield getReporterAndServer("data_submission_server_failure"); - try { - Object.defineProperty(server, "_handleNamespaceSubmitPost", { - value: function (ns, id, request, response) { - throw HTTP_500; - }, - writable: true, - }); - - let deferred = Promise.defer(); - let now = new Date(); - let request = new DataSubmissionRequest(deferred, now); - reporter.requestDataUpload(request); - yield deferred.promise; - do_check_eq(request.state, request.SUBMISSION_FAILURE_HARD); - - let data = yield getHealthReportProviderValues(reporter, now); - do_check_eq(data.firstDocumentUploadAttempt, 1); - do_check_eq(data.uploadServerFailure, 1); - do_check_eq(Object.keys(data).length, 3); - } finally { - yield shutdownServer(server); - reporter._shutdown(); - } -}); - -add_task(function test_data_submission_success() { - let [reporter, server] = yield getReporterAndServer("data_submission_success"); - try { - yield reporter._providerManager.registerProviderFromType(DummyProvider); - yield reporter._providerManager.registerProviderFromType(DummyConstantProvider); - - do_check_eq(reporter.lastPingDate.getTime(), 0); - do_check_false(reporter.haveRemoteData()); - - let deferred = Promise.defer(); - - let now = new Date(); - let request = new DataSubmissionRequest(deferred, now); - reporter._state.addRemoteID("foo"); - reporter.requestDataUpload(request); - yield deferred.promise; - do_check_eq(request.state, request.SUBMISSION_SUCCESS); - do_check_true(reporter.lastPingDate.getTime() > 0); - do_check_true(reporter.haveRemoteData()); - for (let remoteID of reporter._state.remoteIDs) { - do_check_neq(remoteID, "foo"); - } - - // Ensure data from providers made it to payload. - let o = yield reporter.getJSONPayload(true); - do_check_true("DummyProvider.DummyMeasurement" in o.data.last); - do_check_true("DummyConstantProvider.DummyMeasurement" in o.data.last); - - let data = yield getHealthReportProviderValues(reporter, now); - do_check_eq(data.continuationUploadAttempt, 1); - do_check_eq(data.uploadSuccess, 1); - do_check_eq(Object.keys(data).length, 3); - - let d = reporter.lastPingDate; - let id = reporter.lastSubmitID; - let clientID = reporter._state.clientID; - - yield reporter._shutdown(); - - // Ensure reloading state works. - reporter = yield getReporter("data_submission_success"); - do_check_eq(reporter.lastSubmitID, id); - do_check_eq(reporter.lastPingDate.getTime(), d.getTime()); - do_check_eq(reporter._state.clientID, clientID); - - yield reporter._shutdown(); - } finally { - yield shutdownServer(server); - } -}); - -add_task(function test_recurring_daily_pings() { - let [reporter, server] = yield getReporterAndServer("recurring_daily_pings"); - try { - reporter._providerManager.registerProvider(new DummyProvider()); - - let policy = reporter._policy; - - defineNow(policy, policy.nextDataSubmissionDate); - yield ensureUserNotified(reporter); - let promise = policy.checkStateAndTrigger(); - do_check_neq(promise, null); - yield promise; - - let lastID = reporter.lastSubmitID; - do_check_neq(lastID, null); - do_check_true(server.hasDocument(reporter.serverNamespace, lastID)); - - // Skip forward to next scheduled submission time. - defineNow(policy, policy.nextDataSubmissionDate); - promise = policy.checkStateAndTrigger(); - do_check_neq(promise, null); - yield promise; - do_check_neq(reporter.lastSubmitID, lastID); - do_check_true(server.hasDocument(reporter.serverNamespace, reporter.lastSubmitID)); - do_check_false(server.hasDocument(reporter.serverNamespace, lastID)); - - // now() on the health reporter instance wasn't munged. So, we should see - // both requests attributed to the same day. - let data = yield getHealthReportProviderValues(reporter, new Date()); - do_check_eq(data.firstDocumentUploadAttempt, 1); - do_check_eq(data.continuationUploadAttempt, 1); - do_check_eq(data.uploadSuccess, 2); - do_check_eq(Object.keys(data).length, 4); - } finally { - reporter._shutdown(); - yield shutdownServer(server); - } -}); - -add_task(function test_request_remote_data_deletion() { - let [reporter, server] = yield getReporterAndServer("request_remote_data_deletion"); - - try { - let policy = reporter._policy; - defineNow(policy, policy._futureDate(-24 * 60 * 60 * 1000)); - defineNow(policy, policy.nextDataSubmissionDate); - yield ensureUserNotified(reporter); - yield policy.checkStateAndTrigger(); - let id = reporter.lastSubmitID; - do_check_neq(id, null); - do_check_true(server.hasDocument(reporter.serverNamespace, id)); - - let clientID = reporter._state.clientID; - do_check_neq(clientID, null); - - defineNow(policy, policy._futureDate(10 * 1000)); - - let promise = reporter.requestDeleteRemoteData(); - do_check_neq(promise, null); - yield promise; - do_check_null(reporter.lastSubmitID); - do_check_false(reporter.haveRemoteData()); - do_check_false(server.hasDocument(reporter.serverNamespace, id)); - - // Client ID should be updated. - do_check_neq(reporter._state.clientID, null); - do_check_neq(reporter._state.clientID, clientID); - do_check_eq(reporter._state.clientIDVersion, 1); - - // And it should be persisted to disk. - let o = yield CommonUtils.readJSON(reporter._state._filename); - do_check_eq(o.clientID, reporter._state.clientID); - do_check_eq(o.clientIDVersion, 1); - } finally { - reporter._shutdown(); - yield shutdownServer(server); - } -}); - -add_task(function test_multiple_simultaneous_uploads() { - let [reporter, server] = yield getReporterAndServer("multiple_simultaneous_uploads"); - - try { - let d1 = Promise.defer(); - let d2 = Promise.defer(); - let t1 = new Date(Date.now() - 1000); - let t2 = new Date(t1.getTime() + 500); - let r1 = new DataSubmissionRequest(d1, t1); - let r2 = new DataSubmissionRequest(d2, t2); - - let getPayloadDeferred = Promise.defer(); - - Object.defineProperty(reporter, "getJSONPayload", { - configurable: true, - value: () => { - getPayloadDeferred.resolve(); - delete reporter["getJSONPayload"]; - return reporter.getJSONPayload(); - }, - }); - - let p1 = reporter.requestDataUpload(r1); - yield getPayloadDeferred.promise; - do_check_true(reporter._uploadInProgress); - let p2 = reporter.requestDataUpload(r2); - - yield p1; - yield p2; - - do_check_eq(r1.state, r1.SUBMISSION_SUCCESS); - do_check_eq(r2.state, r2.UPLOAD_IN_PROGRESS); - - // They should both be resolved already. - yield d1; - yield d2; - - let data = yield getHealthReportProviderValues(reporter, t1); - do_check_eq(data.firstDocumentUploadAttempt, 1); - do_check_false("continuationUploadAttempt" in data); - do_check_eq(data.uploadSuccess, 1); - do_check_eq(data.uploadAlreadyInProgress, 1); - } finally { - reporter._shutdown(); - yield shutdownServer(server); - } -}); - -add_task(function test_policy_accept_reject() { - let [reporter, server] = yield getReporterAndServer("policy_accept_reject"); - - try { - let policy = reporter._policy; - - do_check_eq(policy.dataSubmissionPolicyNotifiedDate.getTime(), 0); - do_check_true(policy.dataSubmissionPolicyAcceptedVersion < DATAREPORTING_POLICY_VERSION); - do_check_false(reporter.willUploadData); - - yield ensureUserNotified(reporter); - do_check_true(reporter.willUploadData); - } finally { - yield reporter._shutdown(); - yield shutdownServer(server); - } -}); - -add_task(function test_error_message_scrubbing() { - let reporter = yield getReporter("error_message_scrubbing"); - - try { - let profile = Services.dirsvc.get("ProfD", Ci.nsIFile).path; - reporter._recordError("Foo " + profile); - - do_check_eq(reporter._errors.length, 1); - do_check_eq(reporter._errors[0], "Foo "); - - reporter._errors = []; - - let appdata = Services.dirsvc.get("UAppData", Ci.nsIFile); - let uri = Services.io.newFileURI(appdata); - - reporter._recordError("Foo " + uri.spec); - do_check_eq(reporter._errors[0], "Foo "); - } finally { - yield reporter._shutdown(); - } -}); - -add_task(function test_basic_appinfo() { - function verify(d) { - do_check_eq(d["_v"], 1); - do_check_eq(d._v, 1); - do_check_eq(d.vendor, "Mozilla"); - do_check_eq(d.name, "xpcshell"); - do_check_eq(d.id, "xpcshell@tests.mozilla.org"); - do_check_eq(d.version, "1"); - do_check_eq(d.appBuildID, "20121107"); - do_check_eq(d.platformVersion, "p-ver"); - do_check_eq(d.platformBuildID, "20121106"); - do_check_eq(d.os, "XPCShell"); - do_check_eq(d.xpcomabi, "noarch-spidermonkey"); - do_check_true("updateChannel" in d); - } - let reporter = yield getReporter("basic_appinfo"); - try { - verify(reporter.obtainAppInfo()); - let payload = yield reporter.collectAndObtainJSONPayload(true); - do_check_eq(payload["version"], 2); - verify(payload["geckoAppInfo"]); - } finally { - yield reporter._shutdown(); - } -}); - -// Ensure collection occurs if upload is disabled. -add_task(function test_collect_when_upload_disabled() { - let reporter = getHealthReporter("collect_when_upload_disabled"); - reporter._policy.recordHealthReportUploadEnabled(false, "testing-collect"); - do_check_false(reporter._policy.healthReportUploadEnabled); - - let name = "healthreport-testing-collect_when_upload_disabled-healthreport-lastDailyCollection"; - let pref = "app.update.lastUpdateTime." + name; - do_check_false(Services.prefs.prefHasUserValue(pref)); - - try { - yield reporter.init(); - do_check_true(Services.prefs.prefHasUserValue(pref)); - - // We would ideally ensure the timer fires and does the right thing. - // However, testing the update timer manager is quite involved. - } finally { - yield reporter._shutdown(); - } -}); - -add_task(function test_failure_if_not_initialized() { - let reporter = yield getReporter("failure_if_not_initialized"); - yield reporter._shutdown(); - - let error = false; - try { - yield reporter.requestDataUpload(); - } catch (ex) { - error = true; - do_check_true(ex.message.includes("Not initialized.")); - } finally { - do_check_true(error); - error = false; - } - - try { - yield reporter.collectMeasurements(); - } catch (ex) { - error = true; - do_check_true(ex.message.includes("Not initialized.")); - } finally { - do_check_true(error); - error = false; - } - - // getJSONPayload always works (to facilitate error upload). - yield reporter.getJSONPayload(); -}); - -add_task(function test_upload_on_init_failure() { - let MESSAGE = "Fake error during provider manager initialization." + Math.random(); - - let server = new BagheeraServer(); - server.start(); - let reporter = yield getHealthReporter("upload_on_init_failure", server.serverURI, true); - server.createNamespace(reporter.serverNamespace); - - reporter.onInitializeProviderManagerFinished = function () { - throw new Error(MESSAGE); - }; - - let deferred = Promise.defer(); - - let oldOnResult = reporter._onBagheeraResult; - Object.defineProperty(reporter, "_onBagheeraResult", { - value: function (request, isDelete, date, result) { - do_check_false(isDelete); - do_check_true(result.transportSuccess); - do_check_true(result.serverSuccess); - - oldOnResult.call(reporter, request, isDelete, new Date(), result); - deferred.resolve(); - }, - }); - - let error = false; - try { - yield ensureUserNotified(reporter); - yield reporter.init(); - } catch (ex) { - error = true; - } finally { - do_check_true(error); - } - - // At this point the emergency upload should have been initiated. We - // wait for our monkeypatched response handler to signal request - // completion. - yield deferred.promise; - - do_check_true(server.hasDocument(reporter.serverNamespace, reporter.lastSubmitID)); - let doc = server.getDocument(reporter.serverNamespace, reporter.lastSubmitID); - do_check_true("notInitialized" in doc); - do_check_eq(doc.notInitialized, 1); - do_check_true("errors" in doc); - do_check_eq(doc.errors.length, 1); - do_check_true(doc.errors[0].includes(MESSAGE)); - - yield reporter._shutdown(); - yield shutdownServer(server); -}); - -// Simulate a SQLite write error during upload. -add_task(function* test_upload_with_provider_record_failure() { - let [reporter, server] = yield getReporterAndServer("upload_with_provider_record_failure"); - try { - // Poison the FHR provider to simulate database error or some other - // catastrophic failure. We do this because this provider is written to - // during upload and failure to catch errors would result in upload not - // occurring. - let provider = reporter.getProvider("org.mozilla.healthreport"); - - let wrappedProto = { - __proto__: HealthReportProvider.prototype, - - recordEvent: function (event, date=new Date()) { - this._log.warn("Simulating error during write"); - throw new Error("Fake error during recordEvent."); - }, - }; - provider.__proto__ = wrappedProto; - - let deferred = Promise.defer(); - let now = new Date(); - let request = new DataSubmissionRequest(deferred, now); - reporter._state.addRemoteID("foo"); - reporter.requestDataUpload(request); - yield deferred.promise; - do_check_eq(request.state, request.SUBMISSION_SUCCESS); - } finally { - yield reporter._shutdown(); - yield shutdownServer(server); - } -}); - -add_task(function test_state_prefs_conversion_simple() { - let reporter = getHealthReporter("state_prefs_conversion"); - let prefs = reporter._prefs; - - let lastSubmit = new Date(); - prefs.set("lastSubmitID", "lastID"); - CommonUtils.setDatePref(prefs, "lastPingTime", lastSubmit); - - try { - yield reporter.init(); - - do_check_eq(reporter._state.lastSubmitID, "lastID"); - do_check_eq(reporter._state.remoteIDs.length, 1); - do_check_eq(reporter._state.lastPingDate.getTime(), lastSubmit.getTime()); - do_check_eq(reporter._state.lastPingDate.getTime(), reporter.lastPingDate.getTime()); - do_check_eq(reporter._state.lastSubmitID, reporter.lastSubmitID); - do_check_true(reporter.haveRemoteData()); - - // User set preferences should have been wiped out. - do_check_false(prefs.isSet("lastSubmitID")); - do_check_false(prefs.isSet("lastPingTime")); - } finally { - yield reporter._shutdown(); - } -}); - -// If the saved JSON file does not contain an object, we should reset -// automatically. -add_task(function test_state_no_json_object() { - let reporter = getHealthReporter("state_shared"); - yield CommonUtils.writeJSON("hello", reporter._state._filename); - - try { - yield reporter.init(); - - do_check_eq(reporter.lastPingDate.getTime(), 0); - do_check_null(reporter.lastSubmitID); - - let o = yield CommonUtils.readJSON(reporter._state._filename); - do_check_eq(typeof(o), "object"); - do_check_eq(o.v, 1); - do_check_eq(o.lastPingTime, 0); - do_check_eq(o.remoteIDs.length, 0); - } finally { - yield reporter._shutdown(); - } -}); - -// If we encounter a future version, we reset state to the current version. -add_task(function test_state_future_version() { - let reporter = getHealthReporter("state_shared"); - yield CommonUtils.writeJSON({v: 2, remoteIDs: ["foo"], lastPingTime: 2412}, - reporter._state._filename); - try { - yield reporter.init(); - - do_check_eq(reporter.lastPingDate.getTime(), 0); - do_check_null(reporter.lastSubmitID); - - // While the object is updated, we don't save the file. - let o = yield CommonUtils.readJSON(reporter._state._filename); - do_check_eq(o.v, 2); - do_check_eq(o.lastPingTime, 2412); - do_check_eq(o.remoteIDs.length, 1); - } finally { - yield reporter._shutdown(); - } -}); - -// Test recovery if the state file contains invalid JSON. -add_task(function test_state_invalid_json() { - let reporter = getHealthReporter("state_shared"); - - let encoder = new TextEncoder(); - let arr = encoder.encode("{foo: bad value, 'bad': as2,}"); - let path = reporter._state._filename; - yield OS.File.writeAtomic(path, arr, {tmpPath: path + ".tmp"}); - - try { - yield reporter.init(); - - do_check_eq(reporter.lastPingDate.getTime(), 0); - do_check_null(reporter.lastSubmitID); - } finally { - yield reporter._shutdown(); - } -}); - -add_task(function test_state_multiple_remote_ids() { - let [reporter, server] = yield getReporterAndServer("state_multiple_remote_ids"); - let documents = [ - [reporter.serverNamespace, "one", "{v:1}"], - [reporter.serverNamespace, "two", "{v:2}"], - ]; - let now = new Date(Date.now() - 5000); - - try { - for (let [ns, id, payload] of documents) { - server.setDocument(ns, id, payload); - do_check_true(server.hasDocument(ns, id)); - yield reporter._state.addRemoteID(id); - do_check_eq(reporter._state.remoteIDs.indexOf(id), reporter._state.remoteIDs.length - 1); - } - yield reporter._state.setLastPingDate(now); - do_check_eq(reporter._state.remoteIDs.length, 2); - do_check_eq(reporter.lastSubmitID, documents[0][1]); - - let deferred = Promise.defer(); - let request = new DataSubmissionRequest(deferred, now); - reporter.requestDataUpload(request); - yield deferred.promise; - - do_check_eq(reporter._state.remoteIDs.length, 1); - for (let [,id,] of documents) { - do_check_eq(reporter._state.remoteIDs.indexOf(id), -1); - do_check_false(server.hasDocument(reporter.serverNamespace, id)); - } - do_check_true(reporter.lastPingDate.getTime() > now.getTime()); - - let o = yield CommonUtils.readJSON(reporter._state._filename); - do_check_eq(o.remoteIDs.length, 1); - do_check_eq(o.remoteIDs[0], reporter._state.remoteIDs[0]); - do_check_eq(o.lastPingTime, reporter.lastPingDate.getTime()); - } finally { - yield shutdownServer(server); - yield reporter._shutdown(); - } -}); - -// If we have a state file then downgrade to prefs, the prefs should be -// reimported and should supplement existing state. -add_task(function test_state_downgrade_upgrade() { - let reporter = getHealthReporter("state_shared"); - - let now = new Date(); - - yield CommonUtils.writeJSON({v: 1, remoteIDs: ["id1", "id2"], lastPingTime: now.getTime()}, - reporter._state._filename); - - let prefs = reporter._prefs; - prefs.set("lastSubmitID", "prefID"); - prefs.set("lastPingTime", "" + (now.getTime() + 1000)); - - try { - yield reporter.init(); - - do_check_eq(reporter.lastSubmitID, "id1"); - do_check_eq(reporter._state.remoteIDs.length, 3); - do_check_eq(reporter._state.remoteIDs[2], "prefID"); - do_check_eq(reporter.lastPingDate.getTime(), now.getTime() + 1000); - do_check_false(prefs.isSet("lastSubmitID")); - do_check_false(prefs.isSet("lastPingTime")); - - let o = yield CommonUtils.readJSON(reporter._state._filename); - do_check_eq(o.remoteIDs.length, 3); - do_check_eq(o.lastPingTime, now.getTime() + 1000); - } finally { - yield reporter._shutdown(); - } -}); - -// Invalid stored client ID is reset automatically. -add_task(function* test_empty_client_id() { - let reporter = getHealthReporter("state_empty_client_id"); - - yield CommonUtils.writeJSON({ - v: 1, - clientID: "", - remoteIDs: ["id1", "id2"], - lastPingTime: Date.now(), - removeOutdatedLastPayload: true, - }, reporter._state._filename); - - try { - yield reporter.init(); - - do_check_neq(reporter._state.clientID, null); - do_check_eq(reporter._state.clientID.length, 36); - } finally { - reporter._shutdown(); - } -}); - -add_task(function* test_nonstring_client_id() { - let reporter = getHealthReporter("state_nonstring_client_id"); - - yield CommonUtils.writeJSON({ - v: 1, - clientID: 42, - remoteIDs: ["id1", "id2"], - lastPingTime: Date.now(), - remoteOutdatedLastPayload: true, - }, reporter._state._filename); - - try { - yield reporter.init(); - - do_check_neq(reporter._state.clientID, null); - do_check_eq(reporter._state.clientID.length, 36); - } finally { - reporter._shutdown(); - } -}); - -function UnicodeMeasurement() { - Metrics.Measurement.call(this); -} - -UnicodeMeasurement.prototype = { - __proto__: Metrics.Measurement.prototype, - - name: "unicode", - version: 1, - - fields: { - "last-text": {type: Metrics.Storage.FIELD_LAST_TEXT}, - }, -}; - -function UnicodeProvider() { - Metrics.Provider.call(this); -} - -UnicodeProvider.prototype = { - __proto__: Metrics.Provider.prototype, - - name: "unicode", - - measurementTypes: [UnicodeMeasurement], - - collectConstantData: function () { - return this.enqueueStorageOperation(() => { - let m = this.getMeasurement("unicode", 1); - return m.setLastText("last-text", "ᄃᄄᄅ"); - }); - }, -}; - -// Check for proper handling of Unicode in payload. -add_task(function* test_unicode_payload() { - let [reporter, server] = yield getReporterAndServer("unicode_payload"); - try { - yield reporter._providerManager.registerProviderFromType(UnicodeProvider); - - let deferred = Promise.defer(); - let request = new DataSubmissionRequest(deferred, new Date()); - reporter.requestDataUpload(request); - yield deferred.promise; - Assert.equal(request.state, request.SUBMISSION_SUCCESS); - Assert.ok(server.hasDocument(reporter.serverNamespace, reporter.lastSubmitID)); - - let p = server.getDocument(reporter.serverNamespace, reporter.lastSubmitID); - let v = p.data.last['unicode.unicode']['last-text']; - Assert.equal(v, "ᄃᄄᄅ"); - } finally { - yield shutdownServer(server); - reporter._shutdown(); - } -}); diff --git a/services/healthreport/tests/xpcshell/test_load_modules.js b/services/healthreport/tests/xpcshell/test_load_modules.js deleted file mode 100644 index dde9d4eb57..0000000000 --- a/services/healthreport/tests/xpcshell/test_load_modules.js +++ /dev/null @@ -1,20 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -"use strict"; - -const modules = [ - "healthreporter.jsm", - "profile.jsm", - "providers.jsm", -]; - -function run_test() { - for (let m of modules) { - let resource = "resource://gre/modules/services/healthreport/" + m; - Components.utils.import(resource, {}); - } - - Components.utils.import("resource://gre/modules/HealthReport.jsm", {}); -} - diff --git a/services/healthreport/tests/xpcshell/test_profile.js b/services/healthreport/tests/xpcshell/test_profile.js deleted file mode 100644 index c26f064c5c..0000000000 --- a/services/healthreport/tests/xpcshell/test_profile.js +++ /dev/null @@ -1,257 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -"use strict"; - -var {utils: Cu} = Components; - -const MILLISECONDS_PER_DAY = 24 * 60 * 60 * 1000; - -// Create profile directory before use. -// It can be no older than a day ago…. -var profile_creation_lower = Date.now() - MILLISECONDS_PER_DAY; -do_get_profile(); - -Cu.import("resource://gre/modules/Promise.jsm"); -Cu.import("resource://gre/modules/Metrics.jsm"); -Cu.import("resource://gre/modules/services/healthreport/profile.jsm"); -Cu.import("resource://gre/modules/Task.jsm"); - - -function MockProfileMetadataProvider(name="MockProfileMetadataProvider") { - this.name = name; - ProfileMetadataProvider.call(this); -} -MockProfileMetadataProvider.prototype = { - __proto__: ProfileMetadataProvider.prototype, - includeProfileReset: false, - - getProfileDays: function getProfileDays() { - let result = {profileCreation: 1234}; - if (this.includeProfileReset) { - result.profileReset = 5678; - } - return Promise.resolve(result); - }, -}; - - -function run_test() { - run_next_test(); -} - -/** - * Ensure that OS.File works in our environment. - * This test can go once there are xpcshell tests for OS.File. - */ -add_test(function use_os_file() { - Cu.import("resource://gre/modules/osfile.jsm") - - // Ensure that we get constants, too. - do_check_neq(OS.Constants.Path.profileDir, null); - - let iterator = new OS.File.DirectoryIterator("."); - iterator.forEach(function onEntry(entry) { - print("Got " + entry.path); - }).then(function onSuccess() { - iterator.close(); - print("Done."); - run_next_test(); - }, function onFail() { - iterator.close(); - do_throw("Iterating over current directory failed."); - }); -}); - -function getAccessor() { - let acc = new ProfileTimesAccessor(); - print("Profile is " + acc.profilePath); - return acc; -} - -add_test(function test_time_accessor_no_file() { - let acc = getAccessor(); - - // There should be no file yet. - acc.readTimes() - .then(function onSuccess(json) { - do_throw("File existed!"); - }, - function onFailure() { - run_next_test(); - }); -}); - -add_task(function test_time_accessor_named_file() { - let acc = getAccessor(); - - // There should be no file yet. - yield acc.writeTimes({created: 12345}, "test.json"); - let json = yield acc.readTimes("test.json") - print("Read: " + JSON.stringify(json)); - do_check_eq(12345, json.created); -}); - -add_task(function test_time_accessor_creates_file() { - let lower = profile_creation_lower; - - // Ensure that provided contents are merged, and existing - // files can be overwritten. These two things occur if we - // read and then decide that we have to write. - let acc = getAccessor(); - let existing = {abc: "123", easy: "abc"}; - let expected; - - let created = yield acc.computeAndPersistCreated(existing, "test2.json") - let upper = Date.now() + 1000; - print(lower + " < " + created + " <= " + upper); - do_check_true(lower < created); - do_check_true(upper >= created); - expected = created; - - let json = yield acc.readTimes("test2.json") - print("Read: " + JSON.stringify(json)); - do_check_eq("123", json.abc); - do_check_eq("abc", json.easy); - do_check_eq(expected, json.created); -}); - -add_task(function test_time_accessor_all() { - let lower = profile_creation_lower; - let acc = getAccessor(); - let expected; - let created = yield acc.created - let upper = Date.now() + 1000; - do_check_true(lower < created); - do_check_true(upper >= created); - expected = created; - - let again = yield acc.created - do_check_eq(expected, again); -}); - -add_task(function* test_time_reset() { - let lower = profile_creation_lower; - let acc = getAccessor(); - let testTime = 100000; - yield acc.recordProfileReset(testTime); - let reset = yield acc.reset; - Assert.equal(reset, testTime); -}); - -add_test(function test_constructor() { - let provider = new ProfileMetadataProvider("named"); - run_next_test(); -}); - -add_test(function test_profile_files() { - let provider = new ProfileMetadataProvider(); - - function onSuccess(answer) { - let now = Date.now() / MILLISECONDS_PER_DAY; - print("Got " + answer.profileCreation + ", versus now = " + now); - Assert.ok(answer.profileCreation < now); - run_next_test(); - } - - function onFailure(ex) { - do_throw("Directory iteration failed: " + ex); - } - - provider.getProfileDays().then(onSuccess, onFailure); -}); - -// A generic test helper. We use this with both real -// and mock providers in these tests. -function test_collect_constant(provider, expectReset) { - return Task.spawn(function* () { - yield provider.collectConstantData(); - - let m = provider.getMeasurement("age", 2); - Assert.notEqual(m, null); - let values = yield m.getValues(); - Assert.ok(values.singular.has("profileCreation")); - let createValue = values.singular.get("profileCreation")[1]; - let resetValue; - if (expectReset) { - Assert.equal(values.singular.size, 2); - Assert.ok(values.singular.has("profileReset")); - resetValue = values.singular.get("profileReset")[1]; - } else { - Assert.equal(values.singular.size, 1); - Assert.ok(!values.singular.has("profileReset")); - } - return [createValue, resetValue]; - }); -} - -add_task(function* test_collect_constant_mock_no_reset() { - let storage = yield Metrics.Storage("collect_constant_mock"); - let provider = new MockProfileMetadataProvider(); - yield provider.init(storage); - - let v = yield test_collect_constant(provider, false); - Assert.equal(v.length, 2); - Assert.equal(v[0], 1234); - Assert.equal(v[1], undefined); - - yield storage.close(); -}); - -add_task(function* test_collect_constant_mock_with_reset() { - let storage = yield Metrics.Storage("collect_constant_mock"); - let provider = new MockProfileMetadataProvider(); - provider.includeProfileReset = true; - yield provider.init(storage); - - let v = yield test_collect_constant(provider, true); - Assert.equal(v.length, 2); - Assert.equal(v[0], 1234); - Assert.equal(v[1], 5678); - - yield storage.close(); -}); - -add_task(function* test_collect_constant_real_no_reset() { - let provider = new ProfileMetadataProvider(); - let storage = yield Metrics.Storage("collect_constant_real"); - yield provider.init(storage); - - let vals = yield test_collect_constant(provider, false); - let created = vals[0]; - let reset = vals[1]; - Assert.equal(reset, undefined); - - let ms = created * MILLISECONDS_PER_DAY; - let lower = profile_creation_lower; - let upper = Date.now() + 1000; - print("Day: " + created); - print("msec: " + ms); - print("Lower: " + lower); - print("Upper: " + upper); - Assert.ok(lower <= ms); - Assert.ok(upper >= ms); - - yield storage.close(); -}); - -add_task(function* test_collect_constant_real_with_reset() { - let now = Date.now(); - let acc = getAccessor(); - yield acc.writeTimes({created: now-MILLISECONDS_PER_DAY, // yesterday - reset: Date.now()}); // today - - let provider = new ProfileMetadataProvider(); - let storage = yield Metrics.Storage("collect_constant_real"); - yield provider.init(storage); - - let [created, reset] = yield test_collect_constant(provider, true); - // we've already tested truncate() works as expected, so here just check - // we got values. - Assert.ok(created); - Assert.ok(reset); - Assert.ok(created <= reset); - - yield storage.close(); -}); - diff --git a/services/healthreport/tests/xpcshell/test_provider_addons.js b/services/healthreport/tests/xpcshell/test_provider_addons.js deleted file mode 100644 index 92a8137e4d..0000000000 --- a/services/healthreport/tests/xpcshell/test_provider_addons.js +++ /dev/null @@ -1,299 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -"use strict"; - -var {utils: Cu, classes: Cc, interfaces: Ci} = Components; - - -Cu.import("resource://gre/modules/Metrics.jsm"); -Cu.import("resource://gre/modules/services/healthreport/providers.jsm"); - -// The hack, it burns. This could go away if extensions code exposed its -// test environment setup functions as a testing-only JSM. See similar -// code in Sync's head_helpers.js. -var gGlobalScope = this; -function loadAddonManager() { - let ns = {}; - Cu.import("resource://gre/modules/Services.jsm", ns); - let head = "../../../../toolkit/mozapps/extensions/test/xpcshell/head_addons.js"; - let file = do_get_file(head); - let uri = ns.Services.io.newFileURI(file); - ns.Services.scriptloader.loadSubScript(uri.spec, gGlobalScope); - createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2"); - startupManager(); -} - -function run_test() { - loadAddonManager(); - run_next_test(); -} - -add_test(function test_constructor() { - let provider = new AddonsProvider(); - - run_next_test(); -}); - -add_task(function test_init() { - let storage = yield Metrics.Storage("init"); - let provider = new AddonsProvider(); - yield provider.init(storage); - yield provider.shutdown(); - - yield storage.close(); -}); - -function monkeypatchAddons(provider, addons) { - if (!Array.isArray(addons)) { - throw new Error("Must define array of addon objects."); - } - - Object.defineProperty(provider, "_createDataStructure", { - value: function _createDataStructure() { - return AddonsProvider.prototype._createDataStructure.call(provider, addons); - }, - }); -} - -add_task(function test_collect() { - let storage = yield Metrics.Storage("collect"); - let provider = new AddonsProvider(); - yield provider.init(storage); - - let now = new Date(); - - // FUTURE install add-on via AddonManager and don't use monkeypatching. - let testAddons = [ - { - id: "addon0", - userDisabled: false, - appDisabled: false, - version: "1", - type: "extension", - scope: 1, - foreignInstall: false, - hasBinaryComponents: false, - installDate: now, - updateDate: now, - }, - // This plugin entry should get ignored. - { - id: "addon1", - userDisabled: false, - appDisabled: false, - version: "2", - type: "plugin", - scope: 1, - foreignInstall: false, - hasBinaryComponents: false, - installDate: now, - updateDate: now, - }, - // Is counted but full details are omitted because it is a theme. - { - id: "addon2", - userDisabled: false, - appDisabled: false, - version: "3", - type: "theme", - scope: 1, - foreignInstall: false, - hasBinaryComponents: false, - installDate: now, - updateDate: now, - }, - { - id: "addon3", - userDisabled: false, - appDisabled: false, - version: "4", - type: "service", - scope: 1, - foreignInstall: false, - hasBinaryComponents: false, - installDate: now, - updateDate: now, - description: "addon3 description" - }, - { - // Should be excluded from the report completely - id: "pluginfake", - type: "plugin", - userDisabled: false, - appDisabled: false, - }, - { - // Should be in gm-plugins - id: "gmp-testgmp", - type: "plugin", - userDisabled: false, - version: "7.2", - isGMPlugin: true, - }, - ]; - - monkeypatchAddons(provider, testAddons); - - let testPlugins = { - "Test Plug-in": - { - "version": "1.0.0.0", - "description": "Plug-in for testing purposes.™ (हिन्दी 中文 العربية)", - "blocklisted": false, - "disabled": false, - "clicktoplay": false, - "mimeTypes":[ - "application/x-test" - ], - }, - "Second Test Plug-in": - { - "version": "1.0.0.0", - "description": "Second plug-in for testing purposes.", - "blocklisted": false, - "disabled": false, - "clicktoplay": false, - "mimeTypes":[ - "application/x-second-test" - ], - }, - "Java Test Plug-in": - { - "version": "1.0.0.0", - "description": "Dummy Java plug-in for testing purposes.", - "blocklisted": false, - "disabled": false, - "clicktoplay": false, - "mimeTypes":[ - "application/x-java-test" - ], - }, - }; - - let pluginTags = Cc["@mozilla.org/plugin/host;1"] - .getService(Ci.nsIPluginHost) - .getPluginTags({}); - - for (let tag of pluginTags) { - if (tag.name in testPlugins) { - let p = testPlugins[tag.name]; - p.id = tag.filename+":"+tag.name+":"+p.version+":"+p.description; - } - } - - yield provider.collectConstantData(); - - // Test addons measurement. - - let addons = provider.getMeasurement("addons", 2); - let data = yield addons.getValues(); - - do_check_eq(data.days.size, 0); - do_check_eq(data.singular.size, 1); - do_check_true(data.singular.has("addons")); - - let json = data.singular.get("addons")[1]; - let value = JSON.parse(json); - do_check_eq(typeof(value), "object"); - do_check_eq(Object.keys(value).length, 2); - do_check_true("addon0" in value); - do_check_true(!("addon1" in value)); - do_check_true(!("addon2" in value)); - do_check_true("addon3" in value); - do_check_true(!("pluginfake" in value)); - do_check_true(!("gmp-testgmp" in value)); - - let serializer = addons.serializer(addons.SERIALIZE_JSON); - let serialized = serializer.singular(data.singular); - do_check_eq(typeof(serialized), "object"); - do_check_eq(Object.keys(serialized).length, 3); // Our entries, plus _v. - do_check_true("addon0" in serialized); - do_check_true("addon3" in serialized); - do_check_eq(serialized._v, 2); - - // Test plugins measurement. - - let plugins = provider.getMeasurement("plugins", 1); - data = yield plugins.getValues(); - - do_check_eq(data.days.size, 0); - do_check_eq(data.singular.size, 1); - do_check_true(data.singular.has("plugins")); - - json = data.singular.get("plugins")[1]; - value = JSON.parse(json); - do_check_eq(typeof(value), "object"); - do_check_eq(Object.keys(value).length, pluginTags.length); - - do_check_true(testPlugins["Test Plug-in"].id in value); - do_check_true(testPlugins["Second Test Plug-in"].id in value); - do_check_true(testPlugins["Java Test Plug-in"].id in value); - - for (let id in value) { - let item = value[id]; - let testData = testPlugins[item.name]; - for (let prop in testData) { - if (prop == "mimeTypes" || prop == "id") { - continue; - } - do_check_eq(testData[prop], item[prop]); - } - - for (let mime of testData.mimeTypes) { - do_check_true(item.mimeTypes.indexOf(mime) != -1); - } - } - - serializer = plugins.serializer(plugins.SERIALIZE_JSON); - serialized = serializer.singular(data.singular); - do_check_eq(typeof(serialized), "object"); - do_check_eq(Object.keys(serialized).length, pluginTags.length+1); // Our entries, plus _v. - for (let name in testPlugins) { - do_check_true(testPlugins[name].id in serialized); - } - do_check_eq(serialized._v, 1); - - // Test GMP plugins measurement. - - let gmPlugins = provider.getMeasurement("gm-plugins", 1); - data = yield gmPlugins.getValues(); - - do_check_eq(data.days.size, 0); - do_check_eq(data.singular.size, 1); - do_check_true(data.singular.has("gm-plugins")); - - json = data.singular.get("gm-plugins")[1]; - value = JSON.parse(json); - do_print("value: " + json); - do_check_eq(typeof(value), "object"); - do_check_eq(Object.keys(value).length, 1); - - do_check_eq(value["gmp-testgmp"].version, "7.2"); - do_check_eq(value["gmp-testgmp"].userDisabled, false); - - serializer = gmPlugins.serializer(plugins.SERIALIZE_JSON); - serialized = serializer.singular(data.singular); - do_check_eq(typeof(serialized), "object"); - do_check_eq(serialized["gmp-testgmp"].version, "7.2"); - do_check_eq(serialized._v, 1); - - // Test counts measurement. - - let counts = provider.getMeasurement("counts", 2); - data = yield counts.getValues(); - do_check_eq(data.days.size, 1); - do_check_eq(data.singular.size, 0); - do_check_true(data.days.hasDay(now)); - - value = data.days.getDay(now); - do_check_eq(value.size, 4); - do_check_eq(value.get("extension"), 1); - do_check_eq(value.get("plugin"), pluginTags.length); - do_check_eq(value.get("theme"), 1); - do_check_eq(value.get("service"), 1); - - yield provider.shutdown(); - yield storage.close(); -}); - diff --git a/services/healthreport/tests/xpcshell/test_provider_appinfo.js b/services/healthreport/tests/xpcshell/test_provider_appinfo.js deleted file mode 100644 index cf5082284e..0000000000 --- a/services/healthreport/tests/xpcshell/test_provider_appinfo.js +++ /dev/null @@ -1,281 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -"use strict"; - -var {interfaces: Ci, results: Cr, utils: Cu, classes: Cc} = Components; - -Cu.import("resource://gre/modules/Metrics.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource://gre/modules/services/healthreport/providers.jsm"); -Cu.import("resource://testing-common/services/healthreport/utils.jsm"); -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); - -XPCOMUtils.defineLazyGetter(this, "gDatareportingService", - () => Cc["@mozilla.org/datareporting/service;1"] - .getService(Ci.nsISupports) - .wrappedJSObject); - - -function run_test() { - do_get_profile(); - - // Send the needed startup notifications to the datareporting service - // to ensure that it has been initialized. - gDatareportingService.observe(null, "app-startup", null); - gDatareportingService.observe(null, "profile-after-change", null); - - run_next_test(); -} - -add_test(function test_constructor() { - let provider = new AppInfoProvider(); - - run_next_test(); -}); - -add_task(function test_collect_smoketest() { - let storage = yield Metrics.Storage("collect_smoketest"); - let provider = new AppInfoProvider(); - yield provider.init(storage); - - let now = new Date(); - yield provider.collectConstantData(); - - let m = provider.getMeasurement("appinfo", 2); - let data = yield storage.getMeasurementValues(m.id); - let serializer = m.serializer(m.SERIALIZE_JSON); - let d = serializer.singular(data.singular); - - do_check_eq(d._v, 2); - do_check_eq(d.vendor, "Mozilla"); - do_check_eq(d.name, "xpcshell"); - do_check_eq(d.id, "xpcshell@tests.mozilla.org"); - do_check_eq(d.version, "1"); - do_check_eq(d.appBuildID, "20121107"); - do_check_eq(d.platformVersion, "p-ver"); - do_check_eq(d.platformBuildID, "20121106"); - do_check_eq(d.os, "XPCShell"); - do_check_eq(d.xpcomabi, "noarch-spidermonkey"); - - do_check_eq(data.days.size, 1); - do_check_true(data.days.hasDay(now)); - let day = data.days.getDay(now); - do_check_eq(day.size, 3); - do_check_true(day.has("isDefaultBrowser")); - do_check_true(day.has("isTelemetryEnabled")); - do_check_true(day.has("isBlocklistEnabled")); - - // TODO Bug 827189 Actually test this properly. On some local builds, this - // is always -1 (the service throws). On buildbot, it seems to always be 0. - do_check_neq(day.get("isDefaultBrowser"), 1); - - yield provider.shutdown(); - yield storage.close(); -}); - -add_task(function test_record_version() { - let storage = yield Metrics.Storage("record_version"); - - let provider = new AppInfoProvider(); - let now = new Date(); - yield provider.init(storage); - - // The provider records information on startup. - let m = provider.getMeasurement("versions", 2); - let data = yield m.getValues(); - - do_check_true(data.days.hasDay(now)); - let day = data.days.getDay(now); - do_check_eq(day.size, 4); - do_check_true(day.has("appVersion")); - do_check_true(day.has("platformVersion")); - do_check_true(day.has("appBuildID")); - do_check_true(day.has("platformBuildID")); - - let value = day.get("appVersion"); - do_check_true(Array.isArray(value)); - do_check_eq(value.length, 1); - let ai = getAppInfo(); - do_check_eq(value[0], ai.version); - - value = day.get("platformVersion"); - do_check_true(Array.isArray(value)); - do_check_eq(value.length, 1); - do_check_eq(value[0], ai.platformVersion); - - value = day.get("appBuildID"); - do_check_true(Array.isArray(value)); - do_check_eq(value.length, 1); - do_check_eq(value[0], ai.appBuildID); - - value = day.get("platformBuildID"); - do_check_true(Array.isArray(value)); - do_check_eq(value.length, 1); - do_check_eq(value[0], ai.platformBuildID); - - yield provider.shutdown(); - yield storage.close(); -}); - -add_task(function test_record_version_change() { - let storage = yield Metrics.Storage("record_version_change"); - - let provider = new AppInfoProvider(); - let now = new Date(); - yield provider.init(storage); - yield provider.shutdown(); - - let ai = getAppInfo(); - ai.version = "new app version"; - ai.platformVersion = "new platform version"; - ai.appBuildID = "new app id"; - ai.platformBuildID = "new platform id"; - updateAppInfo(ai); - - provider = new AppInfoProvider(); - yield provider.init(storage); - - // There should be 2 records in the versions history. - let m = provider.getMeasurement("versions", 2); - let data = yield m.getValues(); - do_check_true(data.days.hasDay(now)); - let day = data.days.getDay(now); - - let value = day.get("appVersion"); - do_check_true(Array.isArray(value)); - do_check_eq(value.length, 2); - do_check_eq(value[1], "new app version"); - - value = day.get("platformVersion"); - do_check_true(Array.isArray(value)); - do_check_eq(value.length, 2); - do_check_eq(value[1], "new platform version"); - - // There should be 2 records in the buildID history. - value = day.get("appBuildID"); - do_check_true(Array.isArray(value)); - do_check_eq(value.length, 2); - do_check_eq(value[1], "new app id"); - - value = day.get("platformBuildID"); - do_check_true(Array.isArray(value)); - do_check_eq(value.length, 2); - do_check_eq(value[1], "new platform id"); - - yield provider.shutdown(); - yield storage.close(); -}); - -add_task(function test_record_telemetry() { - let storage = yield Metrics.Storage("record_telemetry"); - let provider; - - let now = new Date(); - - Services.prefs.setBoolPref("toolkit.telemetry.enabled", true); - provider = new AppInfoProvider(); - yield provider.init(storage); - yield provider.collectConstantData(); - - let m = provider.getMeasurement("appinfo", 2); - let data = yield m.getValues(); - let d = yield m.serializer(m.SERIALIZE_JSON).daily(data.days.getDay(now)); - do_check_eq(1, d.isTelemetryEnabled); - yield provider.shutdown(); - - Services.prefs.setBoolPref("toolkit.telemetry.enabled", false); - provider = new AppInfoProvider(); - yield provider.init(storage); - yield provider.collectConstantData(); - - m = provider.getMeasurement("appinfo", 2); - data = yield m.getValues(); - d = yield m.serializer(m.SERIALIZE_JSON).daily(data.days.getDay(now)); - do_check_eq(0, d.isTelemetryEnabled); - yield provider.shutdown(); - - yield storage.close(); -}); - -add_task(function test_record_blocklist() { - let storage = yield Metrics.Storage("record_blocklist"); - - let now = new Date(); - - Services.prefs.setBoolPref("extensions.blocklist.enabled", true); - let provider = new AppInfoProvider(); - yield provider.init(storage); - yield provider.collectConstantData(); - - let m = provider.getMeasurement("appinfo", 2); - let data = yield m.getValues(); - let d = yield m.serializer(m.SERIALIZE_JSON).daily(data.days.getDay(now)); - do_check_eq(d.isBlocklistEnabled, 1); - yield provider.shutdown(); - - Services.prefs.setBoolPref("extensions.blocklist.enabled", false); - provider = new AppInfoProvider(); - yield provider.init(storage); - yield provider.collectConstantData(); - - m = provider.getMeasurement("appinfo", 2); - data = yield m.getValues(); - d = yield m.serializer(m.SERIALIZE_JSON).daily(data.days.getDay(now)); - do_check_eq(d.isBlocklistEnabled, 0); - yield provider.shutdown(); - - yield storage.close(); -}); - -add_task(function test_record_app_update () { - let storage = yield Metrics.Storage("record_update"); - - Services.prefs.setBoolPref("app.update.enabled", true); - Services.prefs.setBoolPref("app.update.auto", true); - let provider = new AppInfoProvider(); - yield provider.init(storage); - let now = new Date(); - yield provider.collectDailyData(); - - let m = provider.getMeasurement("update", 1); - let data = yield m.getValues(); - let d = yield m.serializer(m.SERIALIZE_JSON).daily(data.days.getDay(now)); - do_check_eq(d.enabled, 1); - do_check_eq(d.autoDownload, 1); - - Services.prefs.setBoolPref("app.update.enabled", false); - Services.prefs.setBoolPref("app.update.auto", false); - - yield provider.collectDailyData(); - data = yield m.getValues(); - d = yield m.serializer(m.SERIALIZE_JSON).daily(data.days.getDay(now)); - do_check_eq(d.enabled, 0); - do_check_eq(d.autoDownload, 0); - - yield provider.shutdown(); - yield storage.close(); -}); - -add_task(function test_healthreporter_integration () { - let reporter = getHealthReporter("healthreporter_integration"); - yield reporter.init(); - - try { - yield reporter._providerManager.registerProviderFromType(AppInfoProvider); - yield reporter.collectMeasurements(); - - let payload = yield reporter.getJSONPayload(true); - let days = payload['data']['days']; - - for (let [day, measurements] in Iterator(days)) { - do_check_eq(Object.keys(measurements).length, 3); - do_check_true("org.mozilla.appInfo.appinfo" in measurements); - do_check_true("org.mozilla.appInfo.update" in measurements); - do_check_true("org.mozilla.appInfo.versions" in measurements); - } - } finally { - yield reporter._shutdown(); - } -}); diff --git a/services/healthreport/tests/xpcshell/test_provider_hotfix.js b/services/healthreport/tests/xpcshell/test_provider_hotfix.js deleted file mode 100644 index 1657455962..0000000000 --- a/services/healthreport/tests/xpcshell/test_provider_hotfix.js +++ /dev/null @@ -1,179 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -"use strict"; - -var {utils: Cu} = Components; - -Cu.import("resource://gre/modules/osfile.jsm"); -Cu.import("resource://gre/modules/Metrics.jsm"); -Cu.import("resource://gre/modules/services/healthreport/providers.jsm"); - -const EXAMPLE_2014052701 = { - "upgradedFrom":"13.0.1", - "uninstallReason":"SUCCESSFUL_UPGRADE", - "_entityID":null, - "forensicsID":"29525548-b653-49db-bfb8-a160cdfbeb4a", - "_installInProgress":false, - "_everCompatible":true, - "reportedWindowsVersion":[6,1,1], - "actualWindowsVersion":[6,1,1], - "firstNotifyDay":0, - "lastNotifyDay":0, - "downloadAttempts":1, - "downloadFailures":0, - "installAttempts":1, - "installSuccesses":1, - "installLauncherFailures":0, - "installFailures":0, - "notificationsShown":0, - "notificationsClicked":0, - "notificationsDismissed":0, - "notificationsRemoved":0, - "launcherExitCodes":{"0":1} -}; - -function run_test() { - run_next_test(); -} - -add_task(function* init() { - do_get_profile(); -}); - -add_task(function test_constructor() { - new HotfixProvider(); -}); - -add_task(function* test_init() { - let storage = yield Metrics.Storage("init"); - let provider = new HotfixProvider(); - yield provider.init(storage); - yield provider.shutdown(); - - yield storage.close(); -}); - -add_task(function* test_collect_empty() { - let storage = yield Metrics.Storage("collect_empty"); - let provider = new HotfixProvider(); - yield provider.init(storage); - - yield provider.collectDailyData(); - - let m = provider.getMeasurement("update", 1); - let data = yield m.getValues(); - Assert.equal(data.singular.size, 0); - Assert.equal(data.days.size, 0); - - yield storage.close(); -}); - -add_task(function* test_collect_20140527() { - let storage = yield Metrics.Storage("collect_20140527"); - let provider = new HotfixProvider(); - yield provider.init(storage); - - let path = OS.Path.join(OS.Constants.Path.profileDir, - "hotfix.v20140527.01.json"); - let encoder = new TextEncoder(); - yield OS.File.writeAtomic(path, - encoder.encode(JSON.stringify(EXAMPLE_2014052701))); - - yield provider.collectDailyData(); - - let m = provider.getMeasurement("update", 1); - let data = yield m.getValues(); - let s = data.singular; - Assert.equal(s.size, 7); - Assert.equal(s.get("v20140527.upgradedFrom")[1], "13.0.1"); - Assert.equal(s.get("v20140527.uninstallReason")[1], "SUCCESSFUL_UPGRADE"); - Assert.equal(s.get("v20140527.downloadAttempts")[1], 1); - Assert.equal(s.get("v20140527.downloadFailures")[1], 0); - Assert.equal(s.get("v20140527.installAttempts")[1], 1); - Assert.equal(s.get("v20140527.installFailures")[1], 0); - Assert.equal(s.get("v20140527.notificationsShown")[1], 0); - - // Ensure the dynamic fields get serialized. - let serializer = m.serializer(m.SERIALIZE_JSON); - let d = serializer.singular(s); - - Assert.deepEqual(d, { - "_v": 1, - "v20140527.upgradedFrom": "13.0.1", - "v20140527.uninstallReason": "SUCCESSFUL_UPGRADE", - "v20140527.downloadAttempts": 1, - "v20140527.downloadFailures": 0, - "v20140527.installAttempts": 1, - "v20140527.installFailures": 0, - "v20140527.notificationsShown": 0, - }); - - // Don't interfere with next test. - yield OS.File.remove(path); - - yield storage.close(); -}); - -add_task(function* test_collect_multiple_versions() { - let storage = yield Metrics.Storage("collect_multiple_versions"); - let provider = new HotfixProvider(); - yield provider.init(storage); - - let p1 = { - upgradedFrom: "12.0", - uninstallReason: "SUCCESSFUL_UPGRADE", - downloadAttempts: 3, - downloadFailures: 1, - installAttempts: 1, - installFailures: 1, - notificationsShown: 2, - }; - - let p2 = { - downloadAttempts: 5, - downloadFailures: 3, - installAttempts: 2, - installFailures: 2, - uninstallReason: null, - notificationsShown: 1, - }; - - let path1 = OS.Path.join(OS.Constants.Path.profileDir, "updateHotfix.v20140601.json"); - let path2 = OS.Path.join(OS.Constants.Path.profileDir, "updateHotfix.v20140701.json"); - - let encoder = new TextEncoder(); - yield OS.File.writeAtomic(path1, encoder.encode(JSON.stringify(p1))); - yield OS.File.writeAtomic(path2, encoder.encode(JSON.stringify(p2))); - - yield provider.collectDailyData(); - - let m = provider.getMeasurement("update", 1); - let data = yield m.getValues(); - - let serializer = m.serializer(m.SERIALIZE_JSON); - let d = serializer.singular(data.singular); - - Assert.deepEqual(d, { - "_v": 1, - "v20140601.upgradedFrom": "12.0", - "v20140601.uninstallReason": "SUCCESSFUL_UPGRADE", - "v20140601.downloadAttempts": 3, - "v20140601.downloadFailures": 1, - "v20140601.installAttempts": 1, - "v20140601.installFailures": 1, - "v20140601.notificationsShown": 2, - "v20140701.uninstallReason": "STILL_INSTALLED", - "v20140701.downloadAttempts": 5, - "v20140701.downloadFailures": 3, - "v20140701.installAttempts": 2, - "v20140701.installFailures": 2, - "v20140701.notificationsShown": 1, - }); - - // Don't interfere with next test. - yield OS.File.remove(path1); - yield OS.File.remove(path2); - - yield storage.close(); -}); diff --git a/services/healthreport/tests/xpcshell/test_provider_places.js b/services/healthreport/tests/xpcshell/test_provider_places.js deleted file mode 100644 index 2712d8b883..0000000000 --- a/services/healthreport/tests/xpcshell/test_provider_places.js +++ /dev/null @@ -1,46 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -"use strict"; - -var {utils: Cu} = Components; - -Cu.import("resource://gre/modules/Metrics.jsm"); -Cu.import("resource://gre/modules/services/healthreport/providers.jsm"); - - -function run_test() { - run_next_test(); -} - -add_test(function test_constructor() { - let provider = new PlacesProvider(); - - run_next_test(); -}); - -add_task(function test_collect_smoketest() { - let storage = yield Metrics.Storage("collect_smoketest"); - let provider = new PlacesProvider(); - - yield provider.init(storage); - - let now = new Date(); - yield provider.collectDailyData(); - - let m = provider.getMeasurement("places", 1); - let data = yield storage.getMeasurementValues(m.id); - do_check_eq(data.days.size, 1); - do_check_true(data.days.hasDay(now)); - - let serializer = m.serializer(m.SERIALIZE_JSON); - let day = serializer.daily(data.days.getDay(now)); - - do_check_eq(day._v, 1); - do_check_eq(Object.keys(day).length, 3); - do_check_eq(day.pages, 0); - do_check_eq(day.bookmarks, 0); - - yield storage.close(); -}); - diff --git a/services/healthreport/tests/xpcshell/test_provider_searches.js b/services/healthreport/tests/xpcshell/test_provider_searches.js deleted file mode 100644 index 11a12a84ed..0000000000 --- a/services/healthreport/tests/xpcshell/test_provider_searches.js +++ /dev/null @@ -1,187 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -"use strict"; - -var {utils: Cu} = Components; - -Cu.import("resource://gre/modules/Metrics.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); -var bsp = Cu.import("resource://gre/modules/services/healthreport/providers.jsm"); - -const DEFAULT_ENGINES = [ - {name: "Amazon.com", identifier: "amazondotcom"}, - {name: "Bing", identifier: "bing"}, - {name: "Google", identifier: "google"}, - {name: "Yahoo", identifier: "yahoo"}, - {name: "Foobar Search", identifier: "foobar"}, -]; - -function MockSearchCountMeasurement() { - bsp.SearchCountMeasurement3.call(this); -} -MockSearchCountMeasurement.prototype = { - __proto__: bsp.SearchCountMeasurement3.prototype, -}; - -function MockSearchesProvider() { - SearchesProvider.call(this); -} -MockSearchesProvider.prototype = { - __proto__: SearchesProvider.prototype, - measurementTypes: [MockSearchCountMeasurement], -}; - -function run_test() { - // Tell the search service we are running in the US. This also has the - // desired side-effect of preventing our geoip lookup. - Services.prefs.setBoolPref("browser.search.isUS", true); - Services.prefs.setCharPref("browser.search.countryCode", "US"); - - run_next_test(); -} - -add_test(function test_constructor() { - let provider = new SearchesProvider(); - - run_next_test(); -}); - -add_task(function* test_record() { - let storage = yield Metrics.Storage("record"); - let provider = new MockSearchesProvider(); - - yield provider.init(storage); - - let now = new Date(); - - // Record searches for all but one of our defaults, and one engine that's - // not a default. - for (let engine of DEFAULT_ENGINES.concat([{name: "Not Default", identifier: "notdef"}])) { - if (engine.identifier == "yahoo") { - continue; - } - yield provider.recordSearch(engine, "abouthome"); - yield provider.recordSearch(engine, "contextmenu"); - yield provider.recordSearch(engine, "newtab"); - yield provider.recordSearch(engine, "searchbar"); - yield provider.recordSearch(engine, "urlbar"); - } - - // Invalid sources should throw. - let errored = false; - try { - yield provider.recordSearch(DEFAULT_ENGINES[0], "bad source"); - } catch (ex) { - errored = true; - } finally { - do_check_true(errored); - } - - let m = provider.getMeasurement("counts", 3); - let data = yield m.getValues(); - do_check_eq(data.days.size, 1); - do_check_true(data.days.hasDay(now)); - - let day = data.days.getDay(now); - for (let engine of DEFAULT_ENGINES) { - let identifier = engine.identifier; - let expected = identifier != "yahoo"; - - for (let source of ["abouthome", "contextmenu", "searchbar", "urlbar"]) { - let field = identifier + "." + source; - if (expected) { - do_check_true(day.has(field)); - do_check_eq(day.get(field), 1); - } else { - do_check_false(day.has(field)); - } - } - } - - // Also, check that our non-default engine contributed, with a computed - // identifier. - let identifier = "notdef"; - for (let source of ["abouthome", "contextmenu", "searchbar", "urlbar"]) { - let field = identifier + "." + source; - do_check_true(day.has(field)); - } - - yield storage.close(); -}); - -add_task(function* test_includes_other_fields() { - let storage = yield Metrics.Storage("includes_other_fields"); - let provider = new MockSearchesProvider(); - - yield provider.init(storage); - let m = provider.getMeasurement("counts", 3); - - // Register a search against a provider that isn't live in this session. - let id = yield m.storage.registerField(m.id, "test.searchbar", - Metrics.Storage.FIELD_DAILY_COUNTER); - - let testField = "test.searchbar"; - let now = new Date(); - yield m.storage.incrementDailyCounterFromFieldID(id, now); - - // Make sure we don't know about it. - do_check_false(testField in m.fields); - - // But we want to include it in payloads. - do_check_true(m.shouldIncludeField(testField)); - - // And we do so. - let data = yield provider.storage.getMeasurementValues(m.id); - let serializer = m.serializer(m.SERIALIZE_JSON); - let formatted = serializer.daily(data.days.getDay(now)); - do_check_true(testField in formatted); - do_check_eq(formatted[testField], 1); - - yield storage.close(); -}); - -add_task(function* test_default_search_engine() { - let storage = yield Metrics.Storage("default_search_engine"); - let provider = new SearchesProvider(); - yield provider.init(storage); - - let m = provider.getMeasurement("engines", 2); - - let now = new Date(); - yield provider.collectDailyData(); - let data = yield m.getValues(); - Assert.ok(data.days.hasDay(now)); - - let day = data.days.getDay(now); - Assert.equal(day.size, 1); - Assert.ok(day.has("default")); - - // test environment doesn't have a default engine. - Assert.equal(day.get("default"), "NONE"); - - Services.search.addEngineWithDetails("testdefault", - "http://localhost/icon.png", - null, - "test description", - "GET", - "http://localhost/search/%s"); - let engine1 = Services.search.getEngineByName("testdefault"); - Assert.ok(engine1); - Services.search.defaultEngine = engine1; - - yield provider.collectDailyData(); - data = yield m.getValues(); - Assert.equal(data.days.getDay(now).get("default"), "other-testdefault"); - - // If no cohort identifier is set, we shouldn't report a cohort. - Assert.equal(data.days.getDay(now).get("cohort"), undefined); - - // Set a cohort identifier and verify we record it. - Services.prefs.setCharPref("browser.search.cohort", "testcohort"); - yield provider.collectDailyData(); - data = yield m.getValues(); - Assert.equal(data.days.getDay(now).get("cohort"), "testcohort"); - - yield storage.close(); -}); diff --git a/services/healthreport/tests/xpcshell/test_provider_sessions.js b/services/healthreport/tests/xpcshell/test_provider_sessions.js deleted file mode 100644 index d56b708dd8..0000000000 --- a/services/healthreport/tests/xpcshell/test_provider_sessions.js +++ /dev/null @@ -1,217 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -"use strict"; - -var {utils: Cu} = Components; - - -Cu.import("resource://gre/modules/Promise.jsm"); -Cu.import("resource://gre/modules/Metrics.jsm"); -Cu.import("resource://gre/modules/Task.jsm"); -Cu.import("resource://gre/modules/services-common/utils.js"); -Cu.import("resource://gre/modules/services/datareporting/sessions.jsm"); -Cu.import("resource://gre/modules/services/healthreport/providers.jsm"); - - -function run_test() { - run_next_test(); -} - -add_test(function test_constructor() { - let provider = new SessionsProvider(); - - run_next_test(); -}); - -add_task(function test_init() { - let storage = yield Metrics.Storage("init"); - let provider = new SessionsProvider(); - yield provider.init(storage); - yield provider.shutdown(); - - yield storage.close(); -}); - -function monkeypatchStartupInfo(recorder, start=new Date(), offset=500) { - Object.defineProperty(recorder, "_getStartupInfo", { - value: function _getStartupInfo() { - return { - process: start, - main: new Date(start.getTime() + offset), - firstPaint: new Date(start.getTime() + 2 * offset), - sessionRestored: new Date(start.getTime() + 3 * offset), - }; - } - }); -} - -function sleep(wait) { - let deferred = Promise.defer(); - - let timer = CommonUtils.namedTimer(function onTimer() { - deferred.resolve(); - }, wait, deferred.promise, "_sleepTimer"); - - return deferred.promise; -} - -function getProvider(name, now=new Date(), init=true) { - return Task.spawn(function () { - let storage = yield Metrics.Storage(name); - let provider = new SessionsProvider(); - - let recorder = new SessionRecorder("testing." + name + ".sessions."); - monkeypatchStartupInfo(recorder, now); - provider.healthReporter = {sessionRecorder: recorder}; - recorder.onStartup(); - - if (init) { - yield provider.init(storage); - } - - throw new Task.Result([provider, storage, recorder]); - }); -} - -add_task(function test_current_session() { - let now = new Date(); - let [provider, storage, recorder] = yield getProvider("current_session", now); - - yield sleep(25); - recorder.onActivity(true); - - let current = provider.getMeasurement("current", 3); - let values = yield current.getValues(); - let fields = values.singular; - - for (let field of ["startDay", "activeTicks", "totalTime", "main", "firstPaint", "sessionRestored"]) { - do_check_true(fields.has(field)); - } - - do_check_eq(fields.get("startDay")[1], Metrics.dateToDays(now)); - do_check_eq(fields.get("totalTime")[1], recorder.totalTime); - do_check_eq(fields.get("activeTicks")[1], 1); - do_check_eq(fields.get("main")[1], 500); - do_check_eq(fields.get("firstPaint")[1], 1000); - do_check_eq(fields.get("sessionRestored")[1], 1500); - - yield provider.shutdown(); - yield storage.close(); -}); - -add_task(function test_collect() { - let now = new Date(); - let [provider, storage, recorder] = yield getProvider("collect"); - - recorder.onShutdown(); - yield sleep(25); - - for (let i = 0; i < 5; i++) { - let recorder2 = new SessionRecorder("testing.collect.sessions."); - recorder2.onStartup(); - yield sleep(25); - recorder2.onShutdown(); - yield sleep(25); - } - - recorder = new SessionRecorder("testing.collect.sessions."); - recorder.onStartup(); - - // Collecting the provider should prune all previous sessions. - let sessions = recorder.getPreviousSessions(); - do_check_eq(Object.keys(sessions).length, 6); - yield provider.collectConstantData(); - sessions = recorder.getPreviousSessions(); - do_check_eq(Object.keys(sessions).length, 0); - - // And those previous sessions should make it to storage. - let daily = provider.getMeasurement("previous", 3); - let values = yield daily.getValues(); - do_check_true(values.days.hasDay(now)); - do_check_eq(values.days.size, 1); - let day = values.days.getDay(now); - do_check_eq(day.size, 5); - let previousStorageCount = day.get("main").length; - - for (let field of ["cleanActiveTicks", "cleanTotalTime", "main", "firstPaint", "sessionRestored"]) { - do_check_true(day.has(field)); - do_check_true(Array.isArray(day.get(field))); - do_check_eq(day.get(field).length, 6); - } - - let lastIndex = yield provider.getState("lastSession"); - do_check_eq(lastIndex, "" + (previousStorageCount - 1)); // 0-indexed - - // Fake an aborted session. If we create a 2nd recorder against the same - // prefs branch as a running one, this simulates what would happen if the - // first recorder didn't shut down. - let recorder2 = new SessionRecorder("testing.collect.sessions."); - recorder2.onStartup(); - do_check_eq(Object.keys(recorder.getPreviousSessions()).length, 1); - yield provider.collectConstantData(); - do_check_eq(Object.keys(recorder.getPreviousSessions()).length, 0); - - values = yield daily.getValues(); - day = values.days.getDay(now); - do_check_eq(day.size, previousStorageCount + 1); - previousStorageCount = day.get("main").length; - for (let field of ["abortedActiveTicks", "abortedTotalTime"]) { - do_check_true(day.has(field)); - do_check_true(Array.isArray(day.get(field))); - do_check_eq(day.get(field).length, 1); - } - - lastIndex = yield provider.getState("lastSession"); - do_check_eq(lastIndex, "" + (previousStorageCount - 1)); - - recorder.onShutdown(); - recorder2.onShutdown(); - - // If we try to insert a already-inserted session, it will be ignored. - recorder = new SessionRecorder("testing.collect.sessions."); - recorder._currentIndex = recorder._currentIndex - 1; - recorder._prunedIndex = recorder._currentIndex; - recorder.onStartup(); - // Session is left over from recorder2. - sessions = recorder.getPreviousSessions(); - do_check_eq(Object.keys(sessions).length, 1); - do_check_true(previousStorageCount - 1 in sessions); - yield provider.collectConstantData(); - lastIndex = yield provider.getState("lastSession"); - do_check_eq(lastIndex, "" + (previousStorageCount - 1)); - values = yield daily.getValues(); - day = values.days.getDay(now); - // We should not get additional entry. - do_check_eq(day.get("main").length, previousStorageCount); - recorder.onShutdown(); - - yield provider.shutdown(); - yield storage.close(); -}); - -add_task(function test_serialization() { - let [provider, storage, recorder] = yield getProvider("serialization"); - - yield sleep(1025); - recorder.onActivity(true); - - let current = provider.getMeasurement("current", 3); - let data = yield current.getValues(); - do_check_true("singular" in data); - - let serializer = current.serializer(current.SERIALIZE_JSON); - let fields = serializer.singular(data.singular); - - do_check_eq(fields._v, 3); - do_check_eq(fields.activeTicks, 1); - do_check_eq(fields.startDay, Metrics.dateToDays(recorder.startDate)); - do_check_eq(fields.main, 500); - do_check_eq(fields.firstPaint, 1000); - do_check_eq(fields.sessionRestored, 1500); - do_check_true(fields.totalTime > 0); - - yield provider.shutdown(); - yield storage.close(); -}); - diff --git a/services/healthreport/tests/xpcshell/test_provider_sysinfo.js b/services/healthreport/tests/xpcshell/test_provider_sysinfo.js deleted file mode 100644 index 0a98555649..0000000000 --- a/services/healthreport/tests/xpcshell/test_provider_sysinfo.js +++ /dev/null @@ -1,41 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -"use strict"; - -var {interfaces: Ci, results: Cr, utils: Cu} = Components; - -Cu.import("resource://gre/modules/Metrics.jsm"); -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource://gre/modules/services/healthreport/providers.jsm"); - - -function run_test() { - run_next_test(); -} - -add_test(function test_constructor() { - let provider = new SysInfoProvider(); - - run_next_test(); -}); - -add_task(function test_collect_smoketest() { - let storage = yield Metrics.Storage("collect_smoketest"); - let provider = new SysInfoProvider(); - yield provider.init(storage); - - yield provider.collectConstantData(); - - let m = provider.getMeasurement("sysinfo", 2); - let data = yield storage.getMeasurementValues(m.id); - let serializer = m.serializer(m.SERIALIZE_JSON); - let d = serializer.singular(data.singular); - - do_check_eq(d._v, 2); - do_check_true(d.cpuCount > 0); - do_check_neq(d.name, null); - - yield storage.close(); -}); - diff --git a/services/healthreport/tests/xpcshell/xpcshell.ini b/services/healthreport/tests/xpcshell/xpcshell.ini deleted file mode 100644 index 4ae010ea5a..0000000000 --- a/services/healthreport/tests/xpcshell/xpcshell.ini +++ /dev/null @@ -1,17 +0,0 @@ -[DEFAULT] -head = head.js -tail = -skip-if = toolkit == 'android' || toolkit == 'gonk' - -[test_load_modules.js] -[test_profile.js] -[test_healthreporter.js] -[test_provider_addons.js] -skip-if = buildapp == 'mulet' -[test_provider_appinfo.js] -[test_provider_hotfix.js] -[test_provider_places.js] -[test_provider_searches.js] -[test_provider_sysinfo.js] -[test_provider_sessions.js] - diff --git a/services/moz.build b/services/moz.build index 916c6ebf99..b259cae80c 100644 --- a/services/moz.build +++ b/services/moz.build @@ -10,15 +10,6 @@ DIRS += [ 'fxaccounts', ] -if CONFIG['MOZ_WIDGET_TOOLKIT'] != 'android': - # MOZ_SERVICES_HEALTHREPORT and therefore MOZ_DATA_REPORTING are - # defined on Android, but these features are implemented using Java. - if CONFIG['MOZ_SERVICES_HEALTHREPORT']: - DIRS += ['healthreport'] - - if CONFIG['MOZ_DATA_REPORTING']: - DIRS += ['datareporting'] - if CONFIG['MOZ_SERVICES_METRICS']: DIRS += ['metrics'] diff --git a/testing/mochitest/browser-test.js b/testing/mochitest/browser-test.js index bd3fc1617a..861f621243 100644 --- a/testing/mochitest/browser-test.js +++ b/testing/mochitest/browser-test.js @@ -3,21 +3,9 @@ var gTimeoutSeconds = 45; var gConfig; -if (Cc === undefined) { - var Cc = Components.classes; -} -if (Ci === undefined) { - var Ci = Components.interfaces; -} -if (Cu === undefined) { - var Cu = Components.utils; -} - Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/Task.jsm"); - -XPCOMUtils.defineLazyModuleGetter(this, "Services", - "resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/Services.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "CustomizationTabPreloader", "resource:///modules/CustomizationTabPreloader.jsm"); @@ -270,7 +258,7 @@ Tester.prototype = { // Replace the last tab with a fresh one if (window.gBrowser) { gBrowser.addTab("about:blank", { skipAnimation: true }); - gBrowser.removeCurrentTab(); + gBrowser.removeTab(gBrowser.selectedTab, { skipPermitUnload: true }); gBrowser.stop(); } diff --git a/testing/xpcshell/xpcshell.eslintrc b/testing/xpcshell/xpcshell.eslintrc new file mode 100644 index 0000000000..d4394fabc4 --- /dev/null +++ b/testing/xpcshell/xpcshell.eslintrc @@ -0,0 +1,41 @@ +// Parent config file for all xpcshell files. +{ + rules: { + "mozilla/import-headjs-globals": 1, + }, + + // All globals made available in the test environment. + "globals": { + "add_task": false, + "add_test": false, + "Assert": false, + "deepEqual": false, + "do_check_eq": false, + "do_check_false": false, + "do_check_neq": false, + "do_check_null": false, + "do_check_true": false, + "do_execute_soon": false, + "do_get_cwd": false, + "do_get_file": false, + "do_get_idle": false, + "do_get_profile": false, + "do_load_module": false, + "do_parse_document": false, + "do_print": false, + "do_register_cleanup": false, + "do_test_finished": false, + "do_test_pending": false, + "do_throw": false, + "do_timeout": false, + "equal": false, + "load": false, + "notDeepEqual": false, + "notEqual": false, + "notStrictEqual": false, + "ok": false, + "run_next_test": false, + "run_test": false, + "strictEqual": false, + } +} diff --git a/toolkit/components/aboutperformance/content/aboutPerformance.js b/toolkit/components/aboutperformance/content/aboutPerformance.js index b12d196567..1ab4df9fb6 100644 --- a/toolkit/components/aboutperformance/content/aboutPerformance.js +++ b/toolkit/components/aboutperformance/content/aboutPerformance.js @@ -13,328 +13,928 @@ const { AddonWatcher } = Cu.import("resource://gre/modules/AddonWatcher.jsm", {} const { PerformanceStats } = Cu.import("resource://gre/modules/PerformanceStats.jsm", {}); const { Services } = Cu.import("resource://gre/modules/Services.jsm", {}); const { Task } = Cu.import("resource://gre/modules/Task.jsm", {}); +const { ObjectUtils } = Cu.import("resource://gre/modules/ObjectUtils.jsm", {}); // about:performance observes notifications on this topic. // if a notification is sent, this causes the page to be updated immediately, // regardless of whether the page is on pause. -const UPDATE_IMMEDIATELY_TOPIC = "about:performance-update-immediately"; +const TEST_DRIVER_TOPIC = "test-about:performance-test-driver"; // about:performance posts notifications on this topic whenever the page // is updated. const UPDATE_COMPLETE_TOPIC = "about:performance-update-complete"; -/** - * The various measures we display. - */ -const MEASURES = [ - {probe: "jank", key: "longestDuration", percentOfDeltaT: false, label: "Jank level"}, - {probe: "jank", key: "totalUserTime", percentOfDeltaT: true, label: "User (%)"}, - {probe: "jank", key: "totalSystemTime", percentOfDeltaT: true, label: "System (%)"}, - {probe: "cpow", key: "totalCPOWTime", percentOfDeltaT: true, label: "Cross-Process (%)"}, - {probe: "ticks",key: "ticks", percentOfDeltaT: false, label: "Activations"}, -]; +// How often we should add a sample to our buffer. +const BUFFER_SAMPLING_RATE_MS = 1000; -/** - * Used to control the live updates in the performance page. - */ -let AutoUpdate = { +// The age of the oldest sample to keep. +const BUFFER_DURATION_MS = 10000; - /** - * The timer that is created when setInterval is called. - */ - _timerId: null, +// How often we should update +const UPDATE_INTERVAL_MS = 5000; - /** - * The dropdown DOM element. - */ - _intervalDropdown: null, +// The name of the application +const BRAND_BUNDLE = Services.strings.createBundle( + "chrome://branding/locale/brand.properties"); +const BRAND_NAME = BRAND_BUNDLE.GetStringFromName("brandShortName"); - /** - * Starts updating the performance data if the updates are paused. - */ - start: function () { - if (AutoUpdate._intervalDropdown == null){ - AutoUpdate._intervalDropdown = document.getElementById("intervalDropdown"); - } +// The maximal number of items to display before showing a "Show All" +// button. +const MAX_NUMBER_OF_ITEMS_TO_DISPLAY = 3; - if (AutoUpdate._timerId == null) { - let dropdownIndex = AutoUpdate._intervalDropdown.selectedIndex; - let dropdownValue = AutoUpdate._intervalDropdown.options[dropdownIndex].value; - AutoUpdate._timerId = window.setInterval(update, dropdownValue); +// If the frequency of alerts is below this value, +// we consider that the feature has no impact. +const MAX_FREQUENCY_FOR_NO_IMPACT = .05; +// If the frequency of alerts is above `MAX_FREQUENCY_FOR_NO_IMPACT` +// and below this value, we consider that the feature impacts the +// user rarely. +const MAX_FREQUENCY_FOR_RARE = .1; +// If the frequency of alerts is above `MAX_FREQUENCY_FOR_FREQUENT` +// and below this value, we consider that the feature impacts the +// user frequently. Anything above is consider permanent. +const MAX_FREQUENCY_FOR_FREQUENT = .5; + +// If the number of high-impact alerts among all alerts is above +// this value, we consider that the feature has a major impact +// on user experience. +const MIN_PROPORTION_FOR_MAJOR_IMPACT = .05; +// Otherwise and if the number of medium-impact alerts among all +// alerts is above this value, we consider that the feature has +// a noticeable impact on user experience. +const MIN_PROPORTION_FOR_NOTICEABLE_IMPACT = .1; + +// The current mode. Either `MODE_GLOBAL` to display a summary of results +// since we opened about:performance or `MODE_RECENT` to display the latest +// BUFFER_DURATION_MS ms. +const MODE_GLOBAL = "global"; +const MODE_RECENT = "recent"; + +let tabFinder = { + update: function() { + this._map = new Map(); + let windows = Services.wm.getEnumerator("navigator:browser"); + while (windows.hasMoreElements()) { + let win = windows.getNext(); + let tabbrowser = win.gBrowser; + for (let browser of tabbrowser.browsers) { + let id = browser.outerWindowID; // May be `null` if the browser isn't loaded yet + if (id != null) { + this._map.set(id, browser); + } + } } }, /** - * Stops the updates if the data is updating. + * Find the for a window id. + * + * This is useful e.g. for reloading or closing tabs. + * + * @return null If the xul:tab could not be found, e.g. if the + * windowId is that of a chrome window. + * @return {{tabbrowser: , tab: }} The + * tabbrowser and tab if the latter could be found. */ - stop: function () { - if (AutoUpdate._timerId == null) { - return; + get: function(id) { + let browser = this._map.get(id); + if (!browser) { + return null; } - clearInterval(AutoUpdate._timerId); - AutoUpdate._timerId = null; + let tabbrowser = browser.getTabBrowser(); + return {tabbrowser, tab:tabbrowser.getTabForBrowser(browser)}; }, - /** - * Updates the refresh interval when the dropdown selection is changed. - */ - updateRefreshRate: function () { - AutoUpdate.stop(); - AutoUpdate.start(); + getAny: function(ids) { + for (let id of ids) { + let result = this.get(id); + if (result) { + return result; + } + } + return null; } - }; -let State = { +function wait(ms = 0) { + return new Promise(resolve => setTimeout(resolve, ms)); +} + +/** + * The performance of a webpage or an add-on between two instants. + * + * Clients should call `promiseInit()` before using the methods of this object. + * + * @param {PerformanceDiff} The underlying performance data. + * @param {"addons"|"webpages"} The kind of delta represented by this object. + * @param {Map} ageMap A map containing the oldest known + * appearance of each groupId, used to determine how long we have been monitoring + * this item. + * @param {Map} alertMap A map containing the alerts that each + * item has already triggered in the past. + */ +function Delta(diff, kind, snapshotDate, ageMap, alertMap) { + if (kind != "addons" && kind != "webpages") { + throw new TypeError(`Unknown kind: ${kind}`); + } + + /** + * Either "addons" or "webpages". + */ + this.kind = kind; + + /** + * The underlying PerformanceDiff. + * @type {PerformanceDiff} + */ + this.diff = diff; + + /** + * A key unique to the item (webpage or add-on), shared by successive + * instances of `Delta`. + * @type{string} + */ + this.key = kind + diff.key; + + // Find the oldest occurrence of this item. + let creationDate = snapshotDate; + for (let groupId of diff.groupIds) { + let date = ageMap.get(groupId); + if (date && date <= creationDate) { + creationDate = date; + } + } + + /** + * The timestamp at which the data was measured. + */ + this.creationDate = creationDate; + + /** + * Number of milliseconds since the start of the measure. + */ + this.age = snapshotDate - creationDate; + + /** + * A UX-friendly, human-readable name for this item. + */ + this.readableName = null; + + /** + * A complete name, possibly useful for power users or debugging. + */ + this.fullName = null; + + + // `true` once initialization is complete. + this._initialized = false; + // `true` if this item should be displayed + this._show = false; + + /** + * All the alerts that this item has caused since about:performance + * was opened. + */ + this.alerts = (alertMap.get(this.key) || []).slice(); + switch (this.slowness) { + case 0: break; + case 1: this.alerts[0] = (this.alerts[0] || 0) + 1; break; + case 2: this.alerts[1] = (this.alerts[1] || 0) + 1; break; + default: throw new Error(); + } +} +Delta.prototype = { + /** + * `true` if this item should be displayed, `false` otherwise. + */ + get show() { + this._ensureInitialized(); + return this._show; + }, + + /** + * Estimate the slowness of this item. + * + * @return 0 if the item has good performance. + * @return 1 if the item has average performance. + * @return 2 if the item has poor performance. + */ + get slowness() { + if (Delta.compare(this, Delta.MAX_DELTA_FOR_GOOD_RECENT_PERFORMANCE) <= 0) { + return 0; + } + if (Delta.compare(this, Delta.MAX_DELTA_FOR_AVERAGE_RECENT_PERFORMANCE) <= 0) { + return 1; + } + return 2; + }, + _ensureInitialized() { + if (!this._initialized) { + throw new Error(); + } + }, + + /** + * Initialize, asynchronously. + */ + promiseInit: function() { + if (this.kind == "webpages") { + return this._initWebpage(); + } else if (this.kind == "addons") { + return this._promiseInitAddon(); + } + throw new TypeError(); + }, + _initWebpage: function() { + this._initialized = true; + let found = tabFinder.getAny(this.diff.windowIds); + if (!found || found.tab.linkedBrowser.contentTitle == null) { + // Either this is not a real page or the page isn't restored yet. + return; + } + + this.readableName = found.tab.linkedBrowser.contentTitle; + this.fullName = this.diff.names.join(", "); + this._show = true; + }, + _promiseInitAddon: Task.async(function*() { + let found = yield (new Promise(resolve => + AddonManager.getAddonByID(this.diff.addonId, a => { + if (a) { + this.readableName = a.name; + resolve(true); + } else { + resolve(false); + } + }))); + + this._initialized = true; + + // If the add-on manager doesn't know about an add-on, it's + // probably not a real add-on. + this._show = found; + this.fullName = this.diff.addonId; + }), + toString: function() { + return `[Delta] ${this.diff.key} => ${this.readableName}, ${this.fullName}`; + } +}; + +Delta.compare = function(a, b) { + return ( + (a.diff.jank.longestDuration - b.diff.jank.longestDuration) || + (a.diff.jank.totalUserTime - b.diff.jank.totalUserTime) || + (a.diff.jank.totalSystemTime - b.diff.jank.totalSystemTime) || + (a.diff.cpow.totalCPOWTime - b.diff.cpow.totalCPOWTime) || + (a.diff.ticks.ticks - b.diff.ticks.ticks) || + 0 + ); +}; + +Delta.revCompare = function(a, b) { + return -Delta.compare(a, b); +}; + +/** + * The highest value considered "good performance". + */ +Delta.MAX_DELTA_FOR_GOOD_RECENT_PERFORMANCE = { + diff: { + cpow: { + totalCPOWTime: 0, + }, + jank: { + longestDuration: 3, + totalUserTime: Number.POSITIVE_INFINITY, + totalSystemTime: Number.POSITIVE_INFINITY + }, + ticks: { + ticks: Number.POSITIVE_INFINITY, + } + } +}; + +/** + * The highest value considered "average performance". + */ +Delta.MAX_DELTA_FOR_AVERAGE_RECENT_PERFORMANCE = { + diff: { + cpow: { + totalCPOWTime: Number.POSITIVE_INFINITY, + }, + jank: { + longestDuration: 7, + totalUserTime: Number.POSITIVE_INFINITY, + totalSystemTime: Number.POSITIVE_INFINITY + }, + ticks: { + ticks: Number.POSITIVE_INFINITY, + } + } +}; + +/** + * Utilities for dealing with state + */ +var State = { _monitor: PerformanceStats.getMonitor([ "jank", "cpow", "ticks", - "jank-content", "cpow-content", "ticks-content", ]), /** - * @type{PerformanceData} - */ - _processData: null, - /** - * A mapping from name to PerformanceData + * Indexed by the number of minutes since the snapshot was taken. * - * @type{Map} + * @type {Array} */ - _componentsData: new Map(), + _buffer: [], + /** + * The first snapshot since opening the page. + * + * @type ApplicationSnapshot + */ + _oldest: null, /** - * A number of milliseconds since the high-performance epoch. + * The latest snapshot. + * + * @type ApplicationSnapshot */ - _date: window.performance.now(), + _latest: null, /** - * Fetch the latest information, compute diffs. + * The performance alerts for each group. + * + * This map is cleaned up during each update to avoid leaking references + * to groups that have been gc-ed. + * + * @type{Map} A map in which the keys are provided + * by property `key` of instances of `Delta` and the values are arrays + * [number of moderate-impact alerts, number of high-impact alerts] + */ + _alerts: new Map(), + + /** + * The date at which each group was first seen. + * + * This map is cleaned up during each update to avoid leaking references + * to groups that have been gc-ed. + * + * @type{Map setTimeout(resolve, BUFFER_SAMPLING_RATE_MS * 1.1)); } - State._componentsData = newData; - let now = window.performance.now(); - let process = snapshot.processData.subtract(State._processData); + + + let now = Cu.now(); + + // If we haven't sampled in a while, add a sample to the buffer. + let latestInBuffer = this._buffer[this._buffer.length - 1]; + let deltaT = now - latestInBuffer.date; + if (deltaT > BUFFER_SAMPLING_RATE_MS) { + this._latest = yield this._monitor.promiseSnapshot(); + this._buffer.push(this._latest); + } + + // If we have too many samples, remove the oldest sample. + let oldestInBuffer = this._buffer[0]; + if (oldestInBuffer.date + BUFFER_DURATION_MS < this._latest.date) { + this._buffer.shift(); + } + }), + + /** + * @return {Promise} + */ + promiseDeltaSinceStartOfTime: function() { + return this._promiseDeltaSince(this._oldest); + }, + + /** + * @return {Promise} + */ + promiseDeltaSinceStartOfBuffer: function() { + return this._promiseDeltaSince(this._buffer[0]); + }, + + /** + * @return {Promise} + * @resolve {{ + * addons: Array, + * webpages: Array, + * deltas: Set, + * duration: number of milliseconds + * }} + */ + _promiseDeltaSince: Task.async(function*(oldest) { + let current = this._latest; + if (!oldest) { + throw new TypeError(); + } + if (!current) { + throw new TypeError(); + } + + tabFinder.update(); + // We rebuild the maps during each iteration to make sure that + // we do not maintain references to groups that has been removed + // (e.g. pages that have been closed). + let oldFirstSeen = this._firstSeen; + let cleanedUpFirstSeen = new Map(); + + let oldAlerts = this._alerts; + let cleanedUpAlerts = new Map(); + let result = { - components: deltas.filter(x => x.ticks.ticks > 0), - process: snapshot.processData.subtract(State._processData), - deltaT: now - State._date + addons: [], + webpages: [], + deltas: new Set(), + duration: current.date - oldest.date }; - result.components.sort((a, b) => { - if (a.longestDuration < b.longestDuration) { - return true; + + for (let kind of ["webpages", "addons"]) { + for (let [key, value] of current[kind]) { + let item = ObjectUtils.strict(new Delta(value.subtract(oldest[kind].get(key)), kind, current.date, oldFirstSeen, oldAlerts)); + yield item.promiseInit(); + + if (!item.show) { + continue; + } + result[kind].push(item); + result.deltas.add(item.key); + + for (let groupId of item.diff.groupIds) { + cleanedUpFirstSeen.set(groupId, item.creationDate); + } + cleanedUpAlerts.set(item.key, item.alerts); } - if (a.longestDuration == b.longestDuration) { - return a.totalUserTime <= b.totalUserTime - } - return false; - }); - State._processData = snapshot.processData; - State._date = now; + } + + this._firstSeen = cleanedUpFirstSeen; + this._alerts = cleanedUpAlerts; return result; - }) + }), }; - -let update = Task.async(function*() { - yield updateLiveData(); - yield updateSlowAddons(); - Services.obs.notifyObservers(null, UPDATE_COMPLETE_TOPIC, ""); -}); - -/** - * Update the list of slow addons - */ -let updateSlowAddons = Task.async(function*() { - try { - let data = AddonWatcher.alerts; - if (data.size == 0) { - // Nothing to display. - return; - } - let alerts = 0; - for (let [addonId, details] of data) { - for (let k of Object.keys(details.alerts)) { - alerts += details.alerts[k]; +var View = { + /** + * A cache for all the per-item DOM elements that are reused across refreshes. + * + * Reusing the same elements means that elements that were hidden (respectively + * visible) in an iteration remain hidden (resp visible) in the next iteration. + */ + DOMCache: { + _map: new Map(), + /** + * @param {string} deltaKey The key for the item that we are displaying. + * @return {null} If the `deltaKey` doesn't have a component cached yet. + * Otherwise, the value stored with `set`. + */ + get: function(deltaKey) { + return this._map.get(deltaKey); + }, + set: function(deltaKey, value) { + this._map.set(deltaKey, value); + }, + /** + * Remove all the elements whose key does not appear in `set`. + * + * @param {Set} set a set of deltaKey. + */ + trimTo: function(set) { + let remove = []; + for (let key of this._map.keys()) { + if (!set.has(key)) { + remove.push(key); + } + } + for (let key of remove) { + this._map.delete(key); } } + }, + /** + * Display the items in a category. + * + * @param {Array} subset The items to display. They will + * be displayed in the order of `subset`. + * @param {string} id The id of the DOM element that will contain the items. + * @param {string} nature The nature of the subset. One of "addons", "webpages" or "system". + * @param {string} currentMode The current display mode. One of MODE_GLOBAL or MODE_RECENT. + */ + updateCategory: function(subset, id, nature, currentMode) { + subset = subset.slice().sort(Delta.revCompare); - if (!alerts) { - // Still nothing to display. - return; + let watcherAlerts = null; + if (nature == "addons") { + watcherAlerts = AddonWatcher.alerts; } + // Grab everything from the DOM before cleaning up + let eltContainer = this._setupStructure(id); - let elData = document.getElementById("slowAddonsList"); - elData.innerHTML = ""; - let elTable = document.createElement("table"); - elData.appendChild(elTable); - - // Generate header - let elHeader = document.createElement("tr"); - elTable.appendChild(elHeader); - for (let name of [ - "Alerts", - "Jank level alerts", - "(highest jank)", - "Cross-Process alerts", - "(highest CPOW)" - ]) { - let elName = document.createElement("td"); - elName.textContent = name; - elHeader.appendChild(elName); - elName.classList.add("header"); - } - for (let [addonId, details] of data) { - let elAddon = document.createElement("tr"); - - // Display the number of occurrences of each alerts - let elTotal = document.createElement("td"); - let total = 0; - for (let k of Object.keys(details.alerts)) { - total += details.alerts[k]; + // An array of `cachedElements` that need to be added + let toAdd = []; + for (let delta of subset) { + if (!(delta instanceof Delta)) { + throw new TypeError(); } - elTotal.textContent = total; - elAddon.appendChild(elTotal); + let cachedElements = this._grabOrCreateElements(delta, nature); + toAdd.push(cachedElements); + cachedElements.eltTitle.textContent = delta.readableName; + cachedElements.eltName.textContent = `Full name: ${delta.fullName}.`; + cachedElements.eltLoaded.textContent = `Measure start: ${Math.round(delta.age/1000)} seconds ago.` - for (let filter of ["longestDuration", "totalCPOWTime"]) { - for (let stat of ["alerts", "peaks"]) { - let el = document.createElement("td"); - el.textContent = details[stat][filter] || 0; - elAddon.appendChild(el); + let processes = delta.diff.processes.map(proc => `${proc.processId} (${proc.isChildProcess?"child":"parent"})`); + cachedElements.eltProcess.textContent = `Processes: ${processes.join(", ")}`; + let jankSuffix = ""; + if (watcherAlerts) { + let deltaAlerts = watcherAlerts.get(delta.diff.addonId); + if (deltaAlerts) { + if (deltaAlerts.occurrences) { + jankSuffix = ` (${deltaAlerts.occurrences} alerts)`; + } } } - // Display the name of the add-on - let elName = document.createElement("td"); - elAddon.appendChild(elName); - AddonManager.getAddonByID(addonId, a => { - elName.textContent = a ? a.name : addonId + let eltImpact = cachedElements.eltImpact; + if (currentMode == MODE_RECENT) { + cachedElements.eltRoot.setAttribute("impact", delta.diff.jank.longestDuration + 1); + if (Delta.compare(delta, Delta.MAX_DELTA_FOR_GOOD_RECENT_PERFORMANCE) <= 0) { + eltImpact.textContent = ` currently performs well.`; + } else if (Delta.compare(delta, Delta.MAX_DELTA_FOR_AVERAGE_RECENT_PERFORMANCE)) { + eltImpact.textContent = ` may currently be slowing down ${BRAND_NAME}.`; + } else { + eltImpact.textContent = ` is currently considerably slowing down ${BRAND_NAME}.`; + } + + cachedElements.eltFPS.textContent = `Impact on framerate: ${delta.diff.jank.longestDuration + 1}/${delta.diff.jank.durations.length}${jankSuffix}.`; + cachedElements.eltCPU.textContent = `CPU usage: ${Math.ceil(delta.diff.jank.totalCPUTime/delta.diff.deltaT)}%.`; + cachedElements.eltSystem.textContent = `System usage: ${Math.ceil(delta.diff.jank.totalSystemTime/delta.diff.deltaT)}%.`; + cachedElements.eltCPOW.textContent = `Blocking process calls: ${Math.ceil(delta.diff.cpow.totalCPOWTime/delta.diff.deltaT)}%.`; + } else { + if (delta.alerts.length == 0) { + eltImpact.textContent = " has performed well so far."; + cachedElements.eltFPS.textContent = `Impact on framerate: no impact.`; + cachedElements.eltRoot.setAttribute("impact", 0); + } else { + let impact = 0; + let sum = /* medium impact */ delta.alerts[0] + /* high impact */ delta.alerts[1]; + let frequency = sum * 1000 / delta.diff.deltaT; + + let describeFrequency; + if (frequency <= MAX_FREQUENCY_FOR_NO_IMPACT) { + describeFrequency = `has no impact on the performance of ${BRAND_NAME}.` + } else { + let describeImpact; + if (frequency <= MAX_FREQUENCY_FOR_RARE) { + describeFrequency = `rarely slows down ${BRAND_NAME}.`; + impact += 1; + } else if (frequency <= MAX_FREQUENCY_FOR_FREQUENT) { + describeFrequency = `has slown down ${BRAND_NAME} frequently.`; + impact += 2.5; + } else { + describeFrequency = `seems to have slown down ${BRAND_NAME} very often.`; + impact += 5; + } + // At this stage, `sum != 0` + if (delta.alerts[1] / sum > MIN_PROPORTION_FOR_MAJOR_IMPACT) { + describeImpact = "When this happens, the slowdown is generally important." + impact *= 2; + } else { + describeImpact = "When this happens, the slowdown is generally noticeable." + } + + eltImpact.textContent = ` ${describeFrequency} ${describeImpact}`; + cachedElements.eltFPS.textContent = `Impact on framerate: ${delta.alerts[1] || 0} high-impacts, ${delta.alerts[0] || 0} medium-impact${jankSuffix}.`; + } + cachedElements.eltRoot.setAttribute("impact", Math.round(impact)); + } + + let result = delta.diff.jank.totalCPUTime/delta.diff.deltaT; + cachedElements.eltCPU.textContent = `CPU usage: ${Math.ceil(delta.diff.jank.totalCPUTime/delta.diff.deltaT)}% (total ${delta.diff.jank.totalUserTime}ms).`; + cachedElements.eltSystem.textContent = `System usage: ${Math.ceil(delta.diff.jank.totalSystemTime/delta.diff.deltaT)}% (total ${delta.diff.jank.totalSystemTime}ms).`; + cachedElements.eltCPOW.textContent = `Blocking process calls: ${Math.ceil(delta.diff.cpow.totalCPOWTime/delta.diff.deltaT)}% (total ${delta.diff.cpow.totalCPOWTime}ms).`; + } + } + this._insertElements(toAdd, id); + }, + + _insertElements: function(elements, id) { + let eltContainer = document.getElementById(id); + eltContainer.classList.remove("measuring"); + eltContainer.eltVisibleContent.innerHTML = ""; + eltContainer.eltHiddenContent.innerHTML = ""; + eltContainer.appendChild(eltContainer.eltShowMore); + + for (let i = 0; i < elements.length && i < MAX_NUMBER_OF_ITEMS_TO_DISPLAY; ++i) { + let cachedElements = elements[i]; + eltContainer.eltVisibleContent.appendChild(cachedElements.eltRoot); + } + for (let i = MAX_NUMBER_OF_ITEMS_TO_DISPLAY; i < elements.length; ++i) { + let cachedElements = elements[i]; + eltContainer.eltHiddenContent.appendChild(cachedElements.eltRoot); + } + if (elements.length <= MAX_NUMBER_OF_ITEMS_TO_DISPLAY) { + eltContainer.eltShowMore.classList.add("hidden"); + } else { + eltContainer.eltShowMore.classList.remove("hidden"); + } + if (elements.length == 0) { + eltContainer.textContent = "Nothing"; + } + }, + _setupStructure: function(id) { + let eltContainer = document.getElementById(id); + if (!eltContainer.eltVisibleContent) { + eltContainer.eltVisibleContent = document.createElement("ul"); + eltContainer.eltVisibleContent.classList.add("visible_items"); + eltContainer.appendChild(eltContainer.eltVisibleContent); + } + if (!eltContainer.eltHiddenContent) { + eltContainer.eltHiddenContent = document.createElement("ul"); + eltContainer.eltHiddenContent.classList.add("hidden"); + eltContainer.eltHiddenContent.classList.add("hidden_additional_items"); + eltContainer.appendChild(eltContainer.eltHiddenContent); + } + if (!eltContainer.eltShowMore) { + eltContainer.eltShowMore = document.createElement("button"); + eltContainer.eltShowMore.textContent = "Show all"; + eltContainer.eltShowMore.classList.add("show_all_items"); + eltContainer.appendChild(eltContainer.eltShowMore); + eltContainer.eltShowMore.addEventListener("click", function() { + if (eltContainer.eltHiddenContent.classList.contains("hidden")) { + eltContainer.eltHiddenContent.classList.remove("hidden"); + eltContainer.eltShowMore.textContent = "Hide"; + } else { + eltContainer.eltHiddenContent.classList.add("hidden"); + eltContainer.eltShowMore.textContent = "Show all"; + } + }); + } + return eltContainer; + }, + + _grabOrCreateElements: function(delta, nature) { + let cachedElements = this.DOMCache.get(delta.key); + if (cachedElements) { + if (cachedElements.eltRoot.parentElement) { + cachedElements.eltRoot.parentElement.removeChild(cachedElements.eltRoot); + } + } else { + this.DOMCache.set(delta.key, cachedElements = {}); + + let eltDelta = document.createElement("li"); + eltDelta.classList.add("delta"); + cachedElements.eltRoot = eltDelta; + + let eltSpan = document.createElement("span"); + eltDelta.appendChild(eltSpan); + + let eltSummary = document.createElement("span"); + eltSummary.classList.add("summary"); + eltSpan.appendChild(eltSummary); + + let eltTitle = document.createElement("span"); + eltTitle.classList.add("title"); + eltSummary.appendChild(eltTitle); + cachedElements.eltTitle = eltTitle; + + let eltImpact = document.createElement("span"); + eltImpact.classList.add("impact"); + eltSummary.appendChild(eltImpact); + cachedElements.eltImpact = eltImpact; + + let eltShowMore = document.createElement("a"); + eltShowMore.classList.add("more"); + eltSpan.appendChild(eltShowMore); + eltShowMore.textContent = "more"; + eltShowMore.href = ""; + eltShowMore.addEventListener("click", () => { + if (eltDetails.classList.contains("hidden")) { + eltDetails.classList.remove("hidden"); + eltShowMore.textContent = "less"; + } else { + eltDetails.classList.add("hidden"); + eltShowMore.textContent = "more"; + } }); - elTable.appendChild(elAddon); - } - } catch (ex) { - console.error(ex); - } -}); + // Add buttons + if (nature == "addons") { + eltSpan.appendChild(document.createElement("br")); + let eltDisable = document.createElement("button"); + eltDisable.textContent = "Disable"; + eltSpan.appendChild(eltDisable); -/** - * Update the table of live data. - */ -let updateLiveData = Task.async(function*() { - try { - let dataElt = document.getElementById("liveData"); - dataElt.innerHTML = ""; + let eltUninstall = document.createElement("button"); + eltUninstall.textContent = "Uninstall"; + eltSpan.appendChild(eltUninstall); - // Generate table headers - let headerElt = document.createElement("tr"); - dataElt.appendChild(headerElt); - headerElt.classList.add("header"); - for (let column of [...MEASURES, {key: "name", name: ""}, {key: "process", name: ""}]) { - let el = document.createElement("td"); - el.classList.add(column.key); - el.textContent = column.label; - headerElt.appendChild(el); - } + let eltRestart = document.createElement("button"); + eltRestart.textContent = `Restart ${BRAND_NAME} to apply your changes.` + eltRestart.classList.add("hidden"); + eltSpan.appendChild(eltRestart); - let deltas = yield State.update(); - - for (let item of [deltas.process, ...deltas.components]) { - let row = document.createElement("tr"); - if (item.addonId) { - row.classList.add("addon"); - } else if (item.isSystem) { - row.classList.add("platform"); - } else { - row.classList.add("content"); - } - dataElt.appendChild(row); - - // Measures - for (let {probe, key, percentOfDeltaT} of MEASURES) { - let el = document.createElement("td"); - el.classList.add(key); - el.classList.add("contents"); - row.appendChild(el); - - let rawValue = item[probe][key]; - let value = percentOfDeltaT ? Math.round(rawValue / deltas.deltaT) : rawValue; - if (key == "longestDuration") { - value += 1; - el.classList.add("jank" + value); - } - el.textContent = value; - } - - { - // Name - let el = document.createElement("td"); - let id = item.id; - el.classList.add("contents"); - el.classList.add("name"); - row.appendChild(el); - if (item.addonId) { - let _el = el; - let _item = item; - AddonManager.getAddonByID(item.addonId, a => { - _el.textContent = a ? a.name : _item.name + eltRestart.addEventListener("click", () => { + Services.startup.quit(Services.startup.eForceQuit | Services.startup.eRestart); + }); + AddonManager.getAddonByID(delta.diff.addonId, addon => { + eltDisable.addEventListener("click", () => { + addon.userDisabled = true; + if (addon.pendingOperations == addon.PENDING_NONE) { + // Restartless add-on is now disabled. + return; + } + eltDisable.classList.add("hidden"); + eltUninstall.classList.add("hidden"); + eltRestart.classList.remove("hidden"); }); - } else { - el.textContent = item.title || item.name; - } + + eltUninstall.addEventListener("click", () => { + addon.uninstall(); + if (addon.pendingOperations == addon.PENDING_NONE) { + // Restartless add-on is now disabled. + return; + } + eltDisable.classList.add("hidden"); + eltUninstall.classList.add("hidden"); + eltRestart.classList.remove("hidden"); + }); + }); + } else if (nature == "webpages") { + eltSpan.appendChild(document.createElement("br")); + + let eltCloseTab = document.createElement("button"); + eltCloseTab.textContent = "Close tab"; + eltSpan.appendChild(eltCloseTab); + let windowIds = delta.diff.windowIds; + eltCloseTab.addEventListener("click", () => { + let found = tabFinder.getAny(windowIds); + if (!found) { + // Cannot find the tab. Maybe it is closed already? + return; + } + let {tabbrowser, tab} = found; + tabbrowser.removeTab(tab); + }); + + let eltReloadTab = document.createElement("button"); + eltReloadTab.textContent = "Reload tab"; + eltSpan.appendChild(eltReloadTab); + eltReloadTab.addEventListener("click", () => { + let found = tabFinder.getAny(windowIds); + if (!found) { + // Cannot find the tab. Maybe it is closed already? + return; + } + let {tabbrowser, tab} = found; + tabbrowser.reloadTab(tab); + }); } - { - // Process information. - let el = document.createElement("td"); - el.classList.add("contents"); - el.classList.add("process"); - row.appendChild(el); - if (item.isChildProcess) { - el.textContent = "(child)"; - row.classList.add("child"); - } else { - el.textContent = "(parent)"; - row.classList.add("parent"); - } + // Prepare details + let eltDetails = document.createElement("ul"); + eltDetails.classList.add("details"); + eltDetails.classList.add("hidden"); + eltSpan.appendChild(eltDetails); + + for (let [name, className] of [ + ["eltName", "name"], + ["eltFPS", "fps"], + ["eltCPU", "cpu"], + ["eltSystem", "system"], + ["eltCPOW", "cpow"], + ["eltLoaded", "loaded"], + ["eltProcess", "process"], + ]) { + let elt = document.createElement("li"); + elt.classList.add(className); + eltDetails.appendChild(elt); + cachedElements[name] = elt; } } - } catch (ex) { - console.error(ex); - } + + return cachedElements; + }, +}; + +var Control = { + init: function() { + this._initAutorefresh(); + this._initDisplayMode(); + }, + update: Task.async(function*() { + let mode = this._displayMode; + if (this._autoRefreshInterval || !State._buffer[0]) { + // Update the state only if we are not on pause. + yield State.update(); + } + yield wait(0); + let state = yield (mode == MODE_GLOBAL? + State.promiseDeltaSinceStartOfTime(): + State.promiseDeltaSinceStartOfBuffer()); + + for (let category of ["webpages", "addons"]) { + yield wait(0); + yield View.updateCategory(state[category], category, category, mode); + } + yield wait(0); + + // Make sure that we do not keep obsolete stuff around. + View.DOMCache.trimTo(state.deltas); + + yield wait(0); + + // Inform watchers + Services.obs.notifyObservers(null, UPDATE_COMPLETE_TOPIC, mode); + }), + _setOptions: function(options) { + dump(`about:performance _setOptions ${JSON.stringify(options)}\n`); + let eltRefresh = document.getElementById("check-autorefresh"); + if ((options.autoRefresh > 0) != eltRefresh.checked) { + eltRefresh.click(); + } + let eltCheckRecent = document.getElementById("check-display-recent"); + if (!!options.displayRecent != eltCheckRecent.checked) { + eltCheckRecent.click(); + } + }, + _initAutorefresh: function() { + let onRefreshChange = (shouldUpdateNow = false) => { + if (eltRefresh.checked == !!this._autoRefreshInterval) { + // Nothing to change. + return; + } + if (eltRefresh.checked) { + this._autoRefreshInterval = window.setInterval(() => Control.update(), UPDATE_INTERVAL_MS); + if (shouldUpdateNow) { + Control.update(); + } + } else { + window.clearInterval(this._autoRefreshInterval); + this._autoRefreshInterval = null; + } + } + + let eltRefresh = document.getElementById("check-autorefresh"); + eltRefresh.addEventListener("change", () => onRefreshChange(true)); + + onRefreshChange(false); + }, + _autoRefreshInterval: null, + _initDisplayMode: function() { + let onModeChange = (shouldUpdateNow) => { + if (eltCheckRecent.checked) { + this._displayMode = MODE_RECENT; + } else { + this._displayMode = MODE_GLOBAL; + } + if (shouldUpdateNow) { + Control.update(); + } + }; + + let eltCheckRecent = document.getElementById("check-display-recent"); + let eltLabelRecent = document.getElementById("label-display-recent"); + eltCheckRecent.addEventListener("click", () => onModeChange(true)); + eltLabelRecent.textContent = `Display only the latest ${Math.round(BUFFER_DURATION_MS/1000)}s`; + + onModeChange(false); + }, + // The display mode. One of `MODE_GLOBAL` or `MODE_RECENT`. + _displayMode: MODE_GLOBAL, +}; + +var go = Task.async(function*() { + Control.init(); + + // Setup a hook to allow tests to configure and control this page + let testUpdate = function(subject, topic, value) { + let options = JSON.parse(value); + Control._setOptions(options); + Control.update(); + }; + Services.obs.addObserver(testUpdate, TEST_DRIVER_TOPIC, false); + window.addEventListener("unload", () => Services.obs.removeObserver(testUpdate, TEST_DRIVER_TOPIC)); + + yield Control.update(); + yield new Promise(resolve => setTimeout(resolve, BUFFER_SAMPLING_RATE_MS * 1.1)); + yield Control.update(); }); - -function go() { - document.getElementById("playButton").addEventListener("click", () => AutoUpdate.start()); - document.getElementById("pauseButton").addEventListener("click", () => AutoUpdate.stop()); - - document.getElementById("intervalDropdown").addEventListener("change", () => AutoUpdate.updateRefreshRate()); - - - // Compute initial state immediately, then wait a little - // before we start computing diffs and refreshing. - State.update(); - window.setTimeout(update, 500); - - let observer = update; - - Services.obs.addObserver(update, UPDATE_IMMEDIATELY_TOPIC, false); - window.addEventListener("unload", () => Services.obs.removeObserver(update, UPDATE_IMMEDIATELY_TOPIC)); -} diff --git a/toolkit/components/aboutperformance/content/aboutPerformance.xhtml b/toolkit/components/aboutperformance/content/aboutPerformance.xhtml index e13aa07b13..048b0905c1 100644 --- a/toolkit/components/aboutperformance/content/aboutPerformance.xhtml +++ b/toolkit/components/aboutperformance/content/aboutPerformance.xhtml @@ -9,101 +9,105 @@ about:performance - -

Performance monitor

- - - - - -
- -

Slow add-ons alerts

-
- (none) +
+ + + + +
+
+

Performance of Add-ons

+
+
+
+
+

Performance of Web pages

+
+
- diff --git a/toolkit/components/aboutperformance/tests/browser/.eslintrc b/toolkit/components/aboutperformance/tests/browser/.eslintrc new file mode 100644 index 0000000000..be7c552e0c --- /dev/null +++ b/toolkit/components/aboutperformance/tests/browser/.eslintrc @@ -0,0 +1,5 @@ +{ + "extends": [ + "../../../../../testing/mochitest/browser.eslintrc" + ] +} diff --git a/toolkit/components/aboutperformance/tests/browser/browser_aboutperformance.js b/toolkit/components/aboutperformance/tests/browser/browser_aboutperformance.js index eb48f0437e..ac17c0a0a5 100644 --- a/toolkit/components/aboutperformance/tests/browser/browser_aboutperformance.js +++ b/toolkit/components/aboutperformance/tests/browser/browser_aboutperformance.js @@ -19,64 +19,273 @@ function frameScript() { content.document.title = title; sendAsyncMessage("aboutperformance-test:setTitle", null); }); - - addMessageListener("aboutperformance-test:hasItems", ({data: title}) => { - let observer = function() { + + addMessageListener("aboutperformance-test:closeTab", ({data: options}) => { + let observer = function(subject, topic, mode) { + dump(`aboutperformance-test:closeTab 1 ${options.url}\n`); Services.obs.removeObserver(observer, "about:performance-update-complete"); - let hasPlatform = false; - let hasTitle = false; + + let exn; + let found = false; + try { + for (let eltContent of content.document.querySelectorAll("li.delta")) { + let eltName = eltContent.querySelector("li.name"); + if (!eltName.textContent.includes(options.url)) { + continue; + } + + found = true; + let [eltCloseTab, eltReloadTab] = eltContent.querySelectorAll("button"); + let button; + if (options.mode == "reload") { + button = eltReloadTab; + } else if (options.mode == "close") { + button = eltCloseTab; + } else { + throw new TypeError(options.mode); + } + dump(`aboutperformance-test:closeTab clicking on ${button.textContent}\n`); + button.click(); + return; + } + } catch (ex) { + dump(`aboutperformance-test:closeTab: error ${ex}\n`); + exn = ex; + } finally { + if (exn) { + sendAsyncMessage("aboutperformance-test:closeTab", { error: {message: exn.message, lineNumber: exn.lineNumber, fileName: exn.fileName}, found}); + } else { + sendAsyncMessage("aboutperformance-test:closeTab", { ok: true, found }); + } + } + } + Services.obs.addObserver(observer, "about:performance-update-complete", false); + Services.obs.notifyObservers(null, "test-about:performance-test-driver", JSON.stringify(options)); + }); + + addMessageListener("aboutperformance-test:checkSanity", ({data: options}) => { + let exn = null; + try { + let reFullname = /Full name: (.+)/; + let reFps = /Impact on framerate: (\d+)\/10( \((\d+) alerts\))?/; + let reCpu = /CPU usage: (\d+)%/ + let reCpow = /Blocking process calls: (\d+)%( \((\d+) alerts\))?/; + + let getContentOfSelector = function(eltContainer, selector, re) { + let elt = eltContainer.querySelector(selector); + if (!elt) { + throw new Error(`No item ${selector}`); + } + + if (!re) { + return undefined; + } + + let match = elt.textContent.match(re); + if (!match) { + throw new Error(`Item ${selector} doesn't match regexp ${re}: ${elt.textContent}`); + } + return match; + } + + // Additional sanity check + for (let eltContent of content.document.querySelectorAll("delta")) { + // Do we have an attribute "impact"? Is it a number between 0 and 10? + let impact = eltContent.classList.getAttribute("impact"); + let value = Number.parseInt(impact); + if (isNaN(value) || value < 0 || value > 10) { + throw new Error(`Incorrect value ${value}`); + } + + // Do we have a button "more"? + getContentOfSelector(eltContent, "a.more"); + + // Do we have details? + getContentOfSelector(eltContent, "ul.details"); + + // Do we have a full name? Does it make sense? + getContentOfSelector(eltContent, "li.name", reFullname); + + // Do we have an impact on framerate? Does it make sense? + let [, jankStr,, alertsStr] = getContentOfSelector(eltDetails, "li.fps", reFps); + let jank = Number.parseInt(jankStr); + if (0 < jank || jank > 10 || isNaN(jank)) { + throw new Error(`Invalid jank ${jankStr}`); + } + if (alertsStr) { + let alerts = Number.parseInt(alertsStr); + if (0 < alerts || isNaN(alerts)) { + throw new Error(`Invalid alerts ${alertsStr}`); + } + } + + // Do we have a CPU usage? Does it make sense? + let [, cpuStr] = getContentOfSelector(eltDetails, "li.cpu", reCPU); + let cpu = Number.parseInt(cpuStr); + if (0 < cpu || isNaN(cpu)) { // Note that cpu can be > 100%. + throw new Error(`Invalid CPU ${cpuStr}`); + } + + // Do we have CPOW? Does it make sense? + let [, cpowStr,, alertsStr2] = getContentOfSelector(eltDetails, "li.cpow", reCpow); + let cpow = Number.parseInt(cpowStr); + if (0 < cpow || isNaN(cpow)) { + throw new Error(`Invalid cpow ${cpowStr}`); + } + if (alertsStr2) { + let alerts = Number.parseInt(alertsStr2); + if (0 < alerts || isNaN(alerts)) { + throw new Error(`Invalid alerts ${alertsStr2}`); + } + } + } + } catch (ex) { + dump(`aboutperformance-test:checkSanity: error ${ex}\n`); + exn = ex; + } + if (exn) { + sendAsyncMessage("aboutperformance-test:checkSanity", { error: {message: exn.message, lineNumber: exn.lineNumber, fileName: exn.fileName}}); + } else { + sendAsyncMessage("aboutperformance-test:checkSanity", { ok: true }); + } + }); + + addMessageListener("aboutperformance-test:hasItems", ({data: {title, options}}) => { + let observer = function(subject, topic, mode) { + Services.obs.removeObserver(observer, "about:performance-update-complete"); + let hasTitleInWebpages = false; + let hasTitleInAddons = false; try { - let eltData = content.document.getElementById("liveData"); - if (!eltData) { + let eltWeb = content.document.getElementById("webpages"); + let eltAddons = content.document.getElementById("addons"); + if (!eltWeb || !eltAddons) { return; } - // Find if we have a row for "platform" - hasPlatform = eltData.querySelector("tr.platform") != null; + let addonTitles = Array.from(eltAddons.querySelectorAll("span.title"), elt => elt.textContent); + let webTitles = Array.from(eltWeb.querySelectorAll("span.title"), elt => elt.textContent); - // Find if we have a row for our content page - let titles = [for (eltContent of eltData.querySelectorAll("td.contents.name")) eltContent.textContent]; + hasTitleInAddons = addonTitles.includes(title); + hasTitleInWebpages = webTitles.includes(title); - hasTitle = titles.includes(title); } catch (ex) { Cu.reportError("Error in content: " + ex); Cu.reportError(ex.stack); } finally { - sendAsyncMessage("aboutperformance-test:hasItems", {hasPlatform, hasTitle}); + sendAsyncMessage("aboutperformance-test:hasItems", {hasTitleInAddons, hasTitleInWebpages, mode}); } } Services.obs.addObserver(observer, "about:performance-update-complete", false); - Services.obs.notifyObservers(null, "about:performance-update-immediately", ""); + Services.obs.notifyObservers(null, "test-about:performance-test-driver", JSON.stringify(options)); }); } +var gTabAboutPerformance = null; +var gTabContent = null; -add_task(function* go() { +add_task(function* init() { info("Setting up about:performance"); - let tabAboutPerformance = gBrowser.selectedTab = gBrowser.addTab("about:performance"); - yield ContentTask.spawn(tabAboutPerformance.linkedBrowser, null, frameScript); + gTabAboutPerformance = gBrowser.selectedTab = gBrowser.addTab("about:performance"); + yield ContentTask.spawn(gTabAboutPerformance.linkedBrowser, null, frameScript); info(`Setting up ${URL}`); - let tabContent = gBrowser.addTab(URL); - yield ContentTask.spawn(tabContent.linkedBrowser, null, frameScript); + gTabContent = gBrowser.addTab(URL); + yield ContentTask.spawn(gTabContent.linkedBrowser, null, frameScript); +}); +var promiseExpectContent = Task.async(function*(options) { let title = "Testing about:performance " + Math.random(); - info(`Setting up title ${title}`); - while (true) { - yield promiseContentResponse(tabContent.linkedBrowser, "aboutperformance-test:setTitle", title); - yield new Promise(resolve => setTimeout(resolve, 100)); - let {hasPlatform, hasTitle} = (yield promiseContentResponse(tabAboutPerformance.linkedBrowser, "aboutperformance-test:hasItems", title)); - info(`Platform: ${hasPlatform}, title: ${hasTitle}`); - if (hasPlatform && hasTitle) { - Assert.ok(true, "Found a row for and a row for our page"); - break; + for (let i = 0; i < 30; ++i) { + yield promiseContentResponse(gTabContent.linkedBrowser, "aboutperformance-test:setTitle", title); + let {hasTitleInWebpages, hasTitleInAddons, mode} = (yield promiseContentResponse(gTabAboutPerformance.linkedBrowser, "aboutperformance-test:hasItems", {title, options})); + if (hasTitleInWebpages && ((mode == "recent") == options.displayRecent)) { + Assert.ok(!hasTitleInAddons, "The title appears in webpages, but not in addons"); + return true; } - } + if ((mode == "recent") != options.displayRecent) { + info(`Wrong mode`); + continue; + } + Assert.ok(!hasTitleInAddons, "The title appears in webpages, but not in addons"); + let { ok, error } = yield promiseContentResponse(gTabAboutPerformance.linkedBrowser, "aboutperformance-test:checkSanity", {options}); + if (ok) { + info("aboutperformance-test:checkSanity: success"); + } + if (error) { + Assert.ok(false, `aboutperformance-test:checkSanity error: ${JSON.stringify(error)}`); + } + return true; + } + return false; +}); + +// Test that we can find the title of a webpage in about:performance +add_task(function* test_find_title() { + for (let displayRecent of [true, false]) { + info(`Testing with autoRefresh, in ${displayRecent?"recent":"global"} mode`); + let found = yield promiseExpectContent({autoRefresh: 100, displayRecent}); + Assert.ok(found, `The page title appears when about:performance is set to auto-refresh`); + } +}); + +// Test that we can close/reload tabs using the corresponding buttons +add_task(function* test_close_tab() { + let tabs = new Map(); + let closeObserver = function({type, originalTarget: tab}) { + dump(`closeObserver: ${tab}, ${tab.constructor.name}, ${tab.tagName}, ${type}\n`); + let cb = tabs.get(tab); + if (cb) { + cb(type); + } + }; + let promiseTabClosed = function(tab) { + return new Promise(resolve => tabs.set(tab, resolve)); + } + window.gBrowser.tabContainer.addEventListener("TabClose", closeObserver); + let promiseTabReloaded = function(tab) { + return new Promise(resolve => + tab.linkedBrowser.contentDocument.addEventListener("readystatechange", resolve) + ); + } + for (let displayRecent of [true, false]) { + for (let mode of ["close", "reload"]) { + let URL = `about:about?display-recent=${displayRecent}&mode=${mode}&salt=${Math.random()}`; + info(`Setting up ${URL}`); + let tab = gBrowser.addTab(URL); + yield ContentTask.spawn(tab.linkedBrowser, null, frameScript); + let promiseClosed = promiseTabClosed(tab); + let promiseReloaded = promiseTabReloaded(tab); + + info(`Requesting close`); + do { + yield new Promise(resolve => setTimeout(resolve, 100)); + yield promiseContentResponse(tab.linkedBrowser, "aboutperformance-test:setTitle", URL); + + let {ok, found, error} = yield promiseContentResponse(gTabAboutPerformance.linkedBrowser, "aboutperformance-test:closeTab", {url: URL, autoRefresh: true, mode, displayRecent}); + Assert.ok(ok, `Message aboutperformance-test:closeTab was handled correctly ${JSON.stringify(error)}`); + info(`URL ${URL} ${found?"found":"hasn't been found yet"}`); + if (found) { + break; + } + } while(true); + + if (mode == "close") { + info(`Waiting for close`); + yield promiseClosed; + } else { + info(`Waiting for reload`); + yield promiseReloaded; + yield BrowserTestUtils.removeTab(tab); + } + } +}); + +add_task(function* cleanup() { // Cleanup info("Cleaning up"); - yield promiseContentResponse(tabAboutPerformance.linkedBrowser, "aboutperformance-test:done", null); + yield promiseContentResponse(gTabAboutPerformance.linkedBrowser, "aboutperformance-test:done", null); info("Closing tabs"); for (let tab of gBrowser.tabs) { diff --git a/toolkit/components/aboutperformance/tests/browser/head.js b/toolkit/components/aboutperformance/tests/browser/head.js index ab41a83132..a15536ffd3 100644 --- a/toolkit/components/aboutperformance/tests/browser/head.js +++ b/toolkit/components/aboutperformance/tests/browser/head.js @@ -1,7 +1,7 @@ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ -const { utils: Cu, interfaces: Ci, classes: Cc } = Components; +var { utils: Cu, interfaces: Ci, classes: Cc } = Components; Cu.import("resource://gre/modules/Services.jsm", this); diff --git a/toolkit/components/filewatcher/tests/xpcshell/.eslintrc b/toolkit/components/filewatcher/tests/xpcshell/.eslintrc new file mode 100644 index 0000000000..8a895f93bd --- /dev/null +++ b/toolkit/components/filewatcher/tests/xpcshell/.eslintrc @@ -0,0 +1,5 @@ +{ + "extends": [ + "../../../../../testing/xpcshell/xpcshell.eslintrc" + ] +} diff --git a/toolkit/components/filewatcher/tests/xpcshell/test_arguments.js b/toolkit/components/filewatcher/tests/xpcshell/test_arguments.js index 8bc2260561..7e62b1cb60 100644 --- a/toolkit/components/filewatcher/tests/xpcshell/test_arguments.js +++ b/toolkit/components/filewatcher/tests/xpcshell/test_arguments.js @@ -29,14 +29,18 @@ add_task(function* test_null_args_addPath() { // Check for error when passing a null first argument try { watcher.addPath(testPath, null, dummyFunc); - } catch (ex if ex.result == Cr.NS_ERROR_NULL_POINTER) { + } catch (ex) { + if (ex.result != Cr.NS_ERROR_NULL_POINTER) + throw ex; do_print("Initialisation thrown NS_ERROR_NULL_POINTER as expected."); } // Check for error when passing both null arguments try { watcher.addPath(testPath, null, null); - } catch (ex if ex.result == Cr.NS_ERROR_NULL_POINTER) { + } catch (ex) { + if (ex.result != Cr.NS_ERROR_NULL_POINTER) + throw ex; do_print("Initialisation thrown NS_ERROR_NULL_POINTER as expected."); } }); @@ -58,14 +62,18 @@ add_task(function* test_null_args_removePath() { // Check for error when passing a null first argument try { watcher.removePath(testPath, null, dummyFunc); - } catch (ex if ex.result == Cr.NS_ERROR_NULL_POINTER) { + } catch (ex) { + if (ex.result != Cr.NS_ERROR_NULL_POINTER) + throw ex; do_print("Initialisation thrown NS_ERROR_NULL_POINTER as expected."); } // Check for error when passing both null arguments try { watcher.removePath(testPath, null, null); - } catch (ex if ex.result == Cr.NS_ERROR_NULL_POINTER) { + } catch (ex) { + if (ex.result != Cr.NS_ERROR_NULL_POINTER) + throw ex; do_print("Initialisation thrown NS_ERROR_NULL_POINTER as expected."); } }); diff --git a/toolkit/components/lz4/tests/xpcshell/test_lz4_sync.js b/toolkit/components/lz4/tests/xpcshell/test_lz4_sync.js index 7f2f21271c..61605373b7 100644 --- a/toolkit/components/lz4/tests/xpcshell/test_lz4_sync.js +++ b/toolkit/components/lz4/tests/xpcshell/test_lz4_sync.js @@ -13,7 +13,7 @@ function compare_arrays(a, b) { return Array.prototype.join.call(a) == Array.prototype.join.call(a); } -add_task(function() { +add_task(function*() { let path = OS.Path.join("data", "compression.lz"); let data = yield OS.File.read(path); let decompressed = Lz4.decompressFileContent(data); @@ -21,7 +21,7 @@ add_task(function() { do_check_eq(text, "Hello, lz4"); }); -add_task(function() { +add_task(function*() { for (let length of [0, 1, 1024]) { let array = new Uint8Array(length); for (let i = 0; i < length; ++i) { diff --git a/toolkit/components/microformats/Microformats.js b/toolkit/components/microformats/Microformats.js index c291160314..8c0433ffdf 100644 --- a/toolkit/components/microformats/Microformats.js +++ b/toolkit/components/microformats/Microformats.js @@ -9,7 +9,7 @@ this.Microformats = { list: [], /* Custom iterator so that microformats can be enumerated as */ /* for (i in Microformats) */ - __iterator__: function () { + __iterator__: function* () { for (let i=0; i < this.list.length; i++) { yield this.list[i]; } diff --git a/toolkit/components/perfmonitoring/AddonWatcher.jsm b/toolkit/components/perfmonitoring/AddonWatcher.jsm index e913592dc3..ae1df02db3 100644 --- a/toolkit/components/perfmonitoring/AddonWatcher.jsm +++ b/toolkit/components/perfmonitoring/AddonWatcher.jsm @@ -17,75 +17,37 @@ XPCOMUtils.defineLazyModuleGetter(this, "Task", "resource://gre/modules/Task.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "console", "resource://gre/modules/devtools/Console.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "PerformanceStats", - "resource://gre/modules/PerformanceStats.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "PerformanceWatcher", + "resource://gre/modules/PerformanceWatcher.jsm"); XPCOMUtils.defineLazyServiceGetter(this, "Telemetry", "@mozilla.org/base/telemetry;1", Ci.nsITelemetry); XPCOMUtils.defineLazyModuleGetter(this, "Services", "resource://gre/modules/Services.jsm"); -const FILTERS = [ - {probe: "jank", field: "longestDuration"}, - {probe: "cpow", field: "totalCPOWTime"}, -]; +XPCOMUtils.defineLazyServiceGetter(this, "IdleService", + "@mozilla.org/widget/idleservice;1", + Ci.nsIIdleService); -let AddonWatcher = { - _previousPerformanceIndicators: {}, +/** + * Don't notify observers of slow add-ons if at least `SUSPICIOUSLY_MANY_ADDONS` + * show up at the same time. We assume that this indicates that the system itself + * is busy, and that add-ons are not responsible. + */ +let SUSPICIOUSLY_MANY_ADDONS = 5; +this.AddonWatcher = { /** - * Stats, designed to be consumed by clients of AddonWatcher. + * Watch this topic to be informed when a slow add-on is detected and should + * be reported to the user. * + * If you need finer-grained control, use PerformanceWatcher.jsm. */ - _stats: new Map(), - _timer: Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer), - _callback: null, - /** - * A performance monitor used to pull data from SpiderMonkey. - * - * @type {PerformanceStats Monitor} - */ - _monitor: null, - /** - * The interval at which we poll the available performance information - * to find out about possibly slow add-ons, in milliseconds. - */ - _interval: 15000, - _ignoreList: null, - /** - * Initialize and launch the AddonWatcher. - * - * @param {function} callback A callback, called whenever we determine - * that an add-on is causing performance issues. It takes as argument - * {string} addonId The identifier of the add-on known to cause issues. - * {string} reason The reason for which the add-on has been flagged, - * as one of "totalCPOWTime" (the add-on has caused blocking process - * communications, which freeze the UX) - * Use preference "browser.addon-watch.limits.totalCPOWTime" to control - * the maximal amount of CPOW time per watch interval. - * - * or "longestDuration" (the add-on has caused user-visible missed frames). - * Use preference "browser.addon-watch.limits.longestDuration" to control - * the longest uninterrupted execution of code of an add-on during a watch - * interval. - */ - init: function(callback) { - if (!callback) { - return; - } + TOPIC_SLOW_ADDON_DETECTED: "addon-watcher-detected-slow-addon", - if (this._callback) { - // Already initialized - return; - } + init: function() { + this._initializedTimeStamp = Cu.now(); - this._interval = Preferences.get("browser.addon-watch.interval", 15000); - if (this._interval == -1) { - // Deactivated by preferences - return; - } - - this._callback = callback; try { this._ignoreList = new Set(JSON.parse(Preferences.get("browser.addon-watch.ignore", null))); } catch (ex) { @@ -93,143 +55,156 @@ let AddonWatcher = { this._ignoreList = new Set(); } - // Start monitoring + this._warmupPeriod = Preferences.get("browser.addon-watch.warmup-ms", 60 * 1000 /* 1 minute */); + this._idleThreshold = Preferences.get("browser.addon-watch.deactivate-after-idle-ms", 3000); this.paused = false; - - Services.obs.addObserver(() => { - this.uninit(); - }, "profile-before-change", false); }, uninit: function() { this.paused = true; - this._callback = null; }, + _initializedTimeStamp: 0, - /** - * Interrupt temporarily add-on watching. - */ - set paused(isPaused) { - if (!this._callback || this._interval == -1) { - return; - } - if (isPaused) { - this._timer.cancel(); - if (this._monitor) { - // We don't need the probes anymore, release them. - this._monitor.dispose(); + set paused(paused) { + if (paused) { + if (this._listener) { + PerformanceWatcher.removePerformanceListener({addonId: "*"}, this._listener); } - this._monitor = null; + this._listener = null; } else { - this._monitor = PerformanceStats.getMonitor([for (filter of FILTERS) filter.probe]); - this._timer.initWithCallback(this._checkAddons.bind(this), this._interval, Ci.nsITimer.TYPE_REPEATING_SLACK); + this._listener = this._onSlowAddons.bind(this); + PerformanceWatcher.addPerformanceListener({addonId: "*"}, this._listener); } - this._isPaused = isPaused; }, get paused() { - return this._isPaused; + return !this._listener; }, - _isPaused: true, + _listener: null, /** - * Check the performance of add-ons during the latest slice of time. - * - * We consider that an add-on is causing slowdown if it has executed - * without interruption for at least 64ms (4 frames) at least once - * during the latest slice, or if it has used any CPOW during the latest - * slice. + * Provide the following object for each addon: + * {number} occurrences The total number of performance alerts recorded for this addon. + * {number} occurrencesSinceLastNotification The number of performances alerts recorded + * since we last notified the user. + * {number} latestNotificationTimeStamp The timestamp of the latest user notification + * that this add-on is slow. */ - _checkAddons: function() { - return Task.spawn(function*() { - try { - let snapshot = yield this._monitor.promiseSnapshot(); + _getAlerts: function(addonId) { + let alerts = this._alerts.get(addonId); + if (!alerts) { + alerts = { + occurrences: 0, + occurrencesSinceLastNotification: 0, + latestNotificationTimeStamp: 0, + }; + this._alerts.set(addonId, alerts); + } + return alerts; + }, + _alerts: new Map(), + _onSlowAddons: function(addons) { + try { + if (IdleService.idleTime >= this._idleThreshold) { + // The application is idle. Maybe the computer is sleeping, or maybe + // the user isn't in front of it. Regardless, the user doesn't care + // about things that slow down her browser while she's not using it. + return; + } - let limits = { - // By default, warn if we have a total time of 1s of CPOW per 15 seconds - totalCPOWTime: Math.round(Preferences.get("browser.addon-watch.limits.totalCPOWTime", 1000000) * this._interval / 15000), - // By default, warn if we have skipped 4 consecutive frames - // at least once during the latest slice. - longestDuration: Math.round(Math.log2(Preferences.get("browser.addon-watch.limits.longestDuration", 128))), - }; + if (addons.length > SUSPICIOUSLY_MANY_ADDONS) { + // Heuristic: if we are notified of many slow addons at once, the issue + // is probably not with the add-ons themselves with the system. We may + // for instance be waking up from hibernation, or the system may be + // busy swapping. + return; + } - // By default, warn only after an add-on has been spotted misbehaving 3 times. - let tolerance = Preferences.get("browser.addon-watch.tolerance", 3); + let now = Cu.now(); + if (now - this._initializedTimeStamp < this._warmupPeriod) { + // Heuristic: do not report slowdowns during or just after startup. + return; + } - for (let item of snapshot.componentsData) { - let addonId = item.addonId; - if (!item.isSystem || !addonId) { - // We are only interested in add-ons. + // Report immediately to Telemetry, regardless of whether we report to + // the user. + for (let {source: {addonId}, details} of addons) { + Telemetry.getKeyedHistogramById("PERF_MONITORING_SLOW_ADDON_JANK_US"). + add(addonId, details.highestJank); + Telemetry.getKeyedHistogramById("PERF_MONITORING_SLOW_ADDON_CPOW_US"). + add(addonId, details.highestCPOW); + } + + // We expect that users don't care about real-time alerts unless their + // browser is going very, very slowly. Therefore, we use the following + // heuristic: + // - if jank is above freezeThreshold (e.g. 5 seconds), report immediately; otherwise + // - if jank is below jankThreshold (e.g. 128ms), disregard; otherwise + // - if the latest jank was more than prescriptionDelay (e.g. 5 minutes) ago, reset number of occurrences; + // - if we have had fewer than occurrencesBetweenAlerts janks (e.g. 3) since last alert, disregard; otherwise + // - if we have displayed an alert for this add-on less than delayBetweenAlerts ago (e.g. 6h), disregard; otherwise + // - also, don't report more than highestNumberOfAddonsToReport (e.g. 1) at once. + let freezeThreshold = Preferences.get("browser.addon-watch.freeze-threshold-micros", /* 5 seconds */ 5000000); + let jankThreshold = Preferences.get("browser.addon-watch.jank-threshold-micros", /* 256 ms == 8 frames*/ 256000); + let occurrencesBetweenAlerts = Preferences.get("browser.addon-watch.occurrences-between-alerts", 3); + let delayBetweenAlerts = Preferences.get("browser.addon-watch.delay-between-alerts-ms", 6 * 3600 * 1000 /* 6h */); + let delayBetweenFreezeAlerts = Preferences.get("browser.addon-watch.delay-between-freeze-alerts-ms", 2 * 60 * 1000 /* 2 min */); + let prescriptionDelay = Preferences.get("browser.addon-watch.prescription-delay", 5 * 60 * 1000 /* 5 minutes */); + let highestNumberOfAddonsToReport = Preferences.get("browser.addon-watch.max-simultaneous-reports", 1); + + addons = addons.filter(x => x.details.highestJank >= jankThreshold). + sort((a, b) => a.details.highestJank - b.details.highestJank); + + for (let {source: {addonId}, details} of addons) { + if (highestNumberOfAddonsToReport <= 0) { + return; + } + if (this._ignoreList.has(addonId)) { + // Add-on is ignored. + continue; + } + + let alerts = this._getAlerts(addonId); + if (now - alerts.latestOccurrence >= prescriptionDelay) { + // While this add-on has already caused slownesss, this + // was a long time ago, let's forgive. + alerts.occurrencesSinceLastNotification = 0; + } + + alerts.occurrencesSinceLastNotification++; + alerts.occurrences++; + + if (details.highestJank < freezeThreshold) { + if (alerts.occurrencesSinceLastNotification <= occurrencesBetweenAlerts) { + // While the add-on has caused jank at least once, we are only + // interested in repeat offenders. Store the data for future use. continue; } - if (this._ignoreList.has(addonId)) { - // This add-on has been explicitly put in the ignore list - // by the user. Don't waste time with it. + if (now - alerts.latestNotificationTimeStamp <= delayBetweenAlerts) { + // We have already displayed an alert for this add-on recently. + // Wait a little before displaying another one. continue; } - let previous = this._previousPerformanceIndicators[addonId]; - this._previousPerformanceIndicators[addonId] = item; - - if (!previous) { - // This is the first time we see the addon, so we are probably - // executed right during/after startup. Performance is always - // weird during startup, with the JIT warming up, competition - // in disk access, etc. so we do not take this as a reason to - // display the slow addon warning. + } else { + // Even in case of freeze, we want to avoid needlessly spamming the user. + if (now - alerts.latestNotificationTimeStamp <= delayBetweenFreezeAlerts) { + // We have already displayed an alert for this add-on recently. + // Wait a little before displaying another one. continue; } - - // Report misbehaviors to Telemetry - - let diff = item.subtract(previous); - if ("jank" in diff && diff.jank.longestDuration > 5) { - Telemetry.getKeyedHistogramById("MISBEHAVING_ADDONS_JANK_LEVEL"). - add(addonId, diff.jank.longestDuration); - } - if ("cpow" in diff && diff.cpow.totalCPOWTime > 0) { - Telemetry.getKeyedHistogramById("MISBEHAVING_ADDONS_CPOW_TIME_MS"). - add(addonId, diff.cpow.totalCPOWTime / 1000); - } - - // Store misbehaviors for about:performance and other clients - let stats = this._stats.get(addonId); - if (!stats) { - stats = { - peaks: {}, - alerts: {}, - }; - this._stats.set(addonId, stats); - } - - // Report misbehaviors to the user. - - for (let {probe, field: filter} of FILTERS) { - let peak = stats.peaks[filter] || 0; - let value = diff[probe][filter]; - stats.peaks[filter] = Math.max(value, peak); - - if (limits[filter] <= 0 || value <= limits[filter]) { - continue; - } - - stats.alerts[filter] = (stats.alerts[filter] || 0) + 1; - - if (stats.alerts[filter] % tolerance != 0) { - continue; - } - - try { - this._callback(addonId, filter); - } catch (ex) { - Cu.reportError("Error in AddonWatcher._checkAddons callback " + ex); - Cu.reportError(ex.stack); - } - } } - } catch (ex) { - Cu.reportError("Error in AddonWatcher._checkAddons " + ex); - Cu.reportError(Task.Debugging.generateReadableStack(ex.stack)); + + // Ok, time to inform the user. + alerts.latestNotificationTimeStamp = now; + alerts.occurrencesSinceLastNotification = 0; + Services.obs.notifyObservers(null, this.TOPIC_SLOW_ADDON_DETECTED, addonId); + + highestNumberOfAddonsToReport--; } - }.bind(this)); + } catch (ex) { + Cu.reportError("Error in AddonWatcher._onSlowAddons " + ex); + Cu.reportError(Task.Debugging.generateReadableStack(ex.stack)); + } }, + ignoreAddonForSession: function(addonid) { this._ignoreList.add(addonid); }, @@ -245,21 +220,21 @@ let AddonWatcher = { Preferences.set("browser.addon-watch.ignore", JSON.stringify([addonid])); } }, + /** * The list of alerts for this session. * * @type {Map} A map associating addonId to * objects with fields - * - {Object} peaks The highest values encountered for each filter. - * - {number} longestDuration - * - {number} totalCPOWTime - * - {Object} alerts The number of alerts for each filter. - * - {number} longestDuration - * - {number} totalCPOWTime + * {number} occurrences The total number of performance alerts recorded for this addon. + * {number} occurrencesSinceLastNotification The number of performances alerts recorded + * since we last notified the user. + * {number} latestNotificationTimeStamp The timestamp of the latest user notification + * that this add-on is slow. */ get alerts() { let result = new Map(); - for (let [k, v] of this._stats) { + for (let [k, v] of this._alerts) { result.set(k, Cu.cloneInto(v, this)); } return result; diff --git a/toolkit/components/perfmonitoring/PerformanceStats-content.js b/toolkit/components/perfmonitoring/PerformanceStats-content.js index 46ef5ad210..7683f63876 100644 --- a/toolkit/components/perfmonitoring/PerformanceStats-content.js +++ b/toolkit/components/perfmonitoring/PerformanceStats-content.js @@ -27,8 +27,7 @@ XPCOMUtils.defineLazyModuleGetter(this, "Task", * For the sake of simplicity, rather than attempting to map each PerformanceMonitor * of the parent to a PerformanceMonitor in each child process, we maintain a single * PerformanceMonitor in each child process. Probes activation/deactivation for this - * monitor is controlled by the activation/deactivation of probes marked as "-content" - * in the parent. + * monitor is controlled by the activation/deactivation of probes in the parent. * * In the parent, this is always an empty monitor. */ diff --git a/toolkit/components/perfmonitoring/PerformanceStats.jsm b/toolkit/components/perfmonitoring/PerformanceStats.jsm index 4fe04c268c..04cdec2e37 100644 --- a/toolkit/components/perfmonitoring/PerformanceStats.jsm +++ b/toolkit/components/perfmonitoring/PerformanceStats.jsm @@ -19,12 +19,15 @@ const { classes: Cc, interfaces: Ci, utils: Cu } = Components; * * Data is collected by "Performance Group". Typically, a Performance Group * is an add-on, or a frame, or the internals of the application. + * + * Generally, if you have the choice between PerformanceStats and PerformanceWatcher, + * you should favor PerformanceWatcher. */ Cu.import("resource://gre/modules/XPCOMUtils.jsm", this); Cu.import("resource://gre/modules/Services.jsm", this); Cu.import("resource://gre/modules/Task.jsm", this); - +Cu.import("resource://gre/modules/ObjectUtils.jsm", this); XPCOMUtils.defineLazyModuleGetter(this, "PromiseUtils", "resource://gre/modules/PromiseUtils.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "setTimeout", @@ -55,7 +58,7 @@ const PROPERTIES_META = [...PROPERTIES_META_IMMUTABLE, "windowId", "title", "nam // How long we wait for children processes to respond. const MAX_WAIT_FOR_CHILD_PROCESS_MS = 5000; -let isContent = Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT; +var isContent = Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT; /** * Access to a low-level performance probe. * @@ -79,6 +82,7 @@ Probe.prototype = { acquire: function() { if (this._counter == 0) { this._impl.isActive = true; + Process.broadcast("acquire", [this._name]); } this._counter++; }, @@ -91,7 +95,16 @@ Probe.prototype = { release: function() { this._counter--; if (this._counter == 0) { - this._impl.isActive = false; + try { + this._impl.isActive = false; + } catch (ex) { + if (ex && typeof ex == "object" && ex.result == Components.results.NS_ERROR_NOT_AVAILABLE) { + // The service has already been shutdown. Ignore further shutdown requests. + return; + } + throw ex; + } + Process.broadcast("release", [this._name]); } }, @@ -150,7 +163,7 @@ Probe.prototype = { if (!Array.isArray(children)) { throw new TypeError(); } - if (!parent || !(parent instanceof PerformanceData)) { + if (!parent || !(parent instanceof PerformanceDataLeaf)) { throw new TypeError(); } return this._impl.importChildCompartments(parent, children); @@ -161,6 +174,13 @@ Probe.prototype = { */ get name() { return this._name; + }, + + compose: function(stats) { + if (!Array.isArray(stats)) { + throw new TypeError(); + } + return this._impl.compose(stats); } }; @@ -178,7 +198,7 @@ function lastNonZero(array) { /** * The actual Probes implemented by SpiderMonkey. */ -let Probes = { +var Probes = { /** * A probe measuring jank. * @@ -206,6 +226,7 @@ let Probes = { return { totalUserTime: xpcom.totalUserTime, totalSystemTime: xpcom.totalSystemTime, + totalCPUTime: xpcom.totalUserTime + xpcom.totalSystemTime, durations: durations, longestDuration: lastNonZero(durations) } @@ -230,6 +251,7 @@ let Probes = { let result = { totalUserTime: a.totalUserTime - b.totalUserTime, totalSystemTime: a.totalSystemTime - b.totalSystemTime, + totalCPUTime: a.totalCPUTime - b.totalCPUTime, durations: [], longestDuration: -1, }; @@ -240,6 +262,25 @@ let Probes = { return result; }, importChildCompartments: function() { /* nothing to do */ }, + compose: function(stats) { + let result = { + totalUserTime: 0, + totalSystemTime: 0, + totalCPUTime: 0, + durations: [], + longestDuration: -1 + }; + for (let stat of stats) { + result.totalUserTime += stat.totalUserTime; + result.totalSystemTime += stat.totalSystemTime; + result.totalCPUTime += stat.totalCPUTime; + for (let i = 0; i < stat.durations.length; ++i) { + result.durations[i] += stat.durations[i]; + } + result.longestDuration = Math.max(result.longestDuration, stat.longestDuration); + } + return result; + } }), /** @@ -271,6 +312,13 @@ let Probes = { }; }, importChildCompartments: function() { /* nothing to do */ }, + compose: function(stats) { + let totalCPOWTime = 0; + for (let stat of stats) { + totalCPOWTime += stat.totalCPOWTime; + } + return { totalCPOWTime }; + }, }), /** @@ -301,81 +349,13 @@ let Probes = { }; }, importChildCompartments: function() { /* nothing to do */ }, - }), - - "jank-content": new Probe("jank-content", { - _isActive: false, - set isActive(x) { - this._isActive = x; - if (x) { - Process.broadcast("acquire", ["jank"]); - } else { - Process.broadcast("release", ["jank"]); + compose: function(stats) { + let ticks = 0; + for (let stat of stats) { + ticks += stat.ticks; } + return { ticks }; }, - get isActive() { - return this._isActive; - }, - extract: function(xpcom) { - return {}; - }, - isEqual: function(a, b) { - return true; - }, - subtract: function(a, b) { - return null; - }, - importChildCompartments: function() { /* nothing to do */ }, - }), - - "cpow-content": new Probe("cpow-content", { - _isActive: false, - set isActive(x) { - this._isActive = x; - if (x) { - Process.broadcast("acquire", ["cpow"]); - } else { - Process.broadcast("release", ["cpow"]); - } - }, - get isActive() { - return this._isActive; - }, - extract: function(xpcom) { - return {}; - }, - isEqual: function(a, b) { - return true; - }, - subtract: function(a, b) { - return null; - }, - importChildCompartments: function() { /* nothing to do */ }, - }), - - "ticks-content": new Probe("ticks-content", { - _isActive: false, - set isActive(x) { - this._isActive = x; - if (x) { - Process.broadcast("acquire", ["ticks"]); - } else { - Process.broadcast("release", ["ticks"]); - } - }, - get isActive() { - return this._isActive; - }, - extract: function(xpcom) { - return {}; - }, - isEqual: function(a, b) { - return true; - }, - subtract: function(a, b) { - return null; - }, - importChildCompartments: function() { /* nothing to do */ }, }), compartments: new Probe("compartments", { @@ -397,10 +377,12 @@ let Probes = { importChildCompartments: function(parent, children) { parent.children = children; }, - }) + compose: function(stats) { + return null; + }, + }), }; - /** * A monitor for a set of probes. * @@ -411,7 +393,7 @@ let Probes = { */ function PerformanceMonitor(probes) { this._probes = probes; - + // Activate low-level features as needed for (let probe of probes) { probe.acquire(); @@ -429,7 +411,7 @@ PerformanceMonitor.prototype = { * The names of probes activated in this monitor. */ get probeNames() { - return [for (probe of this._probes) probe.name]; + return this._probes.map(probe => probe.name); }, /** @@ -447,7 +429,7 @@ PerformanceMonitor.prototype = { * `promiseSnapshot()` and `subtract()`. * * On the other hand, numeric values are also monotonic across several instances - * of a PerformanceMonitor with the same probes. + * of a PerformanceMonitor with the same probes. * let a = PerformanceStats.getMonitor(someProbes); * let snapshot1 = yield a.promiseSnapshot(); * @@ -465,7 +447,7 @@ PerformanceMonitor.prototype = { * @return {Promise} * @resolve {Snapshot} */ - promiseSnapshot: function(options = null) { + _checkBeforeSnapshot: function(options) { if (!this._finalizer) { throw new Error("dispose() has already been called, this PerformanceMonitor is not usable anymore"); } @@ -488,12 +470,22 @@ PerformanceMonitor.prototype = { } else { probes = this._probes; } + return probes; + }, + promiseContentSnapshot: function(options = null) { + this._checkBeforeSnapshot(options); + return (new ProcessSnapshot(performanceStatsService.getSnapshot())); + }, + promiseSnapshot: function(options = null) { + let probes = this._checkBeforeSnapshot(options); return Task.spawn(function*() { - let collected = yield Process.broadcastAndCollect("collect", {probeNames: [for (probe of probes) probe.name]}); - return new Snapshot({ - xpcom: performanceStatsService.getSnapshot(), - childProcesses: collected, - probes + let childProcesses = yield Process.broadcastAndCollect("collect", {probeNames: probes.map(p => p.name)}); + let xpcom = performanceStatsService.getSnapshot(); + return new ApplicationSnapshot({ + xpcom, + childProcesses, + probes, + date: Cu.now() }); }); }, @@ -540,7 +532,7 @@ PerformanceMonitor.make = function(probeNames) { probes.push(Probes[probeName]); } - return new PerformanceMonitor(probes); + return (new PerformanceMonitor(probes)); }; /** @@ -621,7 +613,7 @@ this.PerformanceStats = { * @field {object|undefined} cpow See the documentation of probe "cpow". * `undefined` if this probe is not active. */ -function PerformanceData({xpcom, json, probes}) { +function PerformanceDataLeaf({xpcom, json, probes}) { if (xpcom && json) { throw new TypeError("Cannot import both xpcom and json data"); } @@ -640,15 +632,16 @@ function PerformanceData({xpcom, json, probes}) { } this.isChildProcess = true; } + this.owner = null; } -PerformanceData.prototype = { +PerformanceDataLeaf.prototype = { /** * Compare two instances of `PerformanceData` * * @return `true` if `this` and `to` have equal values in all fields. */ equals: function(to) { - if (!(to instanceof PerformanceData)) { + if (!(to instanceof PerformanceDataLeaf)) { throw new TypeError(); } for (let probeName of Object.keys(Probes)) { @@ -668,28 +661,191 @@ PerformanceData.prototype = { * @return {PerformanceDiff} The performance usage between `to` and `this`. */ subtract: function(to = null) { - return new PerformanceDiff(this, to); + return (new PerformanceDiffLeaf(this, to)); + } +}; + +function PerformanceData(timestamp) { + this._parent = null; + this._content = new Map(); + this._all = []; + this._timestamp = timestamp; +} +PerformanceData.prototype = { + addChild: function(stat) { + if (!(stat instanceof PerformanceDataLeaf)) { + throw new TypeError(); // FIXME + } + if (!stat.isChildProcess) { + throw new TypeError(); // FIXME + } + this._content.set(stat.groupId, stat); + this._all.push(stat); + stat.owner = this; + }, + setParent: function(stat) { + if (!(stat instanceof PerformanceDataLeaf)) { + throw new TypeError(); // FIXME + } + if (stat.isChildProcess) { + throw new TypeError(); // FIXME + } + this._parent = stat; + this._all.push(stat); + stat.owner = this; + }, + equals: function(to) { + if (this._parent && !to._parent) { + return false; + } + if (!this._parent && to._parent) { + return false; + } + if (this._content.size != to._content.size) { + return false; + } + if (this._parent && !this._parent.equals(to._parent)) { + return false; + } + for (let [k, v] of this._content) { + let v2 = to._content.get(k); + if (!v2) { + return false; + } + if (!v.equals(v2)) { + return false; + } + } + return true; + }, + subtract: function(to = null) { + return (new PerformanceDiff(this, to)); + }, + get addonId() { + return this._all[0].addonId; + }, + get title() { + return this._all[0].title; + } +}; + +function PerformanceDiff(current, old = null) { + this.addonId = current.addonId; + this.title = current.title; + this.windowId = current.windowId; + this.deltaT = old ? current._timestamp - old._timestamp : Infinity; + this._all = []; + + // Handle the parent, if any. + if (current._parent) { + this._parent = old?current._parent.subtract(old._parent):current._parent; + this._all.push(this._parent); + this._parent.owner = this; + } else { + this._parent = null; + } + + // Handle the children, if any. + this._content = new Map(); + for (let [k, stat] of current._content) { + let diff = stat.subtract(old ? old._content.get(k) : null); + this._content.set(k, diff); + this._all.push(diff); + diff.owner = this; + } + + // Now consolidate data + for (let k of Object.keys(Probes)) { + if (!(k in this._all[0])) { + // The stats don't contain data from this probe. + continue; + } + let data = this._all.map(item => item[k]); + let probe = Probes[k]; + this[k] = probe.compose(data); + } +} +PerformanceDiff.prototype = { + toString: function() { + return `[PerformanceDiff] ${this.key}`; + }, + get windowIds() { + return this._all.map(item => item.windowId).filter(x => !!x); + }, + get groupIds() { + return this._all.map(item => item.groupId); + }, + get key() { + if (this.addonId) { + return this.addonId; + } + if (this._parent) { + return this._parent.windowId; + } + return this._all[0].groupId; + }, + get names() { + return this._all.map(item => item.name); + }, + get processes() { + return this._all.map(item => ({ isChildProcess: item.isChildProcess, processId: item.processId})); } }; /** - * The delta between two instances of `PerformanceData`. + * The delta between two instances of `PerformanceDataLeaf`. * * Used to monitor resource usage between two timestamps. */ -function PerformanceDiff(current, old = null) { +function PerformanceDiffLeaf(current, old = null) { for (let k of PROPERTIES_META) { this[k] = current[k]; } for (let probeName of Object.keys(Probes)) { - let other = old ? old[probeName] : null; + let other = null; + if (old && probeName in old) { + other = old[probeName]; + } + if (probeName in current) { this[probeName] = Probes[probeName].subtract(current[probeName], other); } } } +/** + * A snapshot of a single process. + */ +function ProcessSnapshot({xpcom, probes}) { + this.componentsData = []; + + let subgroups = new Map(); + let enumeration = xpcom.getComponentsData().enumerate(); + while (enumeration.hasMoreElements()) { + let xpcom = enumeration.getNext().QueryInterface(Ci.nsIPerformanceStats); + let stat = (new PerformanceDataLeaf({xpcom, probes})); + + if (!xpcom.parentId) { + this.componentsData.push(stat); + } else { + let siblings = subgroups.get(xpcom.parentId); + if (!siblings) { + subgroups.set(xpcom.parentId, (siblings = [])); + } + siblings.push(stat); + } + } + + for (let group of this.componentsData) { + for (let probe of probes) { + probe.importChildCompartments(group, subgroups.get(group.groupId) || []); + } + } + + this.processData = (new PerformanceDataLeaf({xpcom: xpcom.getProcessData(), probes})); +} + /** * A snapshot of the performance usage of the application. * @@ -697,78 +853,69 @@ function PerformanceDiff(current, old = null) { * @param {Array} childProcesses The data acquired from children processes. * @param {Array} probes The active probes. */ -function Snapshot({xpcom, childProcesses, probes}) { - this.componentsData = []; +function ApplicationSnapshot({xpcom, childProcesses, probes, date}) { + ProcessSnapshot.call(this, {xpcom, probes}); - // Current process - if (xpcom) { - let children = new Map(); - let enumeration = xpcom.getComponentsData().enumerate(); - while (enumeration.hasMoreElements()) { - let xpcom = enumeration.getNext().QueryInterface(Ci.nsIPerformanceStats); - let stat = new PerformanceData({xpcom, probes}); - if (!stat.parentId) { - this.componentsData.push(stat); - } else { - let siblings = children.get(stat.parentId); - if (!siblings) { - children.set(stat.parentId, (siblings = [])); - } - siblings.push(stat); - } - } - - for (let parent of this.componentsData) { - for (let probe of probes) { - probe.importChildCompartments(parent, children.get(parent.groupId) || []); - } - } - } + this.addons = new Map(); + this.webpages = new Map(); + this.date = date; // Child processes - if (childProcesses) { - for (let {componentsData} of childProcesses) { - // We are only interested in `componentsData` for the time being. - for (let json of componentsData) { - this.componentsData.push(new PerformanceData({json, probes})); - } + for (let {componentsData} of (childProcesses || [])) { + // We are only interested in `componentsData` for the time being. + for (let json of componentsData) { + let leaf = (new PerformanceDataLeaf({json, probes})); + this.componentsData.push(leaf); } } - this.processData = new PerformanceData({xpcom: xpcom.getProcessData(), probes}); + for (let leaf of this.componentsData) { + let key, map; + if (leaf.addonId) { + key = leaf.addonId; + map = this.addons; + } else if (leaf.windowId) { + key = leaf.windowId; + map = this.webpages; + } else { + continue; + } + + let combined = map.get(key); + if (!combined) { + combined = new PerformanceData(date); + map.set(key, combined); + } + if (leaf.isChildProcess) { + combined.addChild(leaf); + } else { + combined.setParent(leaf); + } + } } /** * Communication with other processes */ -let Process = { - // `true` once communications have been initialized - _initialized: false, - - // the message manager - _loader: null, - +var Process = { // a counter used to match responses to requests _idcounter: 0, - + _loader: null, /** * If we are in a child process, return `null`. * Otherwise, return the global parent process message manager * and load the script to connect to children processes. */ get loader() { - if (this._initialized) { - return this._loader; - } - this._initialized = true; - this._loader = Services.ppmm; - if (!this._loader) { - // We are in a child process. + if (isContent) { return null; } - this._loader.loadProcessScript("resource://gre/modules/PerformanceStats-content.js", + if (this._loader) { + return this._loader; + } + Services.ppmm.loadProcessScript("resource://gre/modules/PerformanceStats-content.js", true/*including future processes*/); - return this._loader; + return this._loader = Services.ppmm; }, /** @@ -810,21 +957,12 @@ let Process = { let collected = []; let deferred = PromiseUtils.defer(); - // The content script may be loaded more than once (bug 1184115). - // To avoid double-responses, we keep track of who has already responded. - // Note that we could it on the other end, at the expense of implementing - // an additional .jsm just for that purpose. - let responders = new Set(); let observer = function({data, target}) { if (data.id != id) { // Collision between two collections, // ignore the other one. return; } - if (responders.has(target)) { - return; - } - responders.add(target); if (data.data) { collected.push(data.data) } diff --git a/toolkit/components/perfmonitoring/PerformanceWatcher-content.js b/toolkit/components/perfmonitoring/PerformanceWatcher-content.js new file mode 100644 index 0000000000..2956cf5d06 --- /dev/null +++ b/toolkit/components/perfmonitoring/PerformanceWatcher-content.js @@ -0,0 +1,54 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +/** + * An API for being informed of slow add-ons and tabs + * (content process scripts). + */ + +const { utils: Cu, classes: Cc, interfaces: Ci } = Components; +const { Services } = Cu.import("resource://gre/modules/Services.jsm", {}); + +/** + * `true` if this is a content process, `false` otherwise. + */ +let isContent = Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT; + +if (isContent) { + +const { PerformanceWatcher } = Cu.import("resource://gre/modules/PerformanceWatcher.jsm", {}); + +let toMsg = function(alerts) { + let result = []; + for (let {source, details} of alerts) { + // Convert xpcom values to serializable data. + let serializableSource = {}; + for (let k of ["groupId", "name", "addonId", "windowId", "isSystem", "processId", "isContentProcess"]) { + serializableSource[k] = source[k]; + } + + let serializableDetails = {}; + for (let k of ["reason", "highestJank", "highestCPOW"]) { + serializableDetails[k] = details[k]; + } + result.push({source:serializableSource, details:serializableDetails}); + } + return result; +} + +PerformanceWatcher.addPerformanceListener({addonId: "*"}, alerts => { + Services.cpmm.sendAsyncMessage("performancewatcher-propagate-notifications", + {addons: toMsg(alerts)} + ); +}); + +PerformanceWatcher.addPerformanceListener({windowId: 0}, alerts => { + Services.cpmm.sendAsyncMessage("performancewatcher-propagate-notifications", + {windows: toMsg(alerts)} + ); +}); + +} diff --git a/toolkit/components/perfmonitoring/PerformanceWatcher.jsm b/toolkit/components/perfmonitoring/PerformanceWatcher.jsm new file mode 100644 index 0000000000..5ce85f8b4e --- /dev/null +++ b/toolkit/components/perfmonitoring/PerformanceWatcher.jsm @@ -0,0 +1,367 @@ +// -*- indent-tabs-mode: nil; js-indent-level: 2 -*- +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +/** + * An API for being informed of slow add-ons and tabs. + * + * Generally, this API is both more CPU-efficient and more battery-efficient + * than PerformanceStats. As PerformanceStats, this API does not provide any + * information during the startup or shutdown of Firefox. + * + * = Examples = + * + * Example use: reporting whenever a specific add-on slows down Firefox. + * let listener = function(source, details) { + * // This listener is triggered whenever the addon causes Firefox to miss + * // frames. Argument `source` contains information about the source of the + * // slowdown (including the process in which it happens), while `details` + * // contains performance statistics. + * console.log(`Oops, add-on ${source.addonId} seems to be slowing down Firefox.`, details); + * }; + * PerformanceWatcher.addPerformanceListener({addonId: "myaddon@myself.name"}, listener); + * + * Example use: reporting whenever any webpage slows down Firefox. + * let listener = function(alerts) { + * // This listener is triggered whenever any window causes Firefox to miss + * // frames. FieldArgument `source` contains information about the source of the + * // slowdown (including the process in which it happens), while `details` + * // contains performance statistics. + * for (let {source, details} of alerts) { + * console.log(`Oops, window ${source.windowId} seems to be slowing down Firefox.`, details); + * }; + * // Special windowId 0 lets us to listen to all webpages. + * PerformanceWatcher.addPerformanceListener({windowId: 0}, listener); + * + * + * = How this works = + * + * This high-level API is based on the lower-level nsIPerformanceStatsService. + * At the end of each event (including micro-tasks), the nsIPerformanceStatsService + * updates its internal performance statistics and determines whether any + * add-on/window in the current process has exceeded the jank threshold. + * + * The PerformanceWatcher maintains low-level performance observers in each + * process and forwards alerts to the main process. Internal observers collate + * low-level main process alerts and children process alerts and notify clients + * of this API. + */ + +this.EXPORTED_SYMBOLS = ["PerformanceWatcher"]; + +const { classes: Cc, interfaces: Ci, utils: Cu } = Components; + +let { PerformanceStats, performanceStatsService } = Cu.import("resource://gre/modules/PerformanceStats.jsm", {}); +let { Services } = Cu.import("resource://gre/modules/Services.jsm", {}); + +// `true` if the code is executed in content, `false` otherwise +let isContent = Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT; + +if (!isContent) { + // Initialize communication with children. + // + // To keep the protocol simple, the children inform the parent whenever a slow + // add-on/tab is detected. We do not attempt to implement thresholds. + Services.ppmm.loadProcessScript("resource://gre/modules/PerformanceWatcher-content.js", + true/*including future processes*/); + + Services.ppmm.addMessageListener("performancewatcher-propagate-notifications", + (...args) => ChildManager.notifyObservers(...args) + ); +} + +// Configure the performance stats service to inform us in case of jank. +performanceStatsService.jankAlertThreshold = 64000 /* us */; + + +/** + * Handle communications with child processes. Handle listening to + * either a single add-on id (including the special add-on id "*", + * which is notified for all add-ons) or a single window id (including + * the special window id 0, which is notified for all windows). + * + * Acquire through `ChildManager.getAddon` and `ChildManager.getWindow`. + */ +function ChildManager(map, key) { + this.key = key; + this._map = map; + this._listeners = new Set(); +} +ChildManager.prototype = { + /** + * Add a listener, which will be notified whenever a child process + * reports a slow performance alert for this addon/window. + */ + addListener: function(listener) { + this._listeners.add(listener); + }, + /** + * Remove a listener. + */ + removeListener: function(listener) { + let deleted = this._listeners.delete(listener); + if (!deleted) { + throw new Error("Unknown listener"); + } + }, + + listeners: function() { + return this._listeners.values(); + } +}; + +/** + * Dispatch child alerts to observers. + * + * Triggered by messages from content processes. + */ +ChildManager.notifyObservers = function({data: {addons, windows}}) { + if (addons && addons.length > 0) { + // Dispatch the entire list to universal listeners + this._notify(ChildManager.getAddon("*").listeners(), addons); + + // Dispatch individual alerts to individual listeners + for (let {source, details} of addons) { + this._notify(ChildManager.getAddon(source.addonId).listeners(), source, details); + } + } + if (windows && windows.length > 0) { + // Dispatch the entire list to universal listeners + this._notify(ChildManager.getWindow(0).listeners(), windows); + + // Dispatch individual alerts to individual listeners + for (let {source, details} of windows) { + this._notify(ChildManager.getWindow(source.windowId).listeners(), source, details); + } + } +}; + +ChildManager._notify = function(targets, ...args) { + for (let target of targets) { + target(...args); + } +}; + +ChildManager.getAddon = function(key) { + return this._get(this._addons, key); +}; +ChildManager._addons = new Map(); + +ChildManager.getWindow = function(key) { + return this._get(this._windows, key); +}; +ChildManager._windows = new Map(); + +ChildManager._get = function(map, key) { + let result = map.get(key); + if (!result) { + result = new ChildManager(map, key); + map.set(key ,result); + } + return result; +}; +let gListeners = new WeakMap(); + +/** + * An object in charge of managing all the observables for a single + * target (window/addon/all windows/all addons). + * + * In a content process, a target is represented by a single observable. + * The situation is more sophisticated in a parent process, as a target + * has both an in-process observable and several observables across children + * processes. + * + * This class abstracts away the difference to simplify the work of + * (un)registering observers for targets. + * + * @param {object} target The target being observed, as an object + * with one of the following fields: + * - {string} addonId Either "*" for the universal add-on observer + * or the add-on id of an addon. Note that this class does not + * check whether the add-on effectively exists, and that observers + * may be registered for an add-on before the add-on is installed + * or started. + * - {xul:tab} tab A single tab. It must already be initialized. + * - {number} windowId Either 0 for the universal window observer + * or the outer window id of the window. + */ +function Observable(target) { + // A mapping from `listener` (function) to `Observer`. + this._observers = new Map(); + if ("addonId" in target) { + this._key = `addonId: ${target.addonId}`; + this._process = performanceStatsService.getObservableAddon(target.addonId); + this._children = isContent ? null : ChildManager.getAddon(target.addonId); + this._isBuffered = target.addonId == "*"; + } else if ("tab" in target || "windowId" in target) { + let windowId; + if ("tab" in target) { + windowId = target.tab.linkedBrowser.outerWindowID; + // By convention, outerWindowID may not be 0. + } else if ("windowId" in target) { + windowId = target.windowId; + } + if (windowId == undefined || windowId == null) { + throw new TypeError(`No outerWindowID. Perhaps the target is a tab that is not initialized yet.`); + } + this._key = `tab-windowId: ${windowId}`; + this._process = performanceStatsService.getObservableWindow(windowId); + this._children = isContent ? null : ChildManager.getWindow(windowId); + this._isBuffered = windowId == 0; + } else { + throw new TypeError("Unexpected target"); + } +} +Observable.prototype = { + addJankObserver: function(listener) { + if (this._observers.has(listener)) { + throw new TypeError(`Listener already registered for target ${this._key}`); + } + if (this._children) { + this._children.addListener(listener); + } + let observer = this._isBuffered ? new BufferedObserver(listener) + : new Observer(listener); + // Store the observer to be able to call `this._process.removeJankObserver`. + this._observers.set(listener, observer); + + this._process.addJankObserver(observer); + }, + removeJankObserver: function(listener) { + let observer = this._observers.get(listener); + if (!observer) { + throw new TypeError(`No listener for target ${this._key}`); + } + this._observers.delete(listener); + + if (this._children) { + this._children.removeListener(listener); + } + + this._process.removeJankObserver(observer); + observer.dispose(); + }, +}; + +/** + * Get a cached observable for a given target. + */ +Observable.get = function(target) { + let key; + if ("addonId" in target) { + key = target.addonId; + } else if ("tab" in target) { + // We do not want to use a tab as a key, as this would prevent it from + // being garbage-collected. + key = target.tab.linkedBrowser.outerWindowID; + } else if ("windowId" in target) { + key = target.windowId; + } + if (key == null) { + throw new TypeError(`Could not extract a key from ${JSON.stringify(target)}. Could the target be an unitialized tab?`); + } + let observable = this._cache.get(key); + if (!observable) { + observable = new Observable(target); + this._cache.set(key, observable); + } + return observable; +}; +Observable._cache = new Map(); + +/** + * Wrap a listener callback as an unbuffered nsIPerformanceObserver. + * + * Each observation is propagated immediately to the listener. + */ +function Observer(listener) { + // Make sure that monitoring stays alive (in all processes) at least as + // long as the observer. + this._monitor = PerformanceStats.getMonitor(["jank", "cpow"]); + this._listener = listener; +} +Observer.prototype = { + observe: function(...args) { + this._listener(...args); + }, + dispose: function() { + this._monitor.dispose(); + this.observe = function poison() { + throw new Error("Internal error: I should have stopped receiving notifications"); + } + }, +}; + +/** + * Wrap a listener callback as an buffered nsIPerformanceObserver. + * + * Observations are buffered and dispatch in the next tick to the listener. + */ +function BufferedObserver(listener) { + Observer.call(this, listener); + this._buffer = []; + this._isDispatching = false; + this._pending = null; +} +BufferedObserver.prototype = Object.create(Observer.prototype); +BufferedObserver.prototype.observe = function(source, details) { + this._buffer.push({source, details}); + if (!this._isDispatching) { + this._isDispatching = true; + Services.tm.mainThread.dispatch(() => { + // Grab buffer, in case something in the listener could modify it. + let buffer = this._buffer; + this._buffer = []; + + // As of this point, any further observations need to use the new buffer + // and a new dispatcher. + this._isDispatching = false; + + this._listener(buffer); + }, Ci.nsIThread.DISPATCH_NORMAL); + } +}; + +this.PerformanceWatcher = { + /** + * Add a listener informed whenever we receive a slow performance alert + * in the application. + * + * @param {object} target An object with one of the following fields: + * - {string} addonId Either "*" to observe all add-ons or a full add-on ID. + * to observe a single add-on. + * - {number} windowId Either 0 to observe all windows or an outer window ID + * to observe a single tab. + * - {xul:browser} tab To observe a single tab. + * @param {function} listener A function that will be triggered whenever + * the target causes a slow performance notification. The notification may + * have originated in any process of the application. + * + * If the listener listens to a single add-on/webpage, it is triggered with + * the following arguments: + * source: {groupId, name, addonId, windowId, isSystem, processId} + * Information on the source of the notification. + * details: {reason, highestJank, highestCPOW} Information on the + * notification. + * + * If the listener listens to all add-ons/all webpages, it is triggered with + * an array of {source, details}, as described above. + */ + addPerformanceListener: function(target, listener) { + if (typeof listener != "function") { + throw new TypeError(); + } + let observable = Observable.get(target); + observable.addJankObserver(listener); + }, + removePerformanceListener: function(target, listener) { + if (typeof listener != "function") { + throw new TypeError(); + } + let observable = Observable.get(target); + observable.removeJankObserver(listener); + }, +}; diff --git a/toolkit/components/perfmonitoring/moz.build b/toolkit/components/perfmonitoring/moz.build index 8d8be7484d..e87cb0ee78 100644 --- a/toolkit/components/perfmonitoring/moz.build +++ b/toolkit/components/perfmonitoring/moz.build @@ -12,6 +12,8 @@ EXTRA_JS_MODULES += [ 'AddonWatcher.jsm', 'PerformanceStats-content.js', 'PerformanceStats.jsm', + 'PerformanceWatcher-content.js', + 'PerformanceWatcher.jsm', ] XPIDL_SOURCES += [ diff --git a/toolkit/components/perfmonitoring/nsIPerformanceStats.idl b/toolkit/components/perfmonitoring/nsIPerformanceStats.idl index d0e9e6dfbf..bb80552fec 100644 --- a/toolkit/components/perfmonitoring/nsIPerformanceStats.idl +++ b/toolkit/components/perfmonitoring/nsIPerformanceStats.idl @@ -16,14 +16,15 @@ */ /** - * Snapshot of the performance of a component, e.g. an add-on, a web - * page, system built-ins, a module or the entire process itself. + * Identification details for a performance group. * - * All values are monotonic and are updated only when - * `nsIPerformanceStatsService.isStopwatchActive` is `true`. + * A performance group is a set of JavaScript compartments whose + * performance is observed as a single entity. Typical examples of + * performance groups: an add-on, a webpage without its frames, a + * webpage with all its frames, the entire JS runtime, ... */ -[scriptable, uuid(f171c901-1888-4087-a002-c2751e510f92)] -interface nsIPerformanceStats: nsISupports { +[scriptable, builtinclass, uuid(994c56be-939a-4f20-8364-124f6422d86a)] +interface nsIPerformanceGroupDetails: nsISupports { /** * An identifier unique to the component. * @@ -49,6 +50,33 @@ interface nsIPerformanceStats: nsISupports { */ readonly attribute uint64_t windowId; + /** + * `true` if this component is executed with system privileges + * (e.g. the platform itself or an add-on), `false` otherwise + * (e.g. webpages). + */ + readonly attribute bool isSystem; + + /** + * The process running this group. + */ + readonly attribute unsigned long long processId; + + /** + * `true` if the code is executed in a content process, `false` otherwise. + */ + readonly attribute bool isContentProcess; +}; + +/** + * Snapshot of the performance of a component, e.g. an add-on, a web + * page, system built-ins, a module or the entire process itself. + * + * All values are monotonic and are updated only when + * `nsIPerformanceStatsService.isStopwatchActive` is `true`. + */ +[scriptable, builtinclass, uuid(8a635d4b-aa56-466b-9a7d-9f91ca9405ef)] +interface nsIPerformanceStats: nsIPerformanceGroupDetails { /** * Total amount of time spent executing code in this group, in * microseconds. @@ -64,18 +92,6 @@ interface nsIPerformanceStats: nsISupports { */ readonly attribute unsigned long long ticks; - /** - * `true` if this component is executed with system privileges - * (e.g. the platform itself or an add-on), `false` otherwise - * (e.g. webpages). - */ - readonly attribute bool isSystem; - - /** - * The process running this group. - */ - readonly attribute unsigned long long processId; - /** * Jank indicator. * @@ -89,7 +105,7 @@ interface nsIPerformanceStats: nsISupports { /** * A snapshot of the performance data of the process. */ -[scriptable, uuid(0ac38e2a-2613-4e3f-9f21-95f085c177de)] +[scriptable, builtinclass, uuid(13cc235b-739e-4690-b0e3-d89cbe036a93)] interface nsIPerformanceSnapshot: nsISupports { /** * Data on all individual components. @@ -107,7 +123,77 @@ interface nsIPerformanceSnapshot: nsISupports { nsIPerformanceStats getProcessData(); }; -[scriptable, builtinclass, uuid(aad18f7c-9ff7-4e22-8cd1-60ab0b57c698)] +/** + * A performance alert. + */ +[scriptable, function, uuid(463c92d9-2e55-4f57-9b22-7badbbdfb2b7)] +interface nsIPerformanceAlert: nsISupports { + // Alert was caused by jank exceeding the threshold. + const unsigned long REASON_JANK = 0; + + /** + * The reason for the alert. + */ + readonly attribute unsigned long reason; + + /** + * Longest interval spent executing code in this group + * since the latest alert, in microseconds. + */ + readonly attribute unsigned long long highestJank; + + /** + * Longest interval spent executing CPOW in this group + * since the latest alert, in microseconds. + */ + readonly attribute unsigned long long highestCPOW; +}; + + +/** + * An observer for slow performance alerts. + */ +[scriptable, function, uuid(b746a929-3fec-420b-8ed8-c35d71995e05)] +interface nsIPerformanceObserver: nsISupports { + /** + * @param target The performance group that caused the jank. + * @param alert The performance cost that triggered the alert. + */ + void observe(in nsIPerformanceGroupDetails target, in nsIPerformanceAlert alert); +}; + + +/** + * A part of the system that may be observed for slow performance. + */ +[scriptable, builtinclass, uuid(b85720d0-e328-4342-9e46-8ca1acf8c70e)] +interface nsIPerformanceObservable: nsISupports { + /** + * If a single group is being observed, information on this group. + */ + readonly attribute nsIPerformanceGroupDetails target; + + /** + * Add an observer that will be informed in case of jank. + * + * Set `jankAlertThreshold` to determine how much jank is needed + * to trigger alerts. + * + * If the same observer is added more than once, it will be + * triggered as many times as it has been added. + */ + void addJankObserver(in nsIPerformanceObserver observer); + + /** + * Remove an observer previously added with `addJankObserver`. + * + * Noop if the observer hasn't been added. + */ + void removeJankObserver(in nsIPerformanceObserver observer); +}; + + +[scriptable, uuid(5675d1d9-638e-4af0-9392-b04aacfad79a)] interface nsIPerformanceStatsService : nsISupports { /** * `true` if we should monitor CPOW, `false` otherwise. @@ -130,8 +216,52 @@ interface nsIPerformanceStatsService : nsISupports { * Capture a snapshot of the performance data. */ [implicit_jscontext] nsIPerformanceSnapshot getSnapshot(); + + /** + * The threshold, in microseconds, above which a performance group is + * considered "slow" and should raise performance alerts. + */ + attribute unsigned long long jankAlertThreshold; + + /** + * A buffering delay, in milliseconds, used by the service to + * regroup performance alerts, before observers are actually + * noticed. Higher delays let the system avoid redundant + * notifications for the same group, and are generally better for + * performance. + */ + attribute unsigned long jankAlertBufferingDelay; + + /** + * Get a nsIPerformanceObservable representing an add-on. This + * observable may then be used to (un)register for watching + * performance alerts for this add-on. + * + * Note that this method has no way of finding out whether an add-on with this + * id is installed on the system. Also note that this covers only the current + * process. + * + * Use special add-on name "*" to get an observable that may be used + * to (un)register for watching performance alerts of all add-ons at + * once. + */ + nsIPerformanceObservable getObservableAddon(in AString addonId); + + /** + * Get a nsIPerformanceObservable representing a DOM window. This + * observable may then be used to (un)register for watching + * performance alerts for this window. + * + * Note that this covers only the current process. + * + * Use special window id 0 to get an observable that may be used to + * (un)register for watching performance alerts of all windows at + * once. + */ + nsIPerformanceObservable getObservableWindow(in unsigned long long windowId); }; + %{C++ #define NS_TOOLKIT_PERFORMANCESTATSSERVICE_CID {0xfd7435d4, 0x9ec4, 0x4699, \ {0xad, 0xd4, 0x1b, 0xe8, 0x3d, 0xd6, 0x8e, 0xf3} } diff --git a/toolkit/components/perfmonitoring/nsPerformanceStats.cpp b/toolkit/components/perfmonitoring/nsPerformanceStats.cpp index 4fb9536474..187164e8a0 100644 --- a/toolkit/components/perfmonitoring/nsPerformanceStats.cpp +++ b/toolkit/components/perfmonitoring/nsPerformanceStats.cpp @@ -137,8 +137,97 @@ GenerateUniqueGroupId(const JSRuntime* rt, uint64_t uid, uint64_t processId, nsA } // namespace +/* ------------------------------------------------------ + * + * class nsPerformanceObservationTarget + * + */ +NS_IMPL_ISUPPORTS(nsPerformanceObservationTarget, nsIPerformanceObservable) + + + +NS_IMETHODIMP +nsPerformanceObservationTarget::GetTarget(nsIPerformanceGroupDetails** _result) { + if (mDetails) { + NS_IF_ADDREF(*_result = mDetails); + } + return NS_OK; +}; + +void +nsPerformanceObservationTarget::SetTarget(nsPerformanceGroupDetails* details) { + MOZ_ASSERT(!mDetails); + mDetails = details; +}; + +NS_IMETHODIMP +nsPerformanceObservationTarget::AddJankObserver(nsIPerformanceObserver* observer) { + if (!mObservers.append(observer)) { + MOZ_CRASH(); + } + return NS_OK; +}; + +NS_IMETHODIMP +nsPerformanceObservationTarget::RemoveJankObserver(nsIPerformanceObserver* observer) { + for (auto iter = mObservers.begin(), end = mObservers.end(); iter < end; ++iter) { + if (*iter == observer) { + mObservers.erase(iter); + return NS_OK; + } + } + return NS_OK; +}; + +bool +nsPerformanceObservationTarget::HasObservers() const { + return !mObservers.empty(); +} + +void +nsPerformanceObservationTarget::NotifyJankObservers(nsIPerformanceGroupDetails* source, nsIPerformanceAlert* gravity) { + // Copy the vector to make sure that it won't change under our feet. + mozilla::Vector> observers; + if (!observers.appendAll(mObservers)) { + MOZ_CRASH(); + } + + // Now actually notify. + for (auto iter = observers.begin(), end = observers.end(); iter < end; ++iter) { + nsCOMPtr observer = *iter; + mozilla::Unused << observer->Observe(source, gravity); + } +} + +/* ------------------------------------------------------ + * + * class nsGroupHolder + * + */ + +nsPerformanceObservationTarget* +nsGroupHolder::ObservationTarget() { + if (!mPendingObservationTarget) { + mPendingObservationTarget = new nsPerformanceObservationTarget(); + } + return mPendingObservationTarget; +} + +nsPerformanceGroup* +nsGroupHolder::GetGroup() { + return mGroup; +} + +void +nsGroupHolder::SetGroup(nsPerformanceGroup* group) { + MOZ_ASSERT(!mGroup); + mGroup = group; + group->SetObservationTarget(ObservationTarget()); + mPendingObservationTarget->SetTarget(group->Details()); +} + /* ------------------------------------------------------ * * struct PerformanceData @@ -160,6 +249,8 @@ PerformanceData::PerformanceData() * */ +NS_IMPL_ISUPPORTS(nsPerformanceGroupDetails, nsIPerformanceGroupDetails) + const nsAString& nsPerformanceGroupDetails::Name() const { return mName; @@ -200,105 +291,134 @@ nsPerformanceGroupDetails::IsWindow() const { return mWindowId != 0; } +bool +nsPerformanceGroupDetails::IsContentProcess() const { + return XRE_GetProcessType() == GeckoProcessType_Content; +} + +/* readonly attribute AString name; */ +NS_IMETHODIMP +nsPerformanceGroupDetails::GetName(nsAString& aName) { + aName.Assign(Name()); + return NS_OK; +}; + +/* readonly attribute AString groupId; */ +NS_IMETHODIMP +nsPerformanceGroupDetails::GetGroupId(nsAString& aGroupId) { + aGroupId.Assign(GroupId()); + return NS_OK; +}; + +/* readonly attribute AString addonId; */ +NS_IMETHODIMP +nsPerformanceGroupDetails::GetAddonId(nsAString& aAddonId) { + aAddonId.Assign(AddonId()); + return NS_OK; +}; + +/* readonly attribute uint64_t windowId; */ +NS_IMETHODIMP +nsPerformanceGroupDetails::GetWindowId(uint64_t *aWindowId) { + *aWindowId = WindowId(); + return NS_OK; +} + +/* readonly attribute bool isSystem; */ +NS_IMETHODIMP +nsPerformanceGroupDetails::GetIsSystem(bool *_retval) { + *_retval = IsSystem(); + return NS_OK; +} + +/* + readonly attribute unsigned long long processId; +*/ +NS_IMETHODIMP +nsPerformanceGroupDetails::GetProcessId(uint64_t* processId) { + *processId = ProcessId(); + return NS_OK; +} + +/* readonly attribute bool IsContentProcess; */ +NS_IMETHODIMP +nsPerformanceGroupDetails::GetIsContentProcess(bool *_retval) { + *_retval = IsContentProcess(); + return NS_OK; +} + + /* ------------------------------------------------------ * - * struct nsPerformanceStats + * class nsPerformanceStats * */ -class nsPerformanceStats: public nsIPerformanceStats, - public nsPerformanceGroupDetails +class nsPerformanceStats final: public nsIPerformanceStats { public: - nsPerformanceStats(const nsAString& aName, - const nsAString& aGroupId, - const nsAString& aAddonId, - const uint64_t aWindowId, - const uint64_t aProcessId, - const bool aIsSystem, - const PerformanceData& aPerformanceData) - : nsPerformanceGroupDetails(aName, aGroupId, aAddonId, aWindowId, aProcessId, aIsSystem) - , mPerformanceData(aPerformanceData) - { - } - nsPerformanceStats(const nsPerformanceGroupDetails& item, - const PerformanceData& aPerformanceData) - : nsPerformanceGroupDetails(item) - , mPerformanceData(aPerformanceData) - { - } - NS_DECL_ISUPPORTS + NS_DECL_NSIPERFORMANCESTATS + NS_FORWARD_NSIPERFORMANCEGROUPDETAILS(mDetails->) - NS_IMETHOD GetName(nsAString& aName) override { - aName.Assign(nsPerformanceGroupDetails::Name()); - return NS_OK; - }; - - NS_IMETHOD GetGroupId(nsAString& aGroupId) override { - aGroupId.Assign(nsPerformanceGroupDetails::GroupId()); - return NS_OK; - }; - - NS_IMETHOD GetAddonId(nsAString& aAddonId) override { - aAddonId.Assign(nsPerformanceGroupDetails::AddonId()); - return NS_OK; - }; - - NS_IMETHOD GetWindowId(uint64_t *aWindowId) override { - *aWindowId = nsPerformanceGroupDetails::WindowId(); - return NS_OK; + nsPerformanceStats(nsPerformanceGroupDetails* item, + const PerformanceData& aPerformanceData) + : mDetails(item) + , mPerformanceData(aPerformanceData) + { } - NS_IMETHOD GetIsSystem(bool *_retval) override { - *_retval = nsPerformanceGroupDetails::IsSystem(); - return NS_OK; - } - - NS_IMETHOD GetTotalUserTime(uint64_t *aTotalUserTime) override { - *aTotalUserTime = mPerformanceData.mTotalUserTime; - return NS_OK; - }; - - NS_IMETHOD GetTotalSystemTime(uint64_t *aTotalSystemTime) override { - *aTotalSystemTime = mPerformanceData.mTotalSystemTime; - return NS_OK; - }; - - NS_IMETHOD GetTotalCPOWTime(uint64_t *aCpowTime) override { - *aCpowTime = mPerformanceData.mTotalCPOWTime; - return NS_OK; - }; - - NS_IMETHOD GetTicks(uint64_t *aTicks) override { - *aTicks = mPerformanceData.mTicks; - return NS_OK; - }; - - NS_IMETHOD GetDurations(uint32_t *aCount, uint64_t **aNumberOfOccurrences) override { - const size_t length = mozilla::ArrayLength(mPerformanceData.mDurations); - if (aCount) { - *aCount = length; - } - *aNumberOfOccurrences = new uint64_t[length]; - for (size_t i = 0; i < length; ++i) { - (*aNumberOfOccurrences)[i] = mPerformanceData.mDurations[i]; - } - return NS_OK; - }; - - NS_IMETHODIMP GetProcessId(uint64_t* processId) override { - *processId = nsPerformanceGroupDetails::ProcessId(); - return NS_OK; - } private: + RefPtr mDetails; PerformanceData mPerformanceData; - virtual ~nsPerformanceStats() {} + ~nsPerformanceStats() {} }; -NS_IMPL_ISUPPORTS(nsPerformanceStats, nsIPerformanceStats) +NS_IMPL_ISUPPORTS(nsPerformanceStats, nsIPerformanceStats, nsIPerformanceGroupDetails) + +/* readonly attribute unsigned long long totalUserTime; */ +NS_IMETHODIMP +nsPerformanceStats::GetTotalUserTime(uint64_t *aTotalUserTime) { + *aTotalUserTime = mPerformanceData.mTotalUserTime; + return NS_OK; +}; + +/* readonly attribute unsigned long long totalSystemTime; */ +NS_IMETHODIMP +nsPerformanceStats::GetTotalSystemTime(uint64_t *aTotalSystemTime) { + *aTotalSystemTime = mPerformanceData.mTotalSystemTime; + return NS_OK; +}; + +/* readonly attribute unsigned long long totalCPOWTime; */ +NS_IMETHODIMP +nsPerformanceStats::GetTotalCPOWTime(uint64_t *aCpowTime) { + *aCpowTime = mPerformanceData.mTotalCPOWTime; + return NS_OK; +}; + +/* readonly attribute unsigned long long ticks; */ +NS_IMETHODIMP +nsPerformanceStats::GetTicks(uint64_t *aTicks) { + *aTicks = mPerformanceData.mTicks; + return NS_OK; +}; + +/* void getDurations (out unsigned long aCount, [array, size_is (aCount), retval] out unsigned long long aNumberOfOccurrences); */ +NS_IMETHODIMP +nsPerformanceStats::GetDurations(uint32_t *aCount, uint64_t **aNumberOfOccurrences) { + const size_t length = mozilla::ArrayLength(mPerformanceData.mDurations); + if (aCount) { + *aCount = length; + } + *aNumberOfOccurrences = new uint64_t[length]; + for (size_t i = 0; i < length; ++i) { + (*aNumberOfOccurrences)[i] = mPerformanceData.mDurations[i]; + } + return NS_OK; +}; /* ------------------------------------------------------ @@ -307,7 +427,7 @@ NS_IMPL_ISUPPORTS(nsPerformanceStats, nsIPerformanceStats) * */ -class nsPerformanceSnapshot : public nsIPerformanceSnapshot +class nsPerformanceSnapshot final : public nsIPerformanceSnapshot { public: NS_DECL_ISUPPORTS @@ -326,7 +446,7 @@ public: void SetProcessStats(nsIPerformanceStats* group); private: - virtual ~nsPerformanceSnapshot() {} + ~nsPerformanceSnapshot() {} private: /** @@ -343,6 +463,7 @@ private: NS_IMPL_ISUPPORTS(nsPerformanceSnapshot, nsIPerformanceSnapshot) +/* nsIArray getComponentsData (); */ NS_IMETHODIMP nsPerformanceSnapshot::GetComponentsData(nsIArray * *aComponents) { @@ -357,6 +478,7 @@ nsPerformanceSnapshot::GetComponentsData(nsIArray * *aComponents) return NS_OK; } +/* nsIPerformanceStats getProcessData (); */ NS_IMETHODIMP nsPerformanceSnapshot::GetProcessData(nsIPerformanceStats * *aProcess) { @@ -376,6 +498,128 @@ nsPerformanceSnapshot::SetProcessStats(nsIPerformanceStats* stats) mProcessData = stats; } + + +/* ------------------------------------------------------ + * + * class PerformanceAlert + * + */ +class PerformanceAlert final: public nsIPerformanceAlert { +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIPERFORMANCEALERT + + PerformanceAlert(const uint32_t reason, nsPerformanceGroup* source); +private: + ~PerformanceAlert() {} + + const uint32_t mReason; + + // The highest values reached by this group since the latest alert, + // in microseconds. + const uint64_t mHighestJank; + const uint64_t mHighestCPOW; +}; + +NS_IMPL_ISUPPORTS(PerformanceAlert, nsIPerformanceAlert); + +PerformanceAlert::PerformanceAlert(const uint32_t reason, nsPerformanceGroup* source) + : mReason(reason) + , mHighestJank(source->HighestRecentJank()) + , mHighestCPOW(source->HighestRecentCPOW()) +{ } + +NS_IMETHODIMP +PerformanceAlert::GetHighestJank(uint64_t* result) { + *result = mHighestJank; + return NS_OK; +} + +NS_IMETHODIMP +PerformanceAlert::GetHighestCPOW(uint64_t* result) { + *result = mHighestCPOW; + return NS_OK; +} + +NS_IMETHODIMP +PerformanceAlert::GetReason(uint32_t* result) { + *result = mReason; + return NS_OK; +} + +/* ------------------------------------------------------ + * + * class PendingAlertsCollector + * + */ + +/** + * A timer callback in charge of collecting the groups in + * `mPendingAlerts` and triggering dispatch of performance alerts. + */ +class PendingAlertsCollector final: public nsITimerCallback { +public: + NS_DECL_ISUPPORTS + NS_DECL_NSITIMERCALLBACK + + PendingAlertsCollector(JSRuntime* runtime, nsPerformanceStatsService* service) + : mService(service) + , mPending(false) + { } + + nsresult Start(uint32_t timerDelayMS); + nsresult Dispose(); +private: + ~PendingAlertsCollector() {} + + RefPtr mService; + bool mPending; + + nsCOMPtr mTimer; +}; + +NS_IMPL_ISUPPORTS(PendingAlertsCollector, nsITimerCallback); + +NS_IMETHODIMP +PendingAlertsCollector::Notify(nsITimer*) { + mPending = false; + mService->NotifyJankObservers(); + return NS_OK; +} + +nsresult +PendingAlertsCollector::Start(uint32_t timerDelayMS) { + if (mPending) { + // Collector is already started. + return NS_OK; + } + + if (!mTimer) { + mTimer = do_CreateInstance(NS_TIMER_CONTRACTID); + } + + nsresult rv = mTimer->InitWithCallback(this, timerDelayMS, nsITimer::TYPE_ONE_SHOT); + if (NS_FAILED(rv)) { + return rv; + } + + mPending = true; + return NS_OK; +} + +nsresult +PendingAlertsCollector::Dispose() { + if (mTimer) { + mozilla::Unused << mTimer->Cancel(); + mTimer = nullptr; + } + mService = nullptr; + return NS_OK; +} + + + /* ------------------------------------------------------ * * class nsPerformanceStatsService @@ -406,8 +650,33 @@ nsPerformanceStatsService::nsPerformanceStatsService() , mProcessMoved(0) , mProcessUpdateCounter(0) , mIsMonitoringPerCompartment(false) + , mJankAlertThreshold(mozilla::MaxValue::value) // By default, no alerts + , mJankAlertBufferingDelay(1000 /* ms */) +{ + mPendingAlertsCollector = new PendingAlertsCollector(mRuntime, this); -{ } + // Attach some artificial group information to the universal listeners, to aid with debugging. + nsString groupIdForAddons; + GenerateUniqueGroupId(mRuntime, GetNextId(), mProcessId, groupIdForAddons); + mUniversalTargets.mAddons-> + SetTarget(new nsPerformanceGroupDetails(NS_LITERAL_STRING(""), + groupIdForAddons, + NS_LITERAL_STRING(""), + 0, // window id + mProcessId, + false)); + + + nsString groupIdForWindows; + GenerateUniqueGroupId(mRuntime, GetNextId(), mProcessId, groupIdForWindows); + mUniversalTargets.mWindows-> + SetTarget(new nsPerformanceGroupDetails(NS_LITERAL_STRING(""), + groupIdForWindows, + NS_LITERAL_STRING(""), + 0, // window id + mProcessId, + false)); +} nsPerformanceStatsService::~nsPerformanceStatsService() { } @@ -445,6 +714,18 @@ nsPerformanceStatsService::Dispose() mozilla::Unused << js::SetStopwatchCommitCallback(mRuntime, nullptr, nullptr); mozilla::Unused << js::SetGetPerformanceGroupsCallback(mRuntime, nullptr, nullptr); + // Clear up and disconnect the alerts collector. + if (mPendingAlertsCollector) { + mPendingAlertsCollector->Dispose(); + mPendingAlertsCollector = nullptr; + } + mPendingAlerts.clear(); + + // Disconnect universal observers. Per-group observers will be + // disconnected below as part of `group->Dispose()`. + mUniversalTargets.mAddons = nullptr; + mUniversalTargets.mWindows = nullptr; + // At this stage, the JS VM may still be holding references to // instances of PerformanceGroup on the stack. To let the service be // collected, we need to break the references from these groups to @@ -456,9 +737,11 @@ nsPerformanceStatsService::Dispose() // not modify the hashtable while iterating it. GroupVector groups; for (auto iter = mGroups.Iter(); !iter.Done(); iter.Next()) { - groups.append(iter.Get()->GetKey()); + if (!groups.append(iter.Get()->GetKey())) { + MOZ_CRASH(); + } } - for (auto iter = groups.begin(); iter < groups.end(); iter++) { + for (auto iter = groups.begin(), end = groups.end(); iter < end; ++iter) { RefPtr group = *iter; group->Dispose(); } @@ -526,6 +809,7 @@ nsPerformanceStatsService::Observe(nsISupports *aSubject, const char *aTopic, return NS_OK; } +/* [implicit_jscontext] attribute bool isMonitoringCPOW; */ NS_IMETHODIMP nsPerformanceStatsService::GetIsMonitoringCPOW(JSContext* cx, bool *aIsStopwatchActive) { @@ -551,6 +835,7 @@ nsPerformanceStatsService::SetIsMonitoringCPOW(JSContext* cx, bool aIsStopwatchA return NS_OK; } +/* [implicit_jscontext] attribute bool isMonitoringJank; */ NS_IMETHODIMP nsPerformanceStatsService::GetIsMonitoringJank(JSContext* cx, bool *aIsStopwatchActive) { @@ -576,6 +861,7 @@ nsPerformanceStatsService::SetIsMonitoringJank(JSContext* cx, bool aIsStopwatchA return NS_OK; } +/* [implicit_jscontext] attribute bool isMonitoringPerCompartment; */ NS_IMETHODIMP nsPerformanceStatsService::GetIsMonitoringPerCompartment(JSContext*, bool *aIsMonitoringPerCompartment) { @@ -617,6 +903,30 @@ nsPerformanceStatsService::SetIsMonitoringPerCompartment(JSContext*, bool aIsMon return NS_OK; } +NS_IMETHODIMP +nsPerformanceStatsService::GetJankAlertThreshold(uint64_t* result) { + *result = mJankAlertThreshold; + return NS_OK; +} + +NS_IMETHODIMP +nsPerformanceStatsService::SetJankAlertThreshold(uint64_t value) { + mJankAlertThreshold = value; + return NS_OK; +} + +NS_IMETHODIMP +nsPerformanceStatsService::GetJankAlertBufferingDelay(uint32_t* result) { + *result = mJankAlertBufferingDelay; + return NS_OK; +} + +NS_IMETHODIMP +nsPerformanceStatsService::SetJankAlertBufferingDelay(uint32_t value) { + mJankAlertBufferingDelay = value; + return NS_OK; +} + nsresult nsPerformanceStatsService::UpdateTelemetry() { @@ -649,9 +959,10 @@ nsPerformanceStatsService::GetStatsForGroup(const js::PerformanceGroup* group) /* static */ nsIPerformanceStats* nsPerformanceStatsService::GetStatsForGroup(const nsPerformanceGroup* group) { - return new nsPerformanceStats(*group, group->data); + return new nsPerformanceStats(group->Details(), group->data); } +/* [implicit_jscontext] nsIPerformanceSnapshot getSnapshot (); */ NS_IMETHODIMP nsPerformanceStatsService::GetSnapshot(JSContext* cx, nsIPerformanceSnapshot * *aSnapshot) { @@ -702,7 +1013,10 @@ nsPerformanceStatsService::GetPerformanceGroups(JSContext* cx, JSGroupVector& ou } // All compartments belong to the top group. - out.append(mTopGroup); + if (!out.append(mTopGroup)) { + JS_ReportOutOfMemory(cx); + return false; + } nsAutoString name; CompartmentName(cx, global, name); @@ -715,18 +1029,22 @@ nsPerformanceStatsService::GetPerformanceGroups(JSContext* cx, JSGroupVector& ou if (jsaddonId) { AssignJSFlatString(addonId, (JSFlatString*)jsaddonId); auto entry = mAddonIdToGroup.PutEntry(addonId); - if (!entry->mGroup) { + if (!entry->GetGroup()) { nsString addonName = name; addonName.AppendLiteral(" (as addon "); addonName.Append(addonId); addonName.AppendLiteral(")"); - entry->mGroup = - nsPerformanceGroup::Make(mRuntime, this, - addonName, addonId, 0, - mProcessId, isSystem, - nsPerformanceGroup::GroupScope::ADDON); + entry-> + SetGroup(nsPerformanceGroup::Make(mRuntime, this, + addonName, addonId, 0, + mProcessId, isSystem, + nsPerformanceGroup::GroupScope::ADDON) + ); + } + if (!out.append(entry->GetGroup())) { + JS_ReportOutOfMemory(cx); + return false; } - out.append(entry->mGroup); } // Find out if the compartment is executed by a window. If so, its @@ -736,18 +1054,22 @@ nsPerformanceStatsService::GetPerformanceGroups(JSContext* cx, JSGroupVector& ou if (ptop) { windowId = ptop->WindowID(); auto entry = mWindowIdToGroup.PutEntry(windowId); - if (!entry->mGroup) { + if (!entry->GetGroup()) { nsString windowName = name; windowName.AppendLiteral(" (as window "); windowName.AppendInt(windowId); windowName.AppendLiteral(")"); - entry->mGroup = - nsPerformanceGroup::Make(mRuntime, this, - windowName, EmptyString(), windowId, - mProcessId, isSystem, - nsPerformanceGroup::GroupScope::WINDOW); + entry-> + SetGroup(nsPerformanceGroup::Make(mRuntime, this, + windowName, EmptyString(), windowId, + mProcessId, isSystem, + nsPerformanceGroup::GroupScope::WINDOW) + ); + } + if (!out.append(entry->GetGroup())) { + JS_ReportOutOfMemory(cx); + return false; } - out.append(entry->mGroup); } // All compartments have their own group. @@ -756,7 +1078,10 @@ nsPerformanceStatsService::GetPerformanceGroups(JSContext* cx, JSGroupVector& ou name, addonId, windowId, mProcessId, isSystem, nsPerformanceGroup::GroupScope::COMPARTMENT); - out.append(group); + if (!out.append(group)) { + JS_ReportOutOfMemory(cx); + return false; + } return true; } @@ -789,7 +1114,7 @@ bool nsPerformanceStatsService::StopwatchCommit(uint64_t iteration, JSGroupVector& recentGroups) { MOZ_ASSERT(iteration == mIteration); - MOZ_ASSERT(recentGroups.length() > 0); + MOZ_ASSERT(!recentGroups.empty()); uint64_t userTimeStop, systemTimeStop; nsresult rv = GetResources(&userTimeStop, &systemTimeStop); @@ -810,9 +1135,10 @@ nsPerformanceStatsService::StopwatchCommit(uint64_t iteration, JSGroupVector& re MOZ_ASSERT(mTopGroup->isUsedInThisIteration()); const uint64_t totalRecentCycles = mTopGroup->recentCycles(iteration); + // We should only reach this stage if `group` has had some activity. MOZ_ASSERT(mTopGroup->recentTicks(iteration) > 0); - for (auto iter = recentGroups.begin(); iter != recentGroups.end(); ++iter) { + for (auto iter = recentGroups.begin(), end = recentGroups.end(); iter != end; ++iter) { RefPtr group = nsPerformanceGroup::Get(*iter); CommitGroup(iteration, userTimeDelta, systemTimeDelta, totalRecentCycles, group); } @@ -820,6 +1146,11 @@ nsPerformanceStatsService::StopwatchCommit(uint64_t iteration, JSGroupVector& re // Make sure that `group` was treated along with the other items of `recentGroups`. MOZ_ASSERT(!mTopGroup->isUsedInThisIteration()); MOZ_ASSERT(mTopGroup->recentTicks(iteration) == 0); + + if (!mPendingAlerts.empty()) { + mPendingAlertsCollector->Start(mJankAlertBufferingDelay); + } + return true; } @@ -871,6 +1202,20 @@ nsPerformanceStatsService::CommitGroup(uint64_t iteration, ++i, duration *= 2) { group->data.mDurations[i]++; } + + group->RecordJank(totalTimeDelta); + group->RecordCPOW(cpowTimeDelta); + + if (totalTimeDelta >= mJankAlertThreshold) { + if (!group->HasPendingAlert()) { + if (mPendingAlerts.append(group)) { + group->SetHasPendingAlert(true); + } + return; + } + } + + return; } nsresult @@ -951,6 +1296,74 @@ nsPerformanceStatsService::GetResources(uint64_t* userTime, return NS_OK; } +void +nsPerformanceStatsService::NotifyJankObservers() { + GroupVector alerts; + mPendingAlerts.swap(alerts); + if (!mPendingAlertsCollector) { + // We are shutting down. + return; + } + + MOZ_ASSERT(!alerts.empty()); + const bool hasUniversalAddonObservers = mUniversalTargets.mAddons->HasObservers(); + const bool hasUniversalWindowObservers = mUniversalTargets.mWindows->HasObservers(); + for (auto iter = alerts.begin(); iter < alerts.end(); ++iter) { + RefPtr group = *iter; + group->SetHasPendingAlert(false); + + RefPtr details = group->Details(); + nsPerformanceObservationTarget* targets[3] = { + hasUniversalAddonObservers && details->IsAddon() ? mUniversalTargets.mAddons.get() : nullptr, + hasUniversalWindowObservers && details->IsWindow() ? mUniversalTargets.mWindows.get() : nullptr, + group->ObservationTarget() + }; + + RefPtr alert; + for (nsPerformanceObservationTarget* target : targets) { + if (!target) { + continue; + } + if (!alert) { + // Wait until we are sure we need to allocate before we allocate. + alert = new PerformanceAlert(nsIPerformanceAlert::REASON_JANK, group); + } + target->NotifyJankObservers(details, alert); + } + + group->ResetHighest(); + } + +} + +NS_IMETHODIMP +nsPerformanceStatsService::GetObservableAddon(const nsAString& addonId, + nsIPerformanceObservable** result) { + if (addonId.Equals(NS_LITERAL_STRING("*"))) { + NS_IF_ADDREF(*result = mUniversalTargets.mAddons); + } else { + auto entry = mAddonIdToGroup.PutEntry(addonId); + NS_IF_ADDREF(*result = entry->ObservationTarget()); + } + return NS_OK; +} + +NS_IMETHODIMP +nsPerformanceStatsService::GetObservableWindow(uint64_t windowId, + nsIPerformanceObservable** result) { + if (windowId == 0) { + NS_IF_ADDREF(*result = mUniversalTargets.mWindows); + } else { + auto entry = mWindowIdToGroup.PutEntry(windowId); + NS_IF_ADDREF(*result = entry->ObservationTarget()); + } + return NS_OK; +} + +nsPerformanceStatsService::UniversalTargets::UniversalTargets() + : mAddons(new nsPerformanceObservationTarget()) + , mWindows(new nsPerformanceObservationTarget()) +{ } /* ------------------------------------------------------ * @@ -981,22 +1394,25 @@ nsPerformanceGroup::nsPerformanceGroup(nsPerformanceStatsService* service, uint64_t processId, bool isSystem, GroupScope scope) - : nsPerformanceGroupDetails(name, groupId, addonId, windowId, processId, isSystem) + : mDetails(new nsPerformanceGroupDetails(name, groupId, addonId, windowId, processId, isSystem)) , mService(service) , mScope(scope) + , mHighestJank(0) + , mHighestCPOW(0) + , mHasPendingAlert(false) { mozilla::Unused << mService->mGroups.PutEntry(this); #if defined(DEBUG) if (scope == GroupScope::ADDON) { - MOZ_ASSERT(IsAddon()); - MOZ_ASSERT(!IsWindow()); + MOZ_ASSERT(mDetails->IsAddon()); + MOZ_ASSERT(!mDetails->IsWindow()); } else if (scope == GroupScope::WINDOW) { - MOZ_ASSERT(IsWindow()); - MOZ_ASSERT(!IsAddon()); + MOZ_ASSERT(mDetails->IsWindow()); + MOZ_ASSERT(!mDetails->IsAddon()); } else if (scope == GroupScope::RUNTIME) { - MOZ_ASSERT(!IsWindow()); - MOZ_ASSERT(!IsAddon()); + MOZ_ASSERT(!mDetails->IsWindow()); + MOZ_ASSERT(!mDetails->IsAddon()); } #endif // defined(DEBUG) setIsActive(mScope != GroupScope::COMPARTMENT || mService->mIsMonitoringPerCompartment); @@ -1008,19 +1424,23 @@ nsPerformanceGroup::Dispose() { // We have already called `Dispose()`. return; } + if (mObservationTarget) { + mObservationTarget = nullptr; + } - // Remove any reference to the service + // Remove any reference to the service. RefPtr service; service.swap(mService); + // Remove any dangling pointer to `this`. service->mGroups.RemoveEntry(this); if (mScope == GroupScope::ADDON) { - MOZ_ASSERT(IsAddon()); - service->mAddonIdToGroup.RemoveEntry(AddonId()); + MOZ_ASSERT(mDetails->IsAddon()); + service->mAddonIdToGroup.RemoveEntry(mDetails->AddonId()); } else if (mScope == GroupScope::WINDOW) { - MOZ_ASSERT(IsWindow()); - service->mWindowIdToGroup.RemoveEntry(WindowId()); + MOZ_ASSERT(mDetails->IsWindow()); + service->mWindowIdToGroup.RemoveEntry(mDetails->WindowId()); } } @@ -1032,3 +1452,61 @@ nsPerformanceGroup::GroupScope nsPerformanceGroup::Scope() const { return mScope; } + +nsPerformanceGroupDetails* +nsPerformanceGroup::Details() const { + return mDetails; +} + +void +nsPerformanceGroup::SetObservationTarget(nsPerformanceObservationTarget* target) { + MOZ_ASSERT(!mObservationTarget); + mObservationTarget = target; +} + +nsPerformanceObservationTarget* +nsPerformanceGroup::ObservationTarget() const { + return mObservationTarget; +} + +bool +nsPerformanceGroup::HasPendingAlert() const { + return mHasPendingAlert; +} + +void +nsPerformanceGroup::SetHasPendingAlert(bool value) { + mHasPendingAlert = value; +} + + +void +nsPerformanceGroup::RecordJank(uint64_t jank) { + if (jank > mHighestJank) { + mHighestJank = jank; + } +} + +void +nsPerformanceGroup::RecordCPOW(uint64_t cpow) { + if (cpow > mHighestCPOW) { + mHighestCPOW = cpow; + } +} + +uint64_t +nsPerformanceGroup::HighestRecentJank() { + return mHighestJank; +} + +uint64_t +nsPerformanceGroup::HighestRecentCPOW() { + return mHighestCPOW; +} + +void +nsPerformanceGroup::ResetHighest() { + mHighestJank = 0; + mHighestCPOW = 0; +} + diff --git a/toolkit/components/perfmonitoring/nsPerformanceStats.h b/toolkit/components/perfmonitoring/nsPerformanceStats.h index a9ae2823e8..52688b9c48 100644 --- a/toolkit/components/perfmonitoring/nsPerformanceStats.h +++ b/toolkit/components/perfmonitoring/nsPerformanceStats.h @@ -17,17 +17,109 @@ #include "nsIPerformanceStats.h" class nsPerformanceGroup; +class nsPerformanceGroupDetails; typedef mozilla::Vector> JSGroupVector; typedef mozilla::Vector> GroupVector; +/** + * A data structure for registering observers interested in + * performance alerts. + * + * Each performance group owns a single instance of this class. + * Additionally, the service owns instances designed to observe the + * performance alerts in all add-ons (respectively webpages). + */ +class nsPerformanceObservationTarget final: public nsIPerformanceObservable { +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIPERFORMANCEOBSERVABLE + + /** + * `true` if this target has at least once performance observer + * registered, `false` otherwise. + */ + bool HasObservers() const; + + /** + * Notify all the observers that jank has happened. + */ + void NotifyJankObservers(nsIPerformanceGroupDetails* source, nsIPerformanceAlert* gravity); + + /** + * Set the details on the group being observed. + */ + void SetTarget(nsPerformanceGroupDetails* details); + +private: + ~nsPerformanceObservationTarget() {} + + // The observers for this target. We hold them as a vector, despite + // the linear removal cost, as we expect that the typical number of + // observers will be lower than 3, and that (un)registrations will + // be fairly infrequent. + mozilla::Vector> mObservers; + + // Details on the group being observed. May be `nullptr`. + RefPtr mDetails; +}; + +/** + * The base class for entries of maps from addon id/window id to + * performance group. + * + * Performance observers may be registered before their group is + * created (e.g., one may register an observer for an add-on before + * all its modules are loaded, or even before the add-on is loaded at + * all or for an observer for a webpage before all its iframes are + * loaded). This class serves to hold the observation target until the + * performance group may be created, and then to associate the + * observation target and the performance group. + */ +class nsGroupHolder { +public: + nsGroupHolder() + : mGroup(nullptr) + , mPendingObservationTarget(nullptr) + { } + + /** + * Get the observation target, creating it if necessary. + */ + nsPerformanceObservationTarget* ObservationTarget(); + + /** + * Get the group, if it has been created. + * + * May return `null` if the group hasn't been created yet. + */ + class nsPerformanceGroup* GetGroup(); + + /** + * Set the group. + * + * Once this method has been called, calling + * `this->ObservationTarget()` and `group->ObservationTarget()` is equivalent. + * + * Must only be called once. + */ + void SetGroup(class nsPerformanceGroup*); +private: + // The group. Initially `nullptr`, until we have called `SetGroup`. + class nsPerformanceGroup* mGroup; + + // The observation target. Instantiated by the first call to + // `ObservationTarget()`. + RefPtr mPendingObservationTarget; +}; + /** * An implementation of the nsIPerformanceStatsService. * * Note that this implementation is not thread-safe. */ -class nsPerformanceStatsService : public nsIPerformanceStatsService, - public nsIObserver +class nsPerformanceStatsService final : public nsIPerformanceStatsService, + public nsIObserver { public: NS_DECL_ISUPPORTS @@ -40,7 +132,7 @@ public: private: nsresult InitInternal(); void Dispose(); - virtual ~nsPerformanceStatsService(); + ~nsPerformanceStatsService(); protected: friend nsPerformanceGroup; @@ -120,12 +212,11 @@ protected: * by add-on id. Each item is shared by all the compartments * that belong to the add-on. */ - struct AddonIdToGroup: public nsStringHashKey { + struct AddonIdToGroup: public nsStringHashKey, + public nsGroupHolder { explicit AddonIdToGroup(const nsAString* key) : nsStringHashKey(key) - , mGroup(nullptr) { } - nsPerformanceGroup* mGroup; }; nsTHashtable mAddonIdToGroup; @@ -134,12 +225,11 @@ protected: * window id. Each item is shared by all the compartments that * belong to the window. */ - struct WindowIdToGroup: public nsUint64HashKey { + struct WindowIdToGroup: public nsUint64HashKey, + public nsGroupHolder { explicit WindowIdToGroup(const uint64_t* key) : nsUint64HashKey(key) - , mGroup(nullptr) {} - nsPerformanceGroup* mGroup; }; nsTHashtable mWindowIdToGroup; @@ -282,6 +372,78 @@ protected: * sandbox). Note that this makes measurements noticeably slower. */ bool mIsMonitoringPerCompartment; + + +public: + /********************************************************** + * + * Letting observers register themselves to watch for performance + * alerts. + * + * To avoid saturating clients with alerts (or even creating loops + * of alerts), each alert is buffered. At the end of each iteration + * of the event loop, groups that have caused performance alerts + * are registered in a set of pending alerts, and the collection + * timer hasn't been started yet, it is started. Once the timer + * firers, we gather all the pending alerts, empty the set and + * dispatch to observers. + */ + + /** + * Clear the set of pending alerts and dispatch the pending alerts + * to observers. + */ + void NotifyJankObservers(); + +private: + /** + * The set of groups for which we know that an alert should be + * raised. This set is cleared once `mPendingAlertsCollector` + * fires. + * + * Invariant: no group may appear twice in this vector. + */ + GroupVector mPendingAlerts; + + /** + * A timer callback in charge of collecting the groups in + * `mPendingAlerts` and triggering `NotifyJankObservers` to dispatch + * performance alerts. + */ + RefPtr mPendingAlertsCollector; + + + /** + * Observation targets that are not attached to a specific group. + */ + struct UniversalTargets { + UniversalTargets(); + /** + * A target for observers interested in watching all addons. + */ + RefPtr mAddons; + + /** + * A target for observers interested in watching all windows. + */ + RefPtr mWindows; + }; + UniversalTargets mUniversalTargets; + + /** + * The threshold, in microseconds, above which a performance group is + * considered "slow" and should raise performance alerts. + */ + uint64_t mJankAlertThreshold; + + /** + * A buffering delay, in milliseconds, used by the service to + * regroup performance alerts, before observers are actually + * noticed. Higher delays let the system avoid redundant + * notifications for the same group, and are generally better for + * performance. + */ + uint32_t mJankAlertBufferingDelay; }; @@ -331,8 +493,11 @@ struct PerformanceData { * Identification information for an item that can hold performance * data. */ -class nsPerformanceGroupDetails { +class nsPerformanceGroupDetails final: public nsIPerformanceGroupDetails { public: + NS_DECL_ISUPPORTS + NS_DECL_NSIPERFORMANCEGROUPDETAILS + nsPerformanceGroupDetails(const nsAString& aName, const nsAString& aGroupId, const nsAString& aAddonId, @@ -355,7 +520,10 @@ public: bool IsAddon() const; bool IsWindow() const; bool IsSystem() const; + bool IsContentProcess() const; private: + ~nsPerformanceGroupDetails() {} + const nsString mName; const nsString mGroupId; const nsString mAddonId; @@ -397,9 +565,7 @@ enum class PerformanceGroupScope { * This class is intended to be the sole implementation of * `js::PerformanceGroup`. */ -class nsPerformanceGroup final: public js::PerformanceGroup, - public nsPerformanceGroupDetails -{ +class nsPerformanceGroup final: public js::PerformanceGroup { public: // Ideally, we would define the enum class in nsPerformanceGroup, @@ -454,10 +620,33 @@ public: */ GroupScope Scope() const; + /** + * Identification details for this group. + */ + nsPerformanceGroupDetails* Details() const; + /** * Cleanup any references. */ void Dispose(); + + /** + * Set the observation target for this group. + * + * This method must be called exactly once, when the performance + * group is attached to its `nsGroupHolder`. + */ + void SetObservationTarget(nsPerformanceObservationTarget*); + + + /** + * `true` if we have already noticed that a performance alert should + * be raised for this group but we have not dispatched it yet, + * `false` otherwise. + */ + bool HasPendingAlert() const; + void SetHasPendingAlert(bool value); + protected: nsPerformanceGroup(nsPerformanceStatsService* service, const nsAString& name, @@ -479,9 +668,14 @@ protected: virtual void Delete() override { delete this; } - virtual ~nsPerformanceGroup(); + ~nsPerformanceGroup(); private: + /** + * Identification details for this group. + */ + RefPtr mDetails; + /** * The stats service. Used to perform cleanup during destruction. */ @@ -492,6 +686,66 @@ private: * should be (de)activated. */ const GroupScope mScope; + +// Observing performance alerts. + +public: + /** + * The observation target, used to register observers. + */ + nsPerformanceObservationTarget* ObservationTarget() const; + + /** + * Record a jank duration. + * + * Update the highest recent jank if necessary. + */ + void RecordJank(uint64_t jank); + uint64_t HighestRecentJank(); + + /** + * Record a CPOW duration. + * + * Update the highest recent CPOW if necessary. + */ + void RecordCPOW(uint64_t cpow); + uint64_t HighestRecentCPOW(); + + /** + * Reset highest recent CPOW/jank to 0. + */ + void ResetHighest(); +private: + /** + * The target used by observers to register for watching slow + * performance alerts caused by this group. + * + * May be nullptr for groups that cannot be watched (the top group). + */ + RefPtr mObservationTarget; + + /** + * The highest jank encountered since jank observers for this group + * were last called, in microseconds. + */ + uint64_t mHighestJank; + + /** + * The highest CPOW encountered since jank observers for this group + * were last called, in microseconds. + */ + uint64_t mHighestCPOW; + + /** + * `true` if this group has caused a performance alert and this alert + * hasn't been dispatched yet. + * + * We use this as part of the buffering of performance alerts. If + * the group generates several alerts several times during the + * buffering delay, we only wish to add the group once to the list + * of alerts. + */ + bool mHasPendingAlert; }; #endif diff --git a/toolkit/components/perfmonitoring/tests/browser/browser.ini b/toolkit/components/perfmonitoring/tests/browser/browser.ini index 4975f2c88d..19c94b513b 100644 --- a/toolkit/components/perfmonitoring/tests/browser/browser.ini +++ b/toolkit/components/perfmonitoring/tests/browser/browser.ini @@ -8,3 +8,6 @@ support-files = [browser_AddonWatcher.js] [browser_compartments.js] +[browser_addonPerformanceAlerts.js] +[browser_addonPerformanceAlerts_2.js] +[browser_webpagePerformanceAlerts.js] diff --git a/toolkit/components/perfmonitoring/tests/browser/browser_AddonWatcher.js b/toolkit/components/perfmonitoring/tests/browser/browser_AddonWatcher.js index b6bf958c4f..bfa796576e 100644 --- a/toolkit/components/perfmonitoring/tests/browser/browser_AddonWatcher.js +++ b/toolkit/components/perfmonitoring/tests/browser/browser_AddonWatcher.js @@ -5,17 +5,16 @@ "use strict"; +requestLongerTimeout(2); + Cu.import("resource://gre/modules/Promise.jsm", this); Cu.import("resource://gre/modules/AddonManager.jsm", this); -Cu.import("resource://gre/modules/AddonWatcher.jsm", this); Cu.import("resource://gre/modules/Services.jsm", this); const ADDON_URL = "http://example.com/browser/toolkit/components/perfmonitoring/tests/browser/browser_Addons_sample.xpi"; const ADDON_ID = "addonwatcher-test@mozilla.com"; add_task(function* init() { - AddonWatcher.uninit(); - info("Installing test add-on"); let installer = yield new Promise(resolve => AddonManager.getInstallForURL(ADDON_URL, resolve, "application/x-xpinstall")); if (installer.error) { @@ -36,58 +35,84 @@ add_task(function* init() { addon.uninstall() }); - Services.prefs.setIntPref("browser.addon-watch.interval", 1000); + + let freezeThreshold = Preferences.get("browser.addon-watch.freeze-threshold-micros", /* 5 seconds */ 5000000); + let jankThreshold = Preferences.get("browser.addon-watch.jank-threshold-micros", /* 256 ms == 8 frames*/ 256000); + let occurrencesBetweenAlerts = Preferences.get("browser.addon-watch.occurrences-between-alerts", 3); + let delayBetweenAlerts = Preferences.get("browser.addon-watch.delay-between-alerts-ms", 6 * 3600 * 1000 /* 6h */); + let delayBetweenFreezeAlerts = Preferences.get("browser.addon-watch.delay-between-freeze-alerts-ms", 2 * 60 * 1000 /* 2 min */); + let prescriptionDelay = Preferences.get("browser.addon-watch.prescription-delay", 5 * 60 * 1000 /* 5 minutes */); + let highestNumberOfAddonsToReport = Preferences.get("browser.addon-watch.max-simultaneous-reports", 1); + + Preferences.set("browser.addon-watch.warmup-ms", 0); + Preferences.set("browser.addon-watch.freeze-threshold-micros", 0); + Preferences.set("browser.addon-watch.jank-threshold-micros", 0); + Preferences.set("browser.addon-watch.occurrences-between-alerts", 0); + Preferences.set("browser.addon-watch.delay-between-alerts-ms", 0); + Preferences.set("browser.addon-watch.delay-between-freeze-alerts-ms", 0); + Preferences.set("browser.addon-watch.max-simultaneous-reports", 10000); + Preferences.set("browser.addon-watch.deactivate-after-idle-ms", 100000000); registerCleanupFunction(() => { - Services.prefs.clearUserPref("browser.addon-watch.interval"); + for (let k of [ + "browser.addon-watch.warmup-ms", + "browser.addon-watch.freeze-threshold-micros", + "browser.addon-watch.jank-threshold-micros", + "browser.addon-watch.occurrences-between-alerts", + "browser.addon-watch.delay-between-alerts-ms", + "browser.addon-watch.delay-between-freeze-alerts-ms", + "browser.addon-watch.max-simultaneous-reports", + "browser.addon-watch.deactivate-after-idle-ms" + ]) { + Preferences.reset(k); + } }); - let oldCanRecord = Services.telemetry.canRecord; + let oldCanRecord = Services.telemetry.canRecordExtended; Services.telemetry.canRecordExtended = true; + AddonWatcher.init(); + registerCleanupFunction(function () { + AddonWatcher.paused = true; Services.telemetry.canRecordExtended = oldCanRecord; }); }); // Utility function to burn some resource, trigger a reaction of the add-on watcher // and check both its notification and telemetry. -let burn_rubber = Task.async(function*({histogramName, topic, expectedReason, prefs, expectedMinSum}) { +let burn_rubber = Task.async(function*({histogramName, topic, expectedMinSum}) { + let detected = false; + let observer = (_, topic, id) => { + Assert.equal(id, ADDON_ID, "The add-on watcher has detected the misbehaving addon"); + detected = true; + }; + try { - for (let key of Object.keys(prefs)) { - Services.prefs.setIntPref(key, prefs[key]); - } info("Preparing add-on watcher"); - let wait = new Promise(resolve => AddonWatcher.init((id, reason) => { - Assert.equal(id, ADDON_ID, "The add-on watcher has detected the misbehaving addon"); - if (reason == expectedReason) { - resolve(reason); - } - })); - let done = false; - wait = wait.then(result => { - done = true; - return result; - }); + + Services.obs.addObserver(observer, AddonWatcher.TOPIC_SLOW_ADDON_DETECTED, false); let histogram = Services.telemetry.getKeyedHistogramById(histogramName); histogram.clear(); let snap1 = histogram.snapshot(ADDON_ID); Assert.equal(snap1.sum, 0, `Histogram ${histogramName} is initially empty for the add-on`); - while (!done) { + + let histogramUpdated = false; + do { + info(`Burning some CPU with ${topic}. This should cause an add-on watcher notification`); yield new Promise(resolve => setTimeout(resolve, 100)); - info("Burning some CPU. This should cause an add-on watcher notification"); Services.obs.notifyObservers(null, topic, ""); - } - let reason = yield wait; + yield new Promise(resolve => setTimeout(resolve, 100)); - Assert.equal(reason, expectedReason, "Reason is valid"); - let snap2 = histogram.snapshot(ADDON_ID); + let snap2 = histogram.snapshot(ADDON_ID); + histogramUpdated = snap2.sum > 0; + info(`For the moment, histogram ${histogramName} shows ${snap2.sum} => ${histogramUpdated}`); + info(`For the moment, we have ${detected?"":"NOT "}detected the slow add-on`); + } while (!histogramUpdated || !detected); - Assert.ok(snap2.sum >= expectedMinSum, `Histogram ${histogramName} recorded a gravity of ${snap2.sum}, expecting at least ${expectedMinSum}.`); + let snap3 = histogram.snapshot(ADDON_ID); + Assert.ok(snap3.sum >= expectedMinSum, `Histogram ${histogramName} recorded a gravity of ${snap3.sum}, expecting at least ${expectedMinSum}.`); } finally { - AddonWatcher.uninit(); - for (let key of Object.keys(prefs)) { - Services.prefs.clearUserPref(key); - } + Services.obs.removeObserver(observer, AddonWatcher.TOPIC_SLOW_ADDON_DETECTED); } }); @@ -95,17 +120,25 @@ let burn_rubber = Task.async(function*({histogramName, topic, expectedReason, pr // the add-on is misbehaving. add_task(function* test_burn_CPU() { yield burn_rubber({ - prefs: { - "browser.addon-watch.limits.longestDuration": 2, - "browser.addon-watch.limits.totalCPOWTime": -1, - }, - histogramName: "MISBEHAVING_ADDONS_JANK_LEVEL", + histogramName: "PERF_MONITORING_SLOW_ADDON_JANK_US", topic: "test-addonwatcher-burn-some-cpu", - expectedReason: "longestDuration", expectedMinSum: 7, }); }); +// Test that burning content CPU will cause the add-on watcher to notice that +// the add-on is misbehaving. +/* +Blocked by bug 1227283. +add_task(function* test_burn_content_CPU() { + yield burn_rubber({ + histogramName: "PERF_MONITORING_SLOW_ADDON_JANK_US", + topic: "test-addonwatcher-burn-some-content-cpu", + expectedMinSum: 7, + }); +}); +*/ + // Test that burning CPOW will cause the add-on watcher to notice that // the add-on is misbehaving. add_task(function* test_burn_CPOW() { @@ -114,13 +147,8 @@ add_task(function* test_burn_CPOW() { return; } yield burn_rubber({ - prefs: { - "browser.addon-watch.limits.longestDuration": -1, - "browser.addon-watch.limits.totalCPOWTime": 100, - }, - histogramName: "MISBEHAVING_ADDONS_CPOW_TIME_MS", + histogramName: "PERF_MONITORING_SLOW_ADDON_CPOW_US", topic: "test-addonwatcher-burn-some-cpow", - expectedReason: "totalCPOWTime", expectedMinSum: 400, }); }); diff --git a/toolkit/components/perfmonitoring/tests/browser/browser_Addons_sample.xpi b/toolkit/components/perfmonitoring/tests/browser/browser_Addons_sample.xpi index 92e0019d6c..d5762e1529 100644 Binary files a/toolkit/components/perfmonitoring/tests/browser/browser_Addons_sample.xpi and b/toolkit/components/perfmonitoring/tests/browser/browser_Addons_sample.xpi differ diff --git a/toolkit/components/perfmonitoring/tests/browser/browser_addonPerformanceAlerts.js b/toolkit/components/perfmonitoring/tests/browser/browser_addonPerformanceAlerts.js new file mode 100644 index 0000000000..5dd3c27b86 --- /dev/null +++ b/toolkit/components/perfmonitoring/tests/browser/browser_addonPerformanceAlerts.js @@ -0,0 +1,92 @@ +"use strict"; + +/** + * Tests for PerformanceWatcher watching slow addons. + */ + +add_task(function* init() { + // Get rid of buffering. + let service = Cc["@mozilla.org/toolkit/performance-stats-service;1"].getService( + Ci.nsIPerformanceStatsService); + let oldDelay = service.jankAlertBufferingDelay; + + service.jankAlertBufferingDelay = 0 /* ms */; + registerCleanupFunction(() => { + info("Cleanup"); + service.jankAlertBufferingDelay = oldDelay; + }); +}); + +add_task(function* test_install_addon_then_watch_it() { + for (let topic of ["burnCPU", "promiseBurnContentCPU", "promiseBurnCPOW"]) { + info(`Starting subtest ${topic}`); + info("Spawning fake add-on, making sure that the compartment is initialized"); + let addon = new AddonBurner(); + yield addon.promiseInitialized; + addon.burnCPU(); + + info(`Check that burning CPU triggers the real listener, but not the fake listener ${topic}`); + let realListener = new AddonListener(addon.addonId, (group, details) => { + if (group.addonId == addon.addonId) { + return details.highestJank; + } + throw new Error(`I shouldn't have been called with addon ${group.addonId}`); + }); + let fakeListener = new AddonListener(addon.addonId + "-fake-" + Math.random(), group => true); // This listener should never be triggered. + let universalListener = new AddonListener("*", alerts => { + info(`AddonListener: received alerts ${JSON.stringify(alerts)}`); + let alert = alerts.find(({source}) => { + return source.addonId == addon.addonId; + }); + if (alert) { + info(`AddonListener: I found an alert for ${addon.addonId}`); + return alert.details.highestJank; + } else { + info(`AddonListener: I didn't find any alert for ${addon.addonId}`); + return null; + } + }); + + // Waiting a little – listeners are buffered. + yield new Promise(resolve => setTimeout(resolve, 100)); + yield addon.run(topic, 10, realListener); + // Waiting a little – listeners are buffered. + yield new Promise(resolve => setTimeout(resolve, 100)); + + Assert.ok(realListener.triggered, `1. The real listener was triggered ${topic}`); + Assert.ok(universalListener.triggered, `1. The universal listener was triggered ${topic}`); + Assert.ok(!fakeListener.triggered, `1. The fake listener was not triggered ${topic}`); + Assert.ok(realListener.result >= 200000, `1. jank is at least 300ms (${realListener.result/1000}ms) ${topic}`); + + info(`Attempting to remove a performance listener incorrectly, check that this does not hurt our real listener ${topic}`); + Assert.throws(() => PerformanceWatcher.removePerformanceListener({addonId: addon.addonId}, () => {})); + Assert.throws(() => PerformanceWatcher.removePerformanceListener({addonId: addon.addonId + "-unbound-id-" + Math.random()}, realListener.listener)); + + yield addon.run(topic, 10, realListener); + // Waiting a little – listeners are buffered. + yield new Promise(resolve => setTimeout(resolve, 300)); + + Assert.ok(realListener.triggered, `2. The real listener was triggered ${topic}`); + Assert.ok(universalListener.triggered, `2. The universal listener was triggered ${topic}`); + Assert.ok(!fakeListener.triggered, `2. The fake listener was not triggered ${topic}`); + Assert.ok(realListener.result >= 200000, `2. jank is at least 300ms (${realListener.result/1000}ms) ${topic}`); + + info(`Attempting to remove correctly, check if the listener is still triggered ${topic}`); + realListener.unregister(); + yield addon.run(topic, 3, realListener); + Assert.ok(!realListener.triggered, `3. After being unregistered, the real listener was not triggered ${topic}`); + Assert.ok(universalListener.triggered, `3. The universal listener is still triggered ${topic}`); + + info("Unregistering universal listener"); + // Waiting a little – listeners are buffered. + yield new Promise(resolve => setTimeout(resolve, 100)); + universalListener.unregister(); + // Waiting a little – listeners are buffered. + yield new Promise(resolve => setTimeout(resolve, 100)); + yield addon.run(topic, 3, realListener); + Assert.ok(!universalListener.triggered, `4. After being unregistered, the universal listener is not triggered ${topic}`); + + fakeListener.unregister(); + addon.dispose(); + } +}); diff --git a/toolkit/components/perfmonitoring/tests/browser/browser_addonPerformanceAlerts_2.js b/toolkit/components/perfmonitoring/tests/browser/browser_addonPerformanceAlerts_2.js new file mode 100644 index 0000000000..2ff47bb011 --- /dev/null +++ b/toolkit/components/perfmonitoring/tests/browser/browser_addonPerformanceAlerts_2.js @@ -0,0 +1,25 @@ +"use strict"; + +/** + * Tests for PerformanceWatcher watching slow addons. + */ + +add_task(function* test_watch_addon_then_install_it() { + for (let topic of ["burnCPU", "promiseBurnContentCPU", "promiseBurnCPOW"]) { + let addonId = "addon:test_watch_addons_before_installing" + Math.random(); + let realListener = new AddonListener(addonId, (group, details) => { + if (group.addonId == addonId) { + return details.highestJank; + } + throw new Error(`I shouldn't have been called with addon ${group.addonId}`); + }); + + info("Now install the add-on, *after* having installed the listener"); + let addon = new AddonBurner(addonId); + + Assert.ok((yield addon.run(topic, 10, realListener)), `5. The real listener was triggered ${topic}`); + Assert.ok(realListener.result >= 200000, `5. jank is at least 200ms (${realListener.result}µs) ${topic}`); + realListener.unregister(); + addon.dispose(); + } +}); diff --git a/toolkit/components/perfmonitoring/tests/browser/browser_compartments.js b/toolkit/components/perfmonitoring/tests/browser/browser_compartments.js index 63f94d5772..268b0636bf 100644 --- a/toolkit/components/perfmonitoring/tests/browser/browser_compartments.js +++ b/toolkit/components/perfmonitoring/tests/browser/browser_compartments.js @@ -60,13 +60,13 @@ function frameScript() { }); } catch (ex) { Cu.reportError("Error in content (setup): " + ex); - Cu.reportError(ex.stack); + Cu.reportError(ex.stack); } } // A variant of `Assert` that doesn't spam the logs // in case of success. -let SilentAssert = { +var SilentAssert = { equal: function(a, b, msg) { if (a == b) { return; @@ -90,7 +90,7 @@ let SilentAssert = { } }; -let isShuttingDown = false; +var isShuttingDown = false; function monotinicity_tester(source, testName) { // In the background, check invariants: // - numeric data can only ever increase; @@ -177,7 +177,7 @@ function monotinicity_tester(source, testName) { let key = item.groupId; if (map.has(key)) { let old = map.get(key); - Assert.ok(false, `Component ${key} has already been seen. Latest: ${item.title||item.addonId||item.name}, previous: ${old.title||old.addonId||old.name}`); + Assert.ok(false, `Component ${key} has already been seen. Latest: ${item.addonId||item.name}, previous: ${old.addonId||old.name}`); } map.set(key, item); } @@ -254,10 +254,33 @@ add_task(function* test() { let {snapshot: stats} = (yield promiseContentResponse(browser, "compartments-test:getStatistics", null)); - let titles = [for(stat of stats.componentsData) stat.title]; - + // Attach titles to components. + let titles = []; + let map = new Map(); + let windows = Services.wm.getEnumerator("navigator:browser"); + while (windows.hasMoreElements()) { + let window = windows.getNext(); + let tabbrowser = window.gBrowser; + for (let browser of tabbrowser.browsers) { + let id = browser.outerWindowID; // May be `null` if the browser isn't loaded yet + if (id != null) { + map.set(id, browser); + } + } + } for (let stat of stats.componentsData) { - info(`Compartment: ${stat.name} => ${stat.title} (${stat.isSystem?"system":"web"})`); + if (!stat.windowId) { + continue; + } + let browser = map.get(stat.windowId); + if (!browser) { + continue; + } + let title = browser.contentTitle; + if (title) { + stat.title = title; + titles.push(title); + } } // While the webpage consists in three compartments, we should see only @@ -283,11 +306,10 @@ add_task(function* test() { break; } else { info(`Not enough CPU time detected: ${parent.jank.totalUserTime}`); - info(`Details: ${JSON.stringify(parent, null, "\t")}`); } } isShuttingDown = true; // Cleanup - gBrowser.removeTab(newTab); + gBrowser.removeTab(newTab, {skipPermitUnload: true}); }); diff --git a/toolkit/components/perfmonitoring/tests/browser/browser_webpagePerformanceAlerts.js b/toolkit/components/perfmonitoring/tests/browser/browser_webpagePerformanceAlerts.js new file mode 100644 index 0000000000..3780ca78be --- /dev/null +++ b/toolkit/components/perfmonitoring/tests/browser/browser_webpagePerformanceAlerts.js @@ -0,0 +1,111 @@ +"use strict"; + +/** + * Tests for PerformanceWatcher watching slow web pages. + */ + + /** + * Simulate a slow webpage. + */ +function WebpageBurner() { + CPUBurner.call(this, "http://example.com/browser/toolkit/components/perfmonitoring/tests/browser/browser_compartments.html?test=" + Math.random()); +} +WebpageBurner.prototype = Object.create(CPUBurner.prototype); +WebpageBurner.prototype.promiseBurnContentCPU = function() { + return promiseContentResponse(this._browser, "test-performance-watcher:burn-content-cpu", {}); +}; + +function WebpageListener(windowId, accept) { + info(`Creating WebpageListener for ${windowId}`); + AlertListener.call(this, accept, { + register: () => PerformanceWatcher.addPerformanceListener({windowId}, this.listener), + unregister: () => PerformanceWatcher.removePerformanceListener({windowId}, this.listener) + }); +} +WebpageListener.prototype = Object.create(AlertListener.prototype); + +add_task(function* init() { + // Get rid of buffering. + let service = Cc["@mozilla.org/toolkit/performance-stats-service;1"].getService( + Ci.nsIPerformanceStatsService); + let oldDelay = service.jankAlertBufferingDelay; + + service.jankAlertBufferingDelay = 0 /* ms */; + registerCleanupFunction(() => { + info("Cleanup"); + service.jankAlertBufferingDelay = oldDelay; + }); +}); + +add_task(function* test_open_window_then_watch_it() { + let burner = new WebpageBurner(); + yield burner.promiseInitialized; + yield burner.promiseBurnContentCPU(); + + info(`Check that burning CPU triggers the real listener, but not the fake listener`); + let realListener = new WebpageListener(burner.windowId, (group, details) => { + info(`test: realListener for ${burner.tab.linkedBrowser.outerWindowID}: ${group}, ${details}\n`); + Assert.equal(group.windowId, burner.windowId, "We should not receive data meant for another group"); + return details; + }); // This listener should be triggered. + + info(`Creating fake burner`); + let otherTab = gBrowser.addTab(); + yield BrowserTestUtils.browserLoaded(otherTab.linkedBrowser); + info(`Check that burning CPU triggers the real listener, but not the fake listener`); + let fakeListener = new WebpageListener(otherTab.linkedBrowser.outerWindowID, group => group.windowId == burner.windowId); // This listener should never be triggered. + let universalListener = new WebpageListener(0, alerts => + alerts.find(alert => alert.source.windowId == burner.windowId) + ); + + // Waiting a little – listeners are buffered. + yield new Promise(resolve => setTimeout(resolve, 100)); + + yield burner.run("promiseBurnContentCPU", 20, realListener); + Assert.ok(realListener.triggered, `1. The real listener was triggered`); + Assert.ok(universalListener.triggered, `1. The universal listener was triggered`); + Assert.ok(!fakeListener.triggered, `1. The fake listener was not triggered`); + + if (realListener.result) { + Assert.ok(realListener.result.highestJank >= 300, `1. jank is at least 300ms (${realListener.result.highestJank}ms)`); + } + + info(`Attempting to remove a performance listener incorrectly, check that this does not hurt our real listener`); + Assert.throws(() => PerformanceWatcher.removePerformanceListener({addonId: addon.addonId}, () => {})); + Assert.throws(() => PerformanceWatcher.removePerformanceListener({addonId: addon.addonId + "-unbound-id-" + Math.random()}, realListener.listener)); + + // Waiting a little – listeners are buffered. + yield new Promise(resolve => setTimeout(resolve, 100)); + yield burner.run("promiseBurnContentCPU", 20, realListener); + // Waiting a little – listeners are buffered. + yield new Promise(resolve => setTimeout(resolve, 100)); + + Assert.ok(realListener.triggered, `2. The real listener was triggered`); + Assert.ok(universalListener.triggered, `2. The universal listener was triggered`); + Assert.ok(!fakeListener.triggered, `2. The fake listener was not triggered`); + if (realListener.result) { + Assert.ok(realListener.result.highestJank >= 300, `2. jank is at least 300ms (${realListener.jank}ms)`); + } + + info(`Attempting to remove correctly, check if the listener is still triggered`); + // Waiting a little – listeners are buffered. + yield new Promise(resolve => setTimeout(resolve, 100)); + realListener.unregister(); + + // Waiting a little – listeners are buffered. + yield new Promise(resolve => setTimeout(resolve, 100)); + yield burner.run("promiseBurnContentCPU", 3, realListener); + Assert.ok(!realListener.triggered, `3. After being unregistered, the real listener was not triggered`); + Assert.ok(universalListener.triggered, `3. The universal listener is still triggered`); + + universalListener.unregister(); + + // Waiting a little – listeners are buffered. + yield new Promise(resolve => setTimeout(resolve, 100)); + yield burner.run("promiseBurnContentCPU", 3, realListener); + Assert.ok(!universalListener.triggered, `4. After being unregistered, the universal listener is not triggered`); + + fakeListener.unregister(); + burner.dispose(); + gBrowser.removeTab(otherTab); +}); diff --git a/toolkit/components/perfmonitoring/tests/browser/head.js b/toolkit/components/perfmonitoring/tests/browser/head.js index 60ca6dd6e9..d8ab84bf0a 100644 --- a/toolkit/components/perfmonitoring/tests/browser/head.js +++ b/toolkit/components/perfmonitoring/tests/browser/head.js @@ -3,6 +3,228 @@ const { utils: Cu, interfaces: Ci, classes: Cc } = Components; +Cu.import("resource://gre/modules/Promise.jsm", this); +Cu.import("resource://gre/modules/AddonManager.jsm", this); +Cu.import("resource://gre/modules/AddonWatcher.jsm", this); +Cu.import("resource://gre/modules/PerformanceWatcher.jsm", this); +Cu.import("resource://gre/modules/Services.jsm", this); + +/** + * Base class for simulating slow addons/webpages. + */ +function CPUBurner(url) { + info(`CPUBurner: Opening tab for ${url}\n`); + this.url = url; + this.tab = gBrowser.addTab(url); + let browser = this.tab.linkedBrowser; + this._browser = browser; + ContentTask.spawn(this._browser, null, CPUBurner.frameScript); + this.promiseInitialized = BrowserTestUtils.browserLoaded(browser); +} +CPUBurner.prototype = { + get windowId() { + return this._browser.outerWindowID; + }, + /** + * Burn CPU until it triggers a listener. + */ + run: Task.async(function*(burner, max, listener) { + listener.reset(); + for (let i = 0; i < max; ++i) { + yield new Promise(resolve => setTimeout(resolve, 50)); + try { + yield this[burner](); + } catch (ex) { + return false; + } + if (listener.triggered) { + return true; + } + } + return false; + }), + dispose: function() { + info(`CPUBurner: Closing tab for ${this.url}\n`); + gBrowser.removeTab(this.tab); + } +}; +// This function is injected in all frames +CPUBurner.frameScript = function() { + try { + "use strict"; + + const { utils: Cu, classes: Cc, interfaces: Ci } = Components; + let sandboxes = new Map(); + let getSandbox = function(addonId) { + let sandbox = sandboxes.get(addonId); + if (!sandbox) { + sandbox = Components.utils.Sandbox(Services.scriptSecurityManager.getSystemPrincipal(), { addonId }); + sandboxes.set(addonId, sandbox); + } + return sandbox; + }; + + let burnCPU = function() { + var start = Date.now(); + var ignored = []; + while (Date.now() - start < 500) { + ignored[ignored.length % 2] = ignored.length; + } + }; + let burnCPUInSandbox = function(addonId) { + let sandbox = getSandbox(addonId); + Cu.evalInSandbox(burnCPU.toSource() + "()", sandbox); + }; + + { + let topic = "test-performance-watcher:burn-content-cpu"; + addMessageListener(topic, function(msg) { + try { + if (msg.data && msg.data.addonId) { + burnCPUInSandbox(msg.data.addonId); + } else { + burnCPU(); + } + sendAsyncMessage(topic, {}); + } catch (ex) { + dump(`This is the content attempting to burn CPU: error ${ex}\n`); + dump(`${ex.stack}\n`); + } + }); + } + + sendAsyncMessage("test-performance-watcher:cpow-init", {}, { + burnCPOWInSandbox: function(addonId) { + try { + burnCPUInSandbox(addonId); + } catch (ex) { + dump(`This is the addon attempting to burn CPOW: error ${ex}\n`); + dump(`${ex.stack}\n`); + } + } + }); + + } catch (ex) { + Cu.reportError("This is the addon: error " + ex); + Cu.reportError(ex.stack); + } +}; + +/** + * Base class for listening to slow group alerts + */ +function AlertListener(accept, {register, unregister}) { + this.listener = (...args) => { + if (this._unregistered) { + throw new Error("Listener was unregistered"); + } + let result = accept(...args); + if (!result) { + return; + } + this.result = result; + this.triggered = true; + return; + }; + this.triggered = false; + this.result = null; + this._unregistered = false; + this._unregister = unregister; + registerCleanupFunction(() => { + this.unregister(); + }); + register(); +} +AlertListener.prototype = { + unregister: function() { + this.reset(); + if (this._unregistered) { + info(`head.js: No need to unregister, we're already unregistered.\n`); + return; + } + info(`head.js: Unregistering listener.\n`); + this._unregistered = true; + this._unregister(); + info(`head.js: Unregistration complete.\n`); + }, + reset: function() { + this.triggered = false; + this.result = null; + }, +}; + +/** + * Simulate a slow add-on. + */ +function AddonBurner(addonId = "fake add-on id: " + Math.random()) { + CPUBurner.call(this, `http://example.com/?uri=${addonId}`) + this._addonId = addonId; + this._sandbox = Components.utils.Sandbox(Services.scriptSecurityManager.getSystemPrincipal(), { addonId: this._addonId }); + this._promiseCPOWBurner = new Promise(resolve => { + this._browser.messageManager.addMessageListener("test-performance-watcher:cpow-init", msg => { + resolve(msg.objects.burnCPOWInSandbox); + }); + }); +} +AddonBurner.prototype = Object.create(CPUBurner.prototype); +Object.defineProperty(AddonBurner.prototype, "addonId", { + get: function() { + return this._addonId; + } +}); + +/** + * Simulate slow code being executed by the add-on in the chrome. + */ +AddonBurner.prototype.burnCPU = function() { + Cu.evalInSandbox(AddonBurner.burnCPU.toSource() + "()", this._sandbox); +}; + +/** + * Simulate slow code being executed by the add-on in a CPOW. + */ +AddonBurner.prototype.promiseBurnCPOW = Task.async(function*() { + let burner = yield this._promiseCPOWBurner; + info("Parent: Preparing to burn CPOW"); + try { + yield burner(this._addonId); + info("Parent: Done burning CPOW"); + } catch (ex) { + info(`Parent: Error burning CPOW: ${ex}\n`); + info(ex.stack + "\n"); + } +}); + +/** + * Simulate slow code being executed by the add-on in the content. + */ +AddonBurner.prototype.promiseBurnContentCPU = function() { + return promiseContentResponse(this._browser, "test-performance-watcher:burn-content-cpu", {addonId: this._addonId}); +}; +AddonBurner.burnCPU = function() { + var start = Date.now(); + var ignored = []; + while (Date.now() - start < 500) { + ignored[ignored.length % 2] = ignored.length; + } +}; + + +function AddonListener(addonId, accept) { + let target = {addonId}; + AlertListener.call(this, accept, { + register: () => { + info(`AddonListener: registering ${JSON.stringify(target, null, "\t")}`); + PerformanceWatcher.addPerformanceListener({addonId}, this.listener); + }, + unregister: () => { + info(`AddonListener: unregistering ${JSON.stringify(target, null, "\t")}`); + PerformanceWatcher.removePerformanceListener({addonId}, this.listener); + } + }); +} +AddonListener.prototype = Object.create(AlertListener.prototype); + function promiseContentResponse(browser, name, message) { let mm = browser.messageManager; let promise = new Promise(resolve => { diff --git a/toolkit/components/printing/content/printjoboptions.js b/toolkit/components/printing/content/printjoboptions.js index 7ff711b9e8..041d1c4224 100644 --- a/toolkit/components/printing/content/printjoboptions.js +++ b/toolkit/components/printing/content/printjoboptions.js @@ -232,7 +232,6 @@ function createPaperSizeList(selectedInx) //--------------------------------------------------- function loadDialog() { - var print_paper_type = 0; var print_paper_unit = 0; var print_paper_width = 0.0; var print_paper_height = 0.0; @@ -243,7 +242,6 @@ function loadDialog() gPrefs = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefBranch); if (gPrintSettings) { - print_paper_type = gPrintSettings.paperSizeType; print_paper_unit = gPrintSettings.paperSizeUnit; print_paper_width = gPrintSettings.paperWidth; print_paper_height = gPrintSettings.paperHeight; @@ -353,7 +351,6 @@ function onLoad() //--------------------------------------------------- function onAccept() { - var print_paper_type = gPrintSettingsInterface.kPaperSizeDefined; var print_paper_unit = gPrintSettingsInterface.kPaperSizeInches; var print_paper_width = 0.0; var print_paper_height = 0.0; @@ -370,7 +367,6 @@ function onAccept() print_paper_height = gPaperArray[paperSelectedInx].height; print_paper_name = gPaperArray[paperSelectedInx].name; - gPrintSettings.paperSizeType = print_paper_type; gPrintSettings.paperSizeUnit = print_paper_unit; gPrintSettings.paperWidth = print_paper_width; gPrintSettings.paperHeight = print_paper_height; @@ -387,7 +383,6 @@ function onAccept() if (doDebug) { dump("onAccept******************************\n"); - dump("paperSizeType "+print_paper_type+" (should be 1)\n"); dump("paperSizeUnit "+print_paper_unit+"\n"); dump("paperWidth "+print_paper_width+"\n"); dump("paperHeight "+print_paper_height+"\n"); diff --git a/toolkit/components/promiseworker/PromiseWorker.jsm b/toolkit/components/promiseworker/PromiseWorker.jsm index a90b91477f..5f34b45ddc 100644 --- a/toolkit/components/promiseworker/PromiseWorker.jsm +++ b/toolkit/components/promiseworker/PromiseWorker.jsm @@ -290,11 +290,13 @@ this.BasePromiseWorker.prototype = { this.log("Posting message", message); try { this._worker.postMessage(message, ...[transfers]); - } catch (ex if typeof ex == "number") { - this.log("Could not post message", message, "due to xpcom error", ex); - // handle raw xpcom errors (see eg bug 961317) - throw new Components.Exception("Error in postMessage", ex); } catch (ex) { + if (typeof ex == "number") { + this.log("Could not post message", message, "due to xpcom error", ex); + // handle raw xpcom errors (see eg bug 961317) + throw new Components.Exception("Error in postMessage", ex); + } + this.log("Could not post message", message, "due to error", ex); throw ex; } diff --git a/toolkit/components/promiseworker/tests/xpcshell/test_Promise.js b/toolkit/components/promiseworker/tests/xpcshell/test_Promise.js index 24f91bcc48..eaeaba9689 100644 --- a/toolkit/components/promiseworker/tests/xpcshell/test_Promise.js +++ b/toolkit/components/promiseworker/tests/xpcshell/test_Promise.js @@ -55,7 +55,9 @@ add_task(function* test_rejected_promise_args() { try { yield worker.post("bounce", message); do_throw("I shound have thrown an error by now"); - } catch (ex if ex == error) { + } catch (ex) { + if (ex != error) + throw ex; do_print("I threw the right error"); } }); diff --git a/toolkit/components/reader/ReaderMode.jsm b/toolkit/components/reader/ReaderMode.jsm index 32ed4dc1b6..3005d426a1 100644 --- a/toolkit/components/reader/ReaderMode.jsm +++ b/toolkit/components/reader/ReaderMode.jsm @@ -193,7 +193,9 @@ this.ReaderMode = { try { let array = yield OS.File.read(path); return JSON.parse(new TextDecoder().decode(array)); - } catch (e if e instanceof OS.File.Error && e.becauseNoSuchFile) { + } catch (e) { + if (!(e instanceof OS.File.Error) || !e.becauseNoSuchFile) + throw e; return null; } }), diff --git a/toolkit/components/search/tests/xpcshell/test_rel_searchform.js b/toolkit/components/search/tests/xpcshell/test_rel_searchform.js index e49ae47b1b..79f217e0d4 100644 --- a/toolkit/components/search/tests/xpcshell/test_rel_searchform.js +++ b/toolkit/components/search/tests/xpcshell/test_rel_searchform.js @@ -26,7 +26,7 @@ add_task(function* test_rel_searchform() { // point of the ?search parameter is to avoid accidentally matching the value // returned as a last resort by Engine's searchForm getter, which is simply // the prePath of the engine's first HTML . - let items = [for (e of engineNames) { name: e, xmlFileName: e }]; + let items = engineNames.map(e => ({ name: e, xmlFileName: e })); for (let engine of yield addTestEngines(items)) { do_check_eq(engine.searchForm, "http://" + engine.name + "/?search"); } diff --git a/toolkit/components/social/test/xpcshell/.eslintrc b/toolkit/components/social/test/xpcshell/.eslintrc new file mode 100644 index 0000000000..8a895f93bd --- /dev/null +++ b/toolkit/components/social/test/xpcshell/.eslintrc @@ -0,0 +1,5 @@ +{ + "extends": [ + "../../../../../testing/xpcshell/xpcshell.eslintrc" + ] +} diff --git a/toolkit/components/social/test/xpcshell/head.js b/toolkit/components/social/test/xpcshell/head.js index b93e891321..e7a76ef7b7 100644 --- a/toolkit/components/social/test/xpcshell/head.js +++ b/toolkit/components/social/test/xpcshell/head.js @@ -2,7 +2,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ -const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components; +var { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components; Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); @@ -113,23 +113,20 @@ AsyncRunner.prototype = { this._iteratorQueue.push(iter); }, - next: function next(/* ... */) { + next: function next(arg) { if (!this._iteratorQueue.length) { this.destroy(); this._callbacks.done(); return; } - // send() discards all arguments after the first, so there's no choice here - // but to send only one argument to the yielder. - let args = [arguments.length <= 1 ? arguments[0] : Array.slice(arguments)]; try { - var val = this._iteratorQueue[0].send.apply(this._iteratorQueue[0], args); - } - catch (err if err instanceof StopIteration) { - this._iteratorQueue.shift(); - this.next(); - return; + var { done, value: val } = this._iteratorQueue[0].next(arg); + if (done) { + this._iteratorQueue.shift(); + this.next(); + return; + } } catch (err) { this._callbacks.error(err); diff --git a/toolkit/components/social/test/xpcshell/test_SocialService.js b/toolkit/components/social/test/xpcshell/test_SocialService.js index 2cb4d14074..6b91c61794 100644 --- a/toolkit/components/social/test/xpcshell/test_SocialService.js +++ b/toolkit/components/social/test/xpcshell/test_SocialService.js @@ -41,7 +41,7 @@ function run_test() { runner.next(); } -function testAddProviders(manifests, next) { +function* testAddProviders(manifests, next) { do_check_false(SocialService.enabled); let provider = yield SocialService.addProvider(manifests[0], next); do_check_true(SocialService.enabled); @@ -51,14 +51,14 @@ function testAddProviders(manifests, next) { do_check_false(provider.enabled); } -function testRemoveProviders(manifests, next) { +function* testRemoveProviders(manifests, next) { do_check_true(SocialService.enabled); yield SocialService.disableProvider(manifests[0].origin, next); yield SocialService.disableProvider(manifests[1].origin, next); do_check_false(SocialService.enabled); } -function testGetProvider(manifests, next) { +function* testGetProvider(manifests, next) { for (let i = 0; i < manifests.length; i++) { let manifest = manifests[i]; let provider = yield SocialService.getProvider(manifest.origin, next); @@ -70,7 +70,7 @@ function testGetProvider(manifests, next) { do_check_eq((yield SocialService.getProvider("bogus", next)), null); } -function testGetProviderList(manifests, next) { +function* testGetProviderList(manifests, next) { let providers = yield SocialService.getProviderList(next); do_check_true(providers.length >= manifests.length); for (let i = 0; i < manifests.length; i++) { @@ -83,7 +83,7 @@ function testGetProviderList(manifests, next) { } } -function testAddRemoveProvider(manifests, next) { +function* testAddRemoveProvider(manifests, next) { var threw; try { // Adding a provider whose origin already exists should fail @@ -116,7 +116,7 @@ function testAddRemoveProvider(manifests, next) { do_check_true(!newProvider); } -function testIsSameOrigin(manifests, next) { +function* testIsSameOrigin(manifests, next) { let providers = yield SocialService.getProviderList(next); let provider = providers[0]; // provider.origin is a string. @@ -134,7 +134,7 @@ function testIsSameOrigin(manifests, next) { do_check_false(provider.isSameOrigin(null)); } -function testResolveUri(manifests, next) { +function* testResolveUri(manifests, next) { let providers = yield SocialService.getProviderList(next); let provider = providers[0]; do_check_eq(provider.resolveUri(provider.origin).spec, provider.origin + "/"); @@ -144,7 +144,7 @@ function testResolveUri(manifests, next) { do_check_eq(provider.resolveUri("data:text/html,

hi").spec, "data:text/html,

hi"); } -function testOrderedProviders(manifests, next) { +function* testOrderedProviders(manifests, next) { let providers = yield SocialService.getProviderList(next); // add visits for only one of the providers diff --git a/toolkit/components/social/test/xpcshell/test_SocialServiceMigration21.js b/toolkit/components/social/test/xpcshell/test_SocialServiceMigration21.js index 5ee0597427..d2a996ddbb 100644 --- a/toolkit/components/social/test/xpcshell/test_SocialServiceMigration21.js +++ b/toolkit/components/social/test/xpcshell/test_SocialServiceMigration21.js @@ -30,7 +30,7 @@ function run_test() { runner.next(); } -function testMigration(manifest, next) { +function* testMigration(manifest, next) { // look at social.activeProviders, we should have migrated into that, and // we should be set as a user level pref after migration do_check_false(MANIFEST_PREFS.prefHasUserValue(manifest.origin)); diff --git a/toolkit/components/social/test/xpcshell/test_SocialServiceMigration22.js b/toolkit/components/social/test/xpcshell/test_SocialServiceMigration22.js index 2ba505583a..a0e80e9083 100644 --- a/toolkit/components/social/test/xpcshell/test_SocialServiceMigration22.js +++ b/toolkit/components/social/test/xpcshell/test_SocialServiceMigration22.js @@ -40,7 +40,7 @@ function run_test() { runner.next(); } -function testMigration(manifest, next) { +function* testMigration(manifest, next) { // look at social.activeProviders, we should have migrated into that, and // we should be set as a user level pref after migration do_check_false(MANIFEST_PREFS.prefHasUserValue(manifest.origin)); diff --git a/toolkit/components/social/test/xpcshell/test_SocialServiceMigration29.js b/toolkit/components/social/test/xpcshell/test_SocialServiceMigration29.js index e1ee1be64c..37d61d75f0 100644 --- a/toolkit/components/social/test/xpcshell/test_SocialServiceMigration29.js +++ b/toolkit/components/social/test/xpcshell/test_SocialServiceMigration29.js @@ -41,7 +41,7 @@ function run_test() { runner.next(); } -function testMigration(manifest, next) { +function* testMigration(manifest, next) { // look at social.activeProviders, we should have migrated into that, and // we should be set as a user level pref after migration do_check_true(Services.prefs.prefHasUserValue("social.enabled")); diff --git a/toolkit/components/startup/tests/unit/.eslintrc b/toolkit/components/startup/tests/unit/.eslintrc new file mode 100644 index 0000000000..8a895f93bd --- /dev/null +++ b/toolkit/components/startup/tests/unit/.eslintrc @@ -0,0 +1,5 @@ +{ + "extends": [ + "../../../../../testing/xpcshell/xpcshell.eslintrc" + ] +} diff --git a/toolkit/components/telemetry/Histograms.json b/toolkit/components/telemetry/Histograms.json index 2995e33802..37f629b914 100644 --- a/toolkit/components/telemetry/Histograms.json +++ b/toolkit/components/telemetry/Histograms.json @@ -4960,21 +4960,18 @@ "n_buckets": 10, "description": "Whenever the same-process MessageManager cannot be sent through sendAsyncMessage as it would cause an OOM, the size of the message content, in kb." }, - "MISBEHAVING_ADDONS_CPOW_TIME_MS": { - "expires_in_version": "never", - "kind": "exponential", - "low": 1, - "high": 10000, - "n_buckets": 20, - "keyed": true, - "description": "Time spent by an add-on performing blocking cross-process communications (ms, keyed by add-on ID, updated every 15s by default)" - }, - "MISBEHAVING_ADDONS_JANK_LEVEL": { + "SLOW_ADDON_WARNING_STATES": { "expires_in_version": "never", "kind": "enumerated", - "n_values": 10, - "keyed": true, - "description": "Longest blocking operation performed by the add-on (log2(duration in ms), keyed by add-on, updated every 15s by default)" + "n_values": 20, + "description": "The states the Slow Add-on Warning goes through. 0: Displayed the warning. 1: User clicked on 'Disable add-on'. 2: User clicked 'Ignore add-on for now'. 3: User clicked 'Ignore add-on permanently'. 4: User closed notification. Other values are reserved for future uses." + }, + "SLOW_ADDON_WARNING_RESPONSE_TIME": { + "expires_in_version": "never", + "kind": "exponential", + "high": "86400000", + "n_buckets": 30, + "description": "Time elapsed between before responding to Slow Add-on Warning UI (ms). Not updated if the user doesn't respond at all." }, "SEARCH_COUNTS": { "expires_in_version": "never", @@ -8873,6 +8870,24 @@ "n_buckets": "20", "description": "Proportion (%) of reschedulings of the main process to another CPU during the execution of code inside a JS compartment. Updated while we are measuring jank." }, + "PERF_MONITORING_SLOW_ADDON_JANK_US": { + "expires_in_version": "never", + "kind": "exponential", + "low": 1, + "high": 10000000, + "n_buckets": 20, + "keyed": true, + "description": "Contiguous time spent by an add-on blocking the main loop (microseconds, keyed by add-on ID)." + }, + "PERF_MONITORING_SLOW_ADDON_CPOW_US": { + "expires_in_version": "70", + "kind": "exponential", + "low": 1, + "high": 10000000, + "n_buckets": 20, + "keyed": true, + "description": "Contiguous time spent by an add-on blocking the main loop by performing a blocking cross-process call (microseconds, keyed by add-on ID)." + }, "FXA_CONFIGURED": { "alert_emails": ["fx-team@mozilla.com"], "expires_in_version": "45", diff --git a/toolkit/components/telemetry/Telemetry.cpp b/toolkit/components/telemetry/Telemetry.cpp index 908faf32e0..f601ab1b3c 100644 --- a/toolkit/components/telemetry/Telemetry.cpp +++ b/toolkit/components/telemetry/Telemetry.cpp @@ -16,6 +16,7 @@ #include "mozilla/DebugOnly.h" #include "mozilla/Likely.h" #include "mozilla/MathAlgorithms.h" +#include "mozilla/unused.h" #include "base/histogram.h" #include "base/pickle.h" @@ -3670,7 +3671,8 @@ TelemetryImpl::RecordThreadHangStats(Telemetry::ThreadHangStats& aStats) MutexAutoLock autoLock(sTelemetry->mThreadHangStatsMutex); - sTelemetry->mThreadHangStats.append(Move(aStats)); + // Ignore OOM. + mozilla::Unused << sTelemetry->mThreadHangStats.append(Move(aStats)); } NS_IMPL_ISUPPORTS(TelemetryImpl, nsITelemetry, nsIMemoryReporter) diff --git a/toolkit/components/telemetry/ThreadHangStats.h b/toolkit/components/telemetry/ThreadHangStats.h index 6ab077fa62..60aa680c87 100644 --- a/toolkit/components/telemetry/ThreadHangStats.h +++ b/toolkit/components/telemetry/ThreadHangStats.h @@ -187,7 +187,9 @@ public: void Add(PRIntervalTime aTime, HangMonitor::HangAnnotationsPtr aAnnotations) { TimeHistogram::Add(aTime); if (aAnnotations) { - mAnnotations.append(Move(aAnnotations)); + if (!mAnnotations.append(Move(aAnnotations))) { + MOZ_CRASH(); + } } } }; diff --git a/services/datareporting/datareporting-prefs.js b/toolkit/components/telemetry/datareporting-prefs.js similarity index 93% rename from services/datareporting/datareporting-prefs.js rename to toolkit/components/telemetry/datareporting-prefs.js index 020fdb78ff..706284cd16 100644 --- a/services/datareporting/datareporting-prefs.js +++ b/toolkit/components/telemetry/datareporting-prefs.js @@ -3,7 +3,6 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ pref("datareporting.policy.dataSubmissionEnabled", true); -pref("datareporting.policy.firstRunTime", "0"); pref("datareporting.policy.dataSubmissionPolicyNotifiedTime", "0"); pref("datareporting.policy.dataSubmissionPolicyAcceptedVersion", 0); pref("datareporting.policy.dataSubmissionPolicyBypassNotification", false); diff --git a/services/healthreport/docs/architecture.rst b/toolkit/components/telemetry/docs/fhr/architecture.rst similarity index 98% rename from services/healthreport/docs/architecture.rst rename to toolkit/components/telemetry/docs/fhr/architecture.rst index 36bafe6193..f5c9b3524b 100644 --- a/services/healthreport/docs/architecture.rst +++ b/toolkit/components/telemetry/docs/fhr/architecture.rst @@ -83,6 +83,9 @@ infoURL about.reportUrl The URL to load in ``about:healthreport``. +about.reportUrlUnified + The URL to load in ``about:healthreport``. This is used instead of ``reportUrl`` for UnifiedTelemetry when it is not opt-in. + service.providerCategories A comma-delimited list of category manager categories that contain registered ``Metrics.Provider`` records. Read below for how provider diff --git a/services/healthreport/docs/dataformat.rst b/toolkit/components/telemetry/docs/fhr/dataformat.rst similarity index 99% rename from services/healthreport/docs/dataformat.rst rename to toolkit/components/telemetry/docs/fhr/dataformat.rst index 21972a05a0..b067f9d0c7 100644 --- a/services/healthreport/docs/dataformat.rst +++ b/toolkit/components/telemetry/docs/fhr/dataformat.rst @@ -502,7 +502,7 @@ Here is an example JSON document for version 1:: "distributionID": "", "distributionVersion": "", "hotfixVersion": "", - "id": "{8de7fcbb-c55c-4fbe-bfc5-fc555c87dbc4}", + "id": "{ec8030f7-c20a-464f-9b0e-13a3a9e97384}", "locale": "en-US", "name": "Firefox", "os": "Darwin", @@ -835,7 +835,7 @@ This example comes from an official OS X Nightly build:: "distributionID": "", "distributionVersion": "", "hotfixVersion": "", - "id": "{8de7fcbb-c55c-4fbe-bfc5-fc555c87dbc4}", + "id": "{ec8030f7-c20a-464f-9b0e-13a3a9e97384}", "locale": "en-US", "name": "Firefox", "os": "Darwin", @@ -1993,5 +1993,5 @@ Example "enabled": 1, "numNewSavedPasswords": 5, "numSuccessfulFills": 11, - "totalLoginsEncountered": 23, + "numTotalLoginsEncountered": 23, } diff --git a/services/healthreport/docs/identifiers.rst b/toolkit/components/telemetry/docs/fhr/identifiers.rst similarity index 100% rename from services/healthreport/docs/identifiers.rst rename to toolkit/components/telemetry/docs/fhr/identifiers.rst diff --git a/services/healthreport/docs/index.rst b/toolkit/components/telemetry/docs/fhr/index.rst similarity index 81% rename from services/healthreport/docs/index.rst rename to toolkit/components/telemetry/docs/fhr/index.rst index 047863bb5a..f1b5aaba53 100644 --- a/services/healthreport/docs/index.rst +++ b/toolkit/components/telemetry/docs/fhr/index.rst @@ -1,11 +1,11 @@ .. _healthreport: -===================== -Firefox Health Report -===================== +================================ +Firefox Health Report (Obsolete) +================================ -``/services/healthreport`` contains the implementation of the -``Firefox Health Report`` (FHR). +**Firefox Health Report (FHR) is obsolete and no longer ships with Firefox. +This documentation will live here for a few more cycles.** Firefox Health Report is a background service that collects application metrics and periodically submits them to a central server. The core diff --git a/toolkit/components/telemetry/healthreport-prefs.js b/toolkit/components/telemetry/healthreport-prefs.js new file mode 100644 index 0000000000..021028e1ca --- /dev/null +++ b/toolkit/components/telemetry/healthreport-prefs.js @@ -0,0 +1,10 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +pref("datareporting.healthreport.infoURL", "https://www.mozilla.org/legal/privacy/firefox.html#health-report"); + +// Health Report is enabled by default on all channels. +pref("datareporting.healthreport.uploadEnabled", true); + +pref("datareporting.healthreport.about.reportUrl", "https://fhr.cdn.mozilla.net/%LOCALE%/v4/"); diff --git a/toolkit/components/telemetry/moz.build b/toolkit/components/telemetry/moz.build index dca40b4094..d9faaa6d35 100644 --- a/toolkit/components/telemetry/moz.build +++ b/toolkit/components/telemetry/moz.build @@ -59,3 +59,4 @@ LOCAL_INCLUDES += [ ] SPHINX_TREES['telemetry'] = 'docs' +SPHINX_TREES['healthreport'] = 'docs/fhr' diff --git a/toolkit/components/terminator/nsTerminatorTelemetry.js b/toolkit/components/terminator/nsTerminatorTelemetry.js index 9978c81633..73956d8a3f 100644 --- a/toolkit/components/terminator/nsTerminatorTelemetry.js +++ b/toolkit/components/terminator/nsTerminatorTelemetry.js @@ -64,7 +64,10 @@ nsTerminatorTelemetry.prototype = { let raw; try { raw = yield OS.File.read(PATH, { encoding: "utf-8" }); - } catch (ex if ex.becauseNoSuchFile) { + } catch (ex) { + if (!ex.becauseNoSuchFile) { + throw ex; + } return; } // Let other errors be reported by Promise's error-reporting. diff --git a/toolkit/components/workerloader/tests/.eslintrc b/toolkit/components/workerloader/tests/.eslintrc new file mode 100644 index 0000000000..e2b648435b --- /dev/null +++ b/toolkit/components/workerloader/tests/.eslintrc @@ -0,0 +1,5 @@ +{ + "extends": [ + "../../../../testing/mochitest/chrome.eslintrc" + ] +} diff --git a/toolkit/forgetaboutsite/ForgetAboutSite.jsm b/toolkit/forgetaboutsite/ForgetAboutSite.jsm index 513f88f18c..75219da23e 100644 --- a/toolkit/forgetaboutsite/ForgetAboutSite.jsm +++ b/toolkit/forgetaboutsite/ForgetAboutSite.jsm @@ -21,17 +21,16 @@ this.EXPORTED_SYMBOLS = ["ForgetAboutSite"]; * "mozilla.org", this will return true. It would return false the other way * around. */ -function hasRootDomain(str, aDomain) { +function hasRootDomain(str, aDomain) +{ let index = str.indexOf(aDomain); // If aDomain is not found, we know we do not have it as a root domain. - if (index == -1) { + if (index == -1) return false; - } // If the strings are the same, we obviously have a match. - if (str == aDomain) { + if (str == aDomain) return true; - } // Otherwise, we have aDomain as our root domain iff the index of aDomain is // aDomain.length subtracted from our length and (since we do not have an diff --git a/toolkit/forgetaboutsite/test/unit/test_removeDataFromDomain.js b/toolkit/forgetaboutsite/test/unit/test_removeDataFromDomain.js index d69f3f5b40..e6f1c608ab 100644 --- a/toolkit/forgetaboutsite/test/unit/test_removeDataFromDomain.js +++ b/toolkit/forgetaboutsite/test/unit/test_removeDataFromDomain.js @@ -288,7 +288,7 @@ function* test_history_not_cleared_with_uri_contains_domain() } // Cookie Service -function* test_cookie_cleared_with_direct_match() +function test_cookie_cleared_with_direct_match() { const TEST_DOMAIN = "mozilla.org"; add_cookie(TEST_DOMAIN); @@ -296,7 +296,7 @@ function* test_cookie_cleared_with_direct_match() check_cookie_exists(TEST_DOMAIN, false); } -function* test_cookie_cleared_with_subdomain() +function test_cookie_cleared_with_subdomain() { const TEST_DOMAIN = "www.mozilla.org"; add_cookie(TEST_DOMAIN); @@ -304,7 +304,7 @@ function* test_cookie_cleared_with_subdomain() check_cookie_exists(TEST_DOMAIN, false); } -function* test_cookie_not_cleared_with_uri_contains_domain() +function test_cookie_not_cleared_with_uri_contains_domain() { const TEST_DOMAIN = "ilovemozilla.org"; add_cookie(TEST_DOMAIN); @@ -313,7 +313,7 @@ function* test_cookie_not_cleared_with_uri_contains_domain() } // Login Manager -function* test_login_manager_disabled_hosts_cleared_with_direct_match() +function test_login_manager_disabled_hosts_cleared_with_direct_match() { const TEST_HOST = "http://mozilla.org"; add_disabled_host(TEST_HOST); @@ -321,7 +321,7 @@ function* test_login_manager_disabled_hosts_cleared_with_direct_match() check_disabled_host(TEST_HOST, false); } -function* test_login_manager_disabled_hosts_cleared_with_subdomain() +function test_login_manager_disabled_hosts_cleared_with_subdomain() { const TEST_HOST = "http://www.mozilla.org"; add_disabled_host(TEST_HOST); @@ -329,7 +329,7 @@ function* test_login_manager_disabled_hosts_cleared_with_subdomain() check_disabled_host(TEST_HOST, false); } -function* test_login_manager_disabled_hosts_not_cleared_with_uri_contains_domain() +function test_login_manager_disabled_hosts_not_cleared_with_uri_contains_domain() { const TEST_HOST = "http://ilovemozilla.org"; add_disabled_host(TEST_HOST); @@ -343,7 +343,7 @@ function* test_login_manager_disabled_hosts_not_cleared_with_uri_contains_domain check_disabled_host(TEST_HOST, false); } -function* test_login_manager_logins_cleared_with_direct_match() +function test_login_manager_logins_cleared_with_direct_match() { const TEST_HOST = "http://mozilla.org"; add_login(TEST_HOST); @@ -351,7 +351,7 @@ function* test_login_manager_logins_cleared_with_direct_match() check_login_exists(TEST_HOST, false); } -function* test_login_manager_logins_cleared_with_subdomain() +function test_login_manager_logins_cleared_with_subdomain() { const TEST_HOST = "http://www.mozilla.org"; add_login(TEST_HOST); @@ -373,7 +373,7 @@ function tets_login_manager_logins_not_cleared_with_uri_contains_domain() } // Permission Manager -function* test_permission_manager_cleared_with_direct_match() +function test_permission_manager_cleared_with_direct_match() { const TEST_URI = uri("http://mozilla.org"); add_permission(TEST_URI); @@ -381,7 +381,7 @@ function* test_permission_manager_cleared_with_direct_match() check_permission_exists(TEST_URI, false); } -function* test_permission_manager_cleared_with_subdomain() +function test_permission_manager_cleared_with_subdomain() { const TEST_URI = uri("http://www.mozilla.org"); add_permission(TEST_URI); @@ -389,7 +389,7 @@ function* test_permission_manager_cleared_with_subdomain() check_permission_exists(TEST_URI, false); } -function* test_permission_manager_not_cleared_with_uri_contains_domain() +function test_permission_manager_not_cleared_with_uri_contains_domain() { const TEST_URI = uri("http://ilovemozilla.org"); add_permission(TEST_URI); @@ -463,7 +463,7 @@ function* test_content_preferences_not_cleared_with_uri_contains_domain() } // Push -function test_push_cleared() +function* test_push_cleared() { let ps; try { @@ -533,7 +533,7 @@ function test_push_cleared() } // Cache -function* test_cache_cleared() +function test_cache_cleared() { // Because this test is asynchronous, it should be the last test do_check_true(tests[tests.length - 1] == arguments.callee); @@ -596,7 +596,7 @@ function* test_storage_cleared() do_check_eq(s[2].length, 1); } -let tests = [ +var tests = [ // History test_history_cleared_with_direct_match, test_history_cleared_with_subdomain, diff --git a/toolkit/mozapps/extensions/test/xpinstall/head.js b/toolkit/mozapps/extensions/test/xpinstall/head.js index 428a8abec1..fde3d357e2 100644 --- a/toolkit/mozapps/extensions/test/xpinstall/head.js +++ b/toolkit/mozapps/extensions/test/xpinstall/head.js @@ -168,7 +168,7 @@ var Harness = { this.runningInstalls = null; if (callback) - callback(count); + executeSoon(() => callback(count)); }, // Window open handling diff --git a/widget/gtk/nsNativeThemeGTK.cpp b/widget/gtk/nsNativeThemeGTK.cpp index f3f0a2ce63..c2e0fdb0a7 100644 --- a/widget/gtk/nsNativeThemeGTK.cpp +++ b/widget/gtk/nsNativeThemeGTK.cpp @@ -860,7 +860,7 @@ DrawThemeWithCairo(gfxContext* aContext, DrawTarget* aDrawTarget, // If using a Cairo xlib surface, then try to reuse it. BorrowedXlibDrawable borrow(aDrawTarget); if (borrow.GetDrawable()) { - nsIntSize size = aDrawTarget->GetSize(); + nsIntSize size = borrow.GetSize(); cairo_surface_t* surf = nullptr; // Check if the surface is using XRender. #ifdef CAIRO_HAS_XLIB_XRENDER_SURFACE @@ -877,6 +877,10 @@ DrawThemeWithCairo(gfxContext* aContext, DrawTarget* aDrawTarget, size.width, size.height); } if (!NS_WARN_IF(!surf)) { + Point offset = borrow.GetOffset(); + if (offset != Point()) { + cairo_surface_set_device_offset(surf, offset.x, offset.y); + } cairo_t* cr = cairo_create(surf); if (!NS_WARN_IF(!cr)) { RefPtr clipper = new SystemCairoClipper(cr); @@ -901,12 +905,16 @@ DrawThemeWithCairo(gfxContext* aContext, DrawTarget* aDrawTarget, nsIntSize size; int32_t stride; SurfaceFormat format; - if (aDrawTarget->LockBits(&data, &size, &stride, &format)) { + IntPoint origin; + if (aDrawTarget->LockBits(&data, &size, &stride, &format, &origin)) { // Create a Cairo image surface context the device rectangle. cairo_surface_t* surf = cairo_image_surface_create_for_data( data, GfxFormatToCairoFormat(format), size.width, size.height, stride); if (!NS_WARN_IF(!surf)) { + if (origin != IntPoint()) { + cairo_surface_set_device_offset(surf, -origin.x, -origin.y); + } cairo_t* cr = cairo_create(surf); if (!NS_WARN_IF(!cr)) { RefPtr clipper = new SystemCairoClipper(cr); diff --git a/widget/nsDeviceContextSpecProxy.cpp b/widget/nsDeviceContextSpecProxy.cpp index 560816ce27..5aa471025f 100644 --- a/widget/nsDeviceContextSpecProxy.cpp +++ b/widget/nsDeviceContextSpecProxy.cpp @@ -67,15 +67,8 @@ nsDeviceContextSpecProxy::GetSurfaceForPrinter(gfxASurface** aSurface) MOZ_ASSERT(aSurface); MOZ_ASSERT(mRealDeviceContextSpec); - // The real device context may need to have created a real printing surface - // even though we're not using it directly. - nsresult rv = mRealDeviceContextSpec->GetSurfaceForPrinter(aSurface); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - double width, height; - rv = mPrintSettings->GetEffectivePageSize(&width, &height); + nsresult rv = mPrintSettings->GetEffectivePageSize(&width, &height); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } diff --git a/widget/nsIPrintSettings.idl b/widget/nsIPrintSettings.idl index 64133137b8..370d65c8c8 100644 --- a/widget/nsIPrintSettings.idl +++ b/widget/nsIPrintSettings.idl @@ -22,7 +22,7 @@ interface nsIPrintSession; /** * Simplified graphics interface for JS rendering. */ -[scriptable, uuid(04dd3a01-a74e-44aa-8d49-2c30478fd7b8)] +[scriptable, uuid(ecc5cbad-57fc-4731-b0bd-09e865bd62ad)] interface nsIPrintSettings : nsISupports { @@ -223,7 +223,6 @@ interface nsIPrintSettings : nsISupports /* Additional XP Related */ attribute wstring paperName; /* name of paper */ - attribute short paperSizeType; /* use native data or is defined here */ attribute short paperData; /* native data value */ attribute double paperWidth; /* width of the paper in inches or mm */ attribute double paperHeight; /* height of the paper in inches or mm */ diff --git a/widget/nsIPrintSettingsWin.idl b/widget/nsIPrintSettingsWin.idl index 363db466c5..ae840b0855 100644 --- a/widget/nsIPrintSettingsWin.idl +++ b/widget/nsIPrintSettingsWin.idl @@ -13,11 +13,12 @@ * Native types */ [ptr] native nsDevMode(DEVMODEW); + native nsHdc(HDC); /** * Simplified PrintSettings for Windows interface */ -[scriptable, uuid(f13b225d-473e-4372-b11f-b6dff9fe0c5b)] +[scriptable, uuid(c63eed41-6ac5-459e-8a64-033eb9ad770a)] interface nsIPrintSettingsWin : nsISupports { @@ -39,4 +40,30 @@ interface nsIPrintSettingsWin : nsISupports [noscript] attribute nsDevMode devMode; + /** + * On Windows we use the printable width and height for the printing surface. + * We don't want to have to create native print device contexts in the content + * process, so we need to store these in the settings. + * Storing in Inches is most convenient as they are retrieved from the device + * using fields which are in pixels and pixels per inch. + * Note these are stored in portrait format to ensure that we can take account + * of our own changes to the orientation print setting. + */ + [noscript] attribute double printableWidthInInches; + [noscript] attribute double printableHeightInInches; + + /** + * Copy relevant print settings from native Windows device. + * + * @param hdc HDC to copy from + * @param devMode DEVMODE to copy from + */ + [notxpcom] void copyFromNative(in nsHdc hdc, in nsDevMode devMode); + + /** + * Copy relevant print settings to native windows structures. + * + * @param devMode DEVMODE to be populated. + */ + [notxpcom] void copyToNative(in nsDevMode devMode); }; diff --git a/widget/nsPrintOptionsImpl.cpp b/widget/nsPrintOptionsImpl.cpp index 06ae3d5ff8..2be12fa667 100644 --- a/widget/nsPrintOptionsImpl.cpp +++ b/widget/nsPrintOptionsImpl.cpp @@ -64,7 +64,6 @@ static const char kPrintFooterStrRight[] = "print_footerright"; static const char kPrintReversed[] = "print_reversed"; static const char kPrintInColor[] = "print_in_color"; static const char kPrintPaperName[] = "print_paper_name"; -static const char kPrintPaperSizeType[] = "print_paper_size_type"; static const char kPrintPaperData[] = "print_paper_data"; static const char kPrintPaperSizeUnit[] = "print_paper_size_unit"; static const char kPrintPaperWidth[] = "print_paper_width"; @@ -176,7 +175,6 @@ nsPrintOptions::SerializeToPrintData(nsIPrintSettings* aSettings, aSettings->GetPaperName(getter_Copies(paperName)); data->paperName() = paperName; - aSettings->GetPaperSizeType(&data->paperSizeType()); aSettings->GetPaperData(&data->paperData()); aSettings->GetPaperWidth(&data->paperWidth()); aSettings->GetPaperHeight(&data->paperHeight()); @@ -214,6 +212,8 @@ nsPrintOptions::SerializeToPrintData(nsIPrintSettings* aSettings, // assertions). // data->driverName() default-initializes // data->deviceName() default-initializes + data->printableWidthInInches() = 0; + data->printableHeightInInches() = 0; data->isFramesetDocument() = false; data->isFramesetFrameSelected() = false; data->isIFrameSelected() = false; @@ -291,7 +291,6 @@ nsPrintOptions::DeserializeToPrintSettings(const PrintData& data, settings->SetPaperName(data.paperName().get()); - settings->SetPaperSizeType(data.paperSizeType()); settings->SetPaperData(data.paperData()); settings->SetPaperWidth(data.paperWidth()); settings->SetPaperHeight(data.paperHeight()); @@ -521,11 +520,10 @@ nsPrintOptions::ReadPrefs(nsIPrintSettings* aPS, const nsAString& aPrinterName, // Paper size prefs are read as a group if (aFlags & nsIPrintSettings::kInitSavePaperSize) { - int32_t sizeUnit, sizeType; + int32_t sizeUnit; double width, height; bool success = GETINTPREF(kPrintPaperSizeUnit, &sizeUnit) - && GETINTPREF(kPrintPaperSizeType, &sizeType) && GETDBLPREF(kPrintPaperWidth, width) && GETDBLPREF(kPrintPaperHeight, height) && GETSTRPREF(kPrintPaperName, &str); @@ -542,8 +540,6 @@ nsPrintOptions::ReadPrefs(nsIPrintSettings* aPS, const nsAString& aPrinterName, if (success) { aPS->SetPaperSizeUnit(sizeUnit); DUMP_INT(kReadStr, kPrintPaperSizeUnit, sizeUnit); - aPS->SetPaperSizeType(sizeType); - DUMP_INT(kReadStr, kPrintPaperSizeType, sizeType); aPS->SetPaperWidth(width); DUMP_DBL(kReadStr, kPrintPaperWidth, width); aPS->SetPaperHeight(height); @@ -775,13 +771,12 @@ nsPrintOptions::WritePrefs(nsIPrintSettings *aPS, const nsAString& aPrinterName, // Paper size prefs are saved as a group if (aFlags & nsIPrintSettings::kInitSavePaperSize) { - int16_t sizeUnit, sizeType; + int16_t sizeUnit; double width, height; char16_t *name; if ( NS_SUCCEEDED(aPS->GetPaperSizeUnit(&sizeUnit)) && - NS_SUCCEEDED(aPS->GetPaperSizeType(&sizeType)) && NS_SUCCEEDED(aPS->GetPaperWidth(&width)) && NS_SUCCEEDED(aPS->GetPaperHeight(&height)) && NS_SUCCEEDED(aPS->GetPaperName(&name)) @@ -789,9 +784,6 @@ nsPrintOptions::WritePrefs(nsIPrintSettings *aPS, const nsAString& aPrinterName, DUMP_INT(kWriteStr, kPrintPaperSizeUnit, sizeUnit); Preferences::SetInt(GetPrefName(kPrintPaperSizeUnit, aPrinterName), int32_t(sizeUnit)); - DUMP_INT(kWriteStr, kPrintPaperSizeType, sizeType); - Preferences::SetInt(GetPrefName(kPrintPaperSizeType, aPrinterName), - int32_t(sizeType)); DUMP_DBL(kWriteStr, kPrintPaperWidth, width); WritePrefDouble(GetPrefName(kPrintPaperWidth, aPrinterName), width); DUMP_DBL(kWriteStr, kPrintPaperHeight, height); @@ -1411,7 +1403,6 @@ Tester::Tester() ps->SetFooterStrCenter(NS_ConvertUTF8toUTF16("Center").get()); ps->SetFooterStrRight(NS_ConvertUTF8toUTF16("Right").get()); ps->SetPaperName(NS_ConvertUTF8toUTF16("Paper Name").get()); - ps->SetPaperSizeType(10); ps->SetPaperData(1); ps->SetPaperWidth(100.0); ps->SetPaperHeight(50.0); diff --git a/widget/nsPrintSettingsImpl.cpp b/widget/nsPrintSettingsImpl.cpp index 7180525294..ae58fac610 100644 --- a/widget/nsPrintSettingsImpl.cpp +++ b/widget/nsPrintSettingsImpl.cpp @@ -35,7 +35,6 @@ nsPrintSettings::nsPrintSettings() : mShowPrintProgress(true), mPrintPageDelay(50), mPaperData(0), - mPaperSizeType(kPaperSizeDefined), mPaperWidth(8.5), mPaperHeight(11.0), mPaperSizeUnit(kPaperSizeInches), @@ -846,18 +845,6 @@ NS_IMETHODIMP nsPrintSettings::SetPaperSizeUnit(int16_t aPaperSizeUnit) return NS_OK; } -NS_IMETHODIMP nsPrintSettings::GetPaperSizeType(int16_t *aPaperSizeType) -{ - NS_ENSURE_ARG_POINTER(aPaperSizeType); - *aPaperSizeType = mPaperSizeType; - return NS_OK; -} -NS_IMETHODIMP nsPrintSettings::SetPaperSizeType(int16_t aPaperSizeType) -{ - mPaperSizeType = aPaperSizeType; - return NS_OK; -} - NS_IMETHODIMP nsPrintSettings::GetPaperData(int16_t *aPaperData) { NS_ENSURE_ARG_POINTER(aPaperData); @@ -1028,7 +1015,6 @@ nsPrintSettings& nsPrintSettings::operator=(const nsPrintSettings& rhs) mShrinkToFit = rhs.mShrinkToFit; mShowPrintProgress = rhs.mShowPrintProgress; mPaperName = rhs.mPaperName; - mPaperSizeType = rhs.mPaperSizeType; mPaperData = rhs.mPaperData; mPaperWidth = rhs.mPaperWidth; mPaperHeight = rhs.mPaperHeight; diff --git a/widget/nsPrintSettingsImpl.h b/widget/nsPrintSettingsImpl.h index d1e46904f7..25cd2a181e 100644 --- a/widget/nsPrintSettingsImpl.h +++ b/widget/nsPrintSettingsImpl.h @@ -81,7 +81,6 @@ protected: nsString mPaperName; int16_t mPaperData; - int16_t mPaperSizeType; double mPaperWidth; double mPaperHeight; int16_t mPaperSizeUnit; diff --git a/widget/windows/nsDeviceContextSpecWin.cpp b/widget/windows/nsDeviceContextSpecWin.cpp index d3b48f9854..5eb15fe5a3 100644 --- a/widget/windows/nsDeviceContextSpecWin.cpp +++ b/widget/windows/nsDeviceContextSpecWin.cpp @@ -49,6 +49,8 @@ PRLogModuleInfo * kWidgetPrintingLogMod = PR_NewLogModule("printing-widget"); using namespace mozilla; +static const wchar_t kDriverName[] = L"WINSPOOL"; + //---------------------------------------------------------------------------------- // The printer data is shared between the PrinterEnumerator and the nsDeviceContextSpecWin // The PrinterEnumerator creates the printer info @@ -81,62 +83,12 @@ protected: GlobalPrinters GlobalPrinters::mGlobalPrinters; nsTArray* GlobalPrinters::mPrinters = nullptr; - -//****************************************************** -// Define native paper sizes -//****************************************************** -typedef struct { - short mPaperSize; // native enum - double mWidth; - double mHeight; - bool mIsInches; -} NativePaperSizes; - -// There are around 40 default print sizes defined by Windows -const NativePaperSizes kPaperSizes[] = { - {DMPAPER_LETTER, 8.5, 11.0, true}, - {DMPAPER_LEGAL, 8.5, 14.0, true}, - {DMPAPER_A4, 210.0, 297.0, false}, - {DMPAPER_B4, 250.0, 354.0, false}, - {DMPAPER_B5, 182.0, 257.0, false}, - {DMPAPER_TABLOID, 11.0, 17.0, true}, - {DMPAPER_LEDGER, 17.0, 11.0, true}, - {DMPAPER_STATEMENT, 5.5, 8.5, true}, - {DMPAPER_EXECUTIVE, 7.25, 10.5, true}, - {DMPAPER_A3, 297.0, 420.0, false}, - {DMPAPER_A5, 148.0, 210.0, false}, - {DMPAPER_CSHEET, 17.0, 22.0, true}, - {DMPAPER_DSHEET, 22.0, 34.0, true}, - {DMPAPER_ESHEET, 34.0, 44.0, true}, - {DMPAPER_LETTERSMALL, 8.5, 11.0, true}, - {DMPAPER_A4SMALL, 210.0, 297.0, false}, - {DMPAPER_FOLIO, 8.5, 13.0, true}, - {DMPAPER_QUARTO, 215.0, 275.0, false}, - {DMPAPER_10X14, 10.0, 14.0, true}, - {DMPAPER_11X17, 11.0, 17.0, true}, - {DMPAPER_NOTE, 8.5, 11.0, true}, - {DMPAPER_ENV_9, 3.875, 8.875, true}, - {DMPAPER_ENV_10, 40.125, 9.5, true}, - {DMPAPER_ENV_11, 4.5, 10.375, true}, - {DMPAPER_ENV_12, 4.75, 11.0, true}, - {DMPAPER_ENV_14, 5.0, 11.5, true}, - {DMPAPER_ENV_DL, 110.0, 220.0, false}, - {DMPAPER_ENV_C5, 162.0, 229.0, false}, - {DMPAPER_ENV_C3, 324.0, 458.0, false}, - {DMPAPER_ENV_C4, 229.0, 324.0, false}, - {DMPAPER_ENV_C6, 114.0, 162.0, false}, - {DMPAPER_ENV_C65, 114.0, 229.0, false}, - {DMPAPER_ENV_B4, 250.0, 353.0, false}, - {DMPAPER_ENV_B5, 176.0, 250.0, false}, - {DMPAPER_ENV_B6, 176.0, 125.0, false}, - {DMPAPER_ENV_ITALY, 110.0, 230.0, false}, - {DMPAPER_ENV_MONARCH, 3.875, 7.5, true}, - {DMPAPER_ENV_PERSONAL, 3.625, 6.5, true}, - {DMPAPER_FANFOLD_US, 14.875, 11.0, true}, - {DMPAPER_FANFOLD_STD_GERMAN, 8.5, 12.0, true}, - {DMPAPER_FANFOLD_LGL_GERMAN, 8.5, 13.0, true}, +struct AutoFreeGlobalPrinters +{ + ~AutoFreeGlobalPrinters() { + GlobalPrinters::GetInstance()->FreeGlobalPrinters(); + } }; -const int32_t kNumPaperSizes = 41; //---------------------------------------------------------------------------------- nsDeviceContextSpecWin::nsDeviceContextSpecWin() @@ -321,7 +273,6 @@ NS_IMETHODIMP nsDeviceContextSpecWin::GetSurfaceForPrinter(gfxASurface **surface } } - mPrintingSurface = newSurface; newSurface.forget(surface); return NS_OK; } @@ -329,11 +280,11 @@ NS_IMETHODIMP nsDeviceContextSpecWin::GetSurfaceForPrinter(gfxASurface **surface float nsDeviceContextSpecWin::GetPrintingScale() { - MOZ_ASSERT(mPrintingSurface); + MOZ_ASSERT(mPrintSettings); - HDC dc = reinterpret_cast(mPrintingSurface.get())->GetDC(); - int32_t OSVal = GetDeviceCaps(dc, LOGPIXELSY); - return float(OSVal) / GetDPI(); + int32_t resolution; + mPrintSettings->GetResolution(&resolution); + return float(resolution) / GetDPI(); } //---------------------------------------------------------------------------------- @@ -365,96 +316,6 @@ nsDeviceContextSpecWin::GetDevMode(LPDEVMODEW &aDevMode) aDevMode = mDevMode; } -//---------------------------------------------------------------------------------- -// Map an incoming size to a Windows Native enum in the DevMode -static void -MapPaperSizeToNativeEnum(LPDEVMODEW aDevMode, - int16_t aType, - double aW, - double aH) -{ - -#ifdef DEBUG_rods - BOOL doingOrientation = aDevMode->dmFields & DM_ORIENTATION; - BOOL doingPaperSize = aDevMode->dmFields & DM_PAPERSIZE; - BOOL doingPaperLength = aDevMode->dmFields & DM_PAPERLENGTH; - BOOL doingPaperWidth = aDevMode->dmFields & DM_PAPERWIDTH; -#endif - - for (int32_t i=0;idmPaperSize = kPaperSizes[i].mPaperSize; - aDevMode->dmFields &= ~DM_PAPERLENGTH; - aDevMode->dmFields &= ~DM_PAPERWIDTH; - aDevMode->dmFields |= DM_PAPERSIZE; - return; - } - } - - short width = 0; - short height = 0; - if (aType == nsIPrintSettings::kPaperSizeInches) { - width = short(NS_TWIPS_TO_MILLIMETERS(NS_INCHES_TO_TWIPS(float(aW))) / 10); - height = short(NS_TWIPS_TO_MILLIMETERS(NS_INCHES_TO_TWIPS(float(aH))) / 10); - - } else if (aType == nsIPrintSettings::kPaperSizeMillimeters) { - width = short(aW / 10.0); - height = short(aH / 10.0); - } else { - return; // don't set anything - } - - // width and height is in - aDevMode->dmPaperSize = 0; - aDevMode->dmPaperWidth = width; - aDevMode->dmPaperLength = height; - - aDevMode->dmFields |= DM_PAPERSIZE; - aDevMode->dmFields |= DM_PAPERLENGTH; - aDevMode->dmFields |= DM_PAPERWIDTH; -} - -//---------------------------------------------------------------------------------- -// Setup Paper Size & Orientation options into the DevMode -// -static void -SetupDevModeFromSettings(LPDEVMODEW aDevMode, nsIPrintSettings* aPrintSettings) -{ - // Setup paper size - if (aPrintSettings) { - int16_t type; - aPrintSettings->GetPaperSizeType(&type); - if (type == nsIPrintSettings::kPaperSizeNativeData) { - int16_t paperEnum; - aPrintSettings->GetPaperData(&paperEnum); - aDevMode->dmPaperSize = paperEnum; - aDevMode->dmFields &= ~DM_PAPERLENGTH; - aDevMode->dmFields &= ~DM_PAPERWIDTH; - aDevMode->dmFields |= DM_PAPERSIZE; - } else { - int16_t unit; - double width, height; - aPrintSettings->GetPaperSizeUnit(&unit); - aPrintSettings->GetPaperWidth(&width); - aPrintSettings->GetPaperHeight(&height); - MapPaperSizeToNativeEnum(aDevMode, unit, width, height); - } - - // Setup Orientation - int32_t orientation; - aPrintSettings->GetOrientation(&orientation); - aDevMode->dmOrientation = orientation == nsIPrintSettings::kPortraitOrientation?DMORIENT_PORTRAIT:DMORIENT_LANDSCAPE; - aDevMode->dmFields |= DM_ORIENTATION; - - // Setup Number of Copies - int32_t copies; - aPrintSettings->GetNumCopies(&copies); - aDevMode->dmCopies = copies; - aDevMode->dmFields |= DM_COPIES; - } - -} - #define DISPLAY_LAST_ERROR //---------------------------------------------------------------------------------- @@ -493,7 +354,9 @@ nsDeviceContextSpecWin::GetDataFromPrinter(char16ptr_t aName, nsIPrintSettings* pDevMode, nullptr, DM_OUT_BUFFER); if (dwRet == IDOK && aPS) { - SetupDevModeFromSettings(pDevMode, aPS); + nsCOMPtr psWin = do_QueryInterface(aPS); + MOZ_ASSERT(psWin); + psWin->CopyToNative(pDevMode); // Sets back the changes we made to the DevMode into the Printer Driver dwRet = ::DocumentPropertiesW(nullptr, hPrinter, name, pDevMode, pDevMode, @@ -512,7 +375,7 @@ nsDeviceContextSpecWin::GetDataFromPrinter(char16ptr_t aName, nsIPrintSettings* SetDeviceName(aName); - SetDriverName(L"WINSPOOL"); + SetDriverName(kDriverName); ::ClosePrinter(hPrinter); rv = NS_OK; @@ -524,95 +387,6 @@ nsDeviceContextSpecWin::GetDataFromPrinter(char16ptr_t aName, nsIPrintSettings* return rv; } -//---------------------------------------------------------------------------------- -// Setup Paper Size options into the DevMode -// -// When using a data member it may be a HGLOCAL or LPDEVMODE -// if it is a HGLOBAL then we need to "lock" it to get the LPDEVMODE -// and unlock it when we are done. -void -nsDeviceContextSpecWin::SetupPaperInfoFromSettings() -{ - LPDEVMODEW devMode; - - GetDevMode(devMode); - NS_ASSERTION(devMode, "DevMode can't be NULL here"); - if (devMode) { - SetupDevModeFromSettings(devMode, mPrintSettings); - } -} - -//---------------------------------------------------------------------------------- -// Helper Function - Free and reallocate the string -nsresult -nsDeviceContextSpecWin::SetPrintSettingsFromDevMode(nsIPrintSettings* aPrintSettings, - LPDEVMODEW aDevMode) -{ - if (aPrintSettings == nullptr) { - return NS_ERROR_FAILURE; - } - aPrintSettings->SetIsInitializedFromPrinter(true); - - BOOL doingNumCopies = aDevMode->dmFields & DM_COPIES; - BOOL doingOrientation = aDevMode->dmFields & DM_ORIENTATION; - BOOL doingPaperSize = aDevMode->dmFields & DM_PAPERSIZE; - BOOL doingPaperLength = aDevMode->dmFields & DM_PAPERLENGTH; - BOOL doingPaperWidth = aDevMode->dmFields & DM_PAPERWIDTH; - - if (doingOrientation) { - int32_t orientation = aDevMode->dmOrientation == DMORIENT_PORTRAIT? - int32_t(nsIPrintSettings::kPortraitOrientation):nsIPrintSettings::kLandscapeOrientation; - aPrintSettings->SetOrientation(orientation); - } - - // Setup Number of Copies - if (doingNumCopies) { - aPrintSettings->SetNumCopies(int32_t(aDevMode->dmCopies)); - } - - if (aDevMode->dmFields & DM_SCALE) { - double scale = double(aDevMode->dmScale) / 100.0f; - if (scale != 1.0) { - aPrintSettings->SetScaling(scale); - aDevMode->dmScale = 100; - // To turn this on you must change where the mPrt->mShrinkToFit is being set in the DocumentViewer - //aPrintSettings->SetShrinkToFit(false); - } - } - - if (doingPaperSize) { - aPrintSettings->SetPaperSizeType(nsIPrintSettings::kPaperSizeNativeData); - aPrintSettings->SetPaperData(aDevMode->dmPaperSize); - for (int32_t i=0;idmPaperSize) { - aPrintSettings->SetPaperSizeUnit(kPaperSizes[i].mIsInches - ?int16_t(nsIPrintSettings::kPaperSizeInches):nsIPrintSettings::kPaperSizeMillimeters); - break; - } - } - - } else if (doingPaperLength && doingPaperWidth) { - bool found = false; - for (int32_t i=0;idmPaperSize) { - aPrintSettings->SetPaperSizeType(nsIPrintSettings::kPaperSizeDefined); - aPrintSettings->SetPaperWidth(kPaperSizes[i].mWidth); - aPrintSettings->SetPaperHeight(kPaperSizes[i].mHeight); - aPrintSettings->SetPaperSizeUnit(kPaperSizes[i].mIsInches - ?int16_t(nsIPrintSettings::kPaperSizeInches):nsIPrintSettings::kPaperSizeMillimeters); - found = true; - break; - } - } - if (!found) { - return NS_ERROR_FAILURE; - } - } else { - return NS_ERROR_FAILURE; - } - return NS_OK; -} - //*********************************************************** // Printer Enumerator //*********************************************************** @@ -657,18 +431,29 @@ nsPrinterEnumeratorWin::InitPrintSettingsFromPrinter(const char16_t *aPrinterNam return NS_ERROR_FAILURE; } + AutoFreeGlobalPrinters autoFreeGlobalPrinters; + devSpecWin->GetDataFromPrinter(aPrinterName); LPDEVMODEW devmode; devSpecWin->GetDevMode(devmode); - NS_ASSERTION(devmode, "DevMode can't be NULL here"); - if (devmode) { - aPrintSettings->SetPrinterName(aPrinterName); - nsDeviceContextSpecWin::SetPrintSettingsFromDevMode(aPrintSettings, devmode); + if (NS_WARN_IF(!devmode)) { + return NS_ERROR_FAILURE; } - // Free them, we won't need them for a while - GlobalPrinters::GetInstance()->FreeGlobalPrinters(); + aPrintSettings->SetPrinterName(aPrinterName); + + // We need to get information from the device as well. + HDC dc = ::CreateICW(kDriverName, aPrinterName, nullptr, devmode); + if (NS_WARN_IF(!dc)) { + return NS_ERROR_FAILURE; + } + + nsCOMPtr psWin = do_QueryInterface(aPrintSettings); + MOZ_ASSERT(psWin); + psWin->CopyFromNative(dc, devmode); + ::DeleteDC(dc); + return NS_OK; } diff --git a/widget/windows/nsDeviceContextSpecWin.h b/widget/windows/nsDeviceContextSpecWin.h index c0fc8197df..43cf08c87c 100644 --- a/widget/windows/nsDeviceContextSpecWin.h +++ b/widget/windows/nsDeviceContextSpecWin.h @@ -51,17 +51,12 @@ public: // helper functions nsresult GetDataFromPrinter(char16ptr_t aName, nsIPrintSettings* aPS = nullptr); - static nsresult SetPrintSettingsFromDevMode(nsIPrintSettings* aPrintSettings, - LPDEVMODEW aDevMode); - protected: void SetDeviceName(char16ptr_t aDeviceName); void SetDriverName(char16ptr_t aDriverName); void SetDevMode(LPDEVMODEW aDevMode); - void SetupPaperInfoFromSettings(); - virtual ~nsDeviceContextSpecWin(); wchar_t* mDriverName; @@ -69,7 +64,6 @@ protected: LPDEVMODEW mDevMode; nsCOMPtr mPrintSettings; - RefPtr mPrintingSurface; }; diff --git a/widget/windows/nsPrintOptionsWin.cpp b/widget/windows/nsPrintOptionsWin.cpp index 91f9977cb2..cce03d9c0b 100644 --- a/widget/windows/nsPrintOptionsWin.cpp +++ b/widget/windows/nsPrintOptionsWin.cpp @@ -67,10 +67,12 @@ nsPrintOptionsWin::SerializeToPrintData(nsIPrintSettings* aSettings, free(deviceName); free(driverName); - // When creating the print dialog on Windows, the parent creates a DEVMODE - // which is used to convey print settings to the Windows printing backend. - // We don't, therefore, want or care about DEVMODEs sent up from the child. + // When creating the print dialog on Windows, we only need to send certain + // print settings information from the parent to the child not vice versa. if (XRE_IsParentProcess()) { + psWin->GetPrintableWidthInInches(&data->printableWidthInInches()); + psWin->GetPrintableHeightInInches(&data->printableHeightInInches()); + // A DEVMODE can actually be of arbitrary size. If it turns out that it'll // make our IPC message larger than the limit, then we'll error out. LPDEVMODEW devModeRaw; @@ -118,6 +120,9 @@ nsPrintOptionsWin::DeserializeToPrintSettings(const PrintData& data, psWin->SetDeviceName(data.deviceName().get()); psWin->SetDriverName(data.driverName().get()); + psWin->SetPrintableWidthInInches(data.printableWidthInInches()); + psWin->SetPrintableHeightInInches(data.printableHeightInInches()); + nsXPIDLString printerName; settings->GetPrinterName(getter_Copies(printerName)); diff --git a/widget/windows/nsPrintSettingsWin.cpp b/widget/windows/nsPrintSettingsWin.cpp index 5766c0612e..8641ca3606 100644 --- a/widget/windows/nsPrintSettingsWin.cpp +++ b/widget/windows/nsPrintSettingsWin.cpp @@ -111,6 +111,174 @@ NS_IMETHODIMP nsPrintSettingsWin::SetDevMode(DEVMODEW * aDevMode) return NS_OK; } +NS_IMETHODIMP +nsPrintSettingsWin::GetPrintableWidthInInches(double* aPrintableWidthInInches) +{ + MOZ_ASSERT(aPrintableWidthInInches); + *aPrintableWidthInInches = mPrintableWidthInInches; + return NS_OK; +} + +NS_IMETHODIMP +nsPrintSettingsWin::SetPrintableWidthInInches(double aPrintableWidthInInches) +{ + mPrintableWidthInInches = aPrintableWidthInInches; + return NS_OK; +} + +NS_IMETHODIMP +nsPrintSettingsWin::GetPrintableHeightInInches(double* aPrintableHeightInInches) +{ + MOZ_ASSERT(aPrintableHeightInInches); + *aPrintableHeightInInches = mPrintableHeightInInches; + return NS_OK; +} + +NS_IMETHODIMP +nsPrintSettingsWin::SetPrintableHeightInInches(double aPrintableHeightInInches) +{ + mPrintableHeightInInches = aPrintableHeightInInches; + return NS_OK; +} + +NS_IMETHODIMP +nsPrintSettingsWin::GetEffectivePageSize(double *aWidth, double *aHeight) +{ + // If printable page size not set, fall back to nsPrintSettings. + if (mPrintableWidthInInches == 0l || mPrintableHeightInInches == 0l) { + return nsPrintSettings::GetEffectivePageSize(aWidth, aHeight); + } + + if (mOrientation == kPortraitOrientation) { + *aWidth = NS_INCHES_TO_TWIPS(mPrintableWidthInInches); + *aHeight = NS_INCHES_TO_TWIPS(mPrintableHeightInInches); + } else { + *aHeight = NS_INCHES_TO_TWIPS(mPrintableWidthInInches); + *aWidth = NS_INCHES_TO_TWIPS(mPrintableHeightInInches); + } + return NS_OK; +} + +void +nsPrintSettingsWin::CopyFromNative(HDC aHdc, DEVMODEW* aDevMode) +{ + MOZ_ASSERT(aHdc); + MOZ_ASSERT(aDevMode); + + mIsInitedFromPrinter = true; + if (aDevMode->dmFields & DM_ORIENTATION) { + mOrientation = int32_t(aDevMode->dmOrientation == DMORIENT_PORTRAIT + ? kPortraitOrientation : kLandscapeOrientation); + } + + if (aDevMode->dmFields & DM_COPIES) { + mNumCopies = aDevMode->dmCopies; + } + + // Since we do the scaling, grab their value and reset back to 100. + if (aDevMode->dmFields & DM_SCALE) { + double scale = double(aDevMode->dmScale) / 100.0f; + if (mScaling == 1.0 || scale != 1.0) { + mScaling = scale; + } + aDevMode->dmScale = 100; + } + + if (aDevMode->dmFields & DM_PAPERSIZE) { + mPaperData = aDevMode->dmPaperSize; + } else { + mPaperData = -1; + } + + // The length and width in DEVMODE are always in tenths of a millimeter, so + // storing them in millimeters is easiest. + mPaperSizeUnit = kPaperSizeMillimeters; + if (aDevMode->dmFields & DM_PAPERLENGTH) { + mPaperHeight = aDevMode->dmPaperLength / 10l; + } else { + mPaperHeight = -1l; + } + + if (aDevMode->dmFields & DM_PAPERWIDTH) { + mPaperWidth = aDevMode->dmPaperWidth / 10l; + } else { + mPaperWidth = -1l; + } + + // On Windows we currently create a surface using the printable area of the + // page and don't set the unwriteable [sic] margins. Using the unwriteable + // margins doesn't appear to work on Windows, but I am not sure if this is a + // bug elsewhere in our code or a Windows quirk. + int32_t printableWidthInDots = GetDeviceCaps(aHdc, HORZRES); + int32_t printableHeightInDots = GetDeviceCaps(aHdc, VERTRES); + int32_t widthDPI = GetDeviceCaps(aHdc, LOGPIXELSX); + int32_t heightDPI = GetDeviceCaps(aHdc, LOGPIXELSY); + + // Keep these values in portrait format, so we can reflect our own changes + // to mOrientation. + if (mOrientation == kPortraitOrientation) { + mPrintableWidthInInches = double(printableWidthInDots) / widthDPI; + mPrintableHeightInInches = double(printableHeightInDots) / heightDPI; + } else { + mPrintableHeightInInches = double(printableWidthInDots) / widthDPI; + mPrintableWidthInInches = double(printableHeightInDots) / heightDPI; + } + + // Using Y to match existing code, X DPI should be the same for printing. + mResolution = heightDPI; +} + +void +nsPrintSettingsWin::CopyToNative(DEVMODEW* aDevMode) +{ + MOZ_ASSERT(aDevMode); + + if (mPaperData >= 0) { + aDevMode->dmPaperSize = mPaperData; + aDevMode->dmFields |= DM_PAPERSIZE; + } else { + aDevMode->dmPaperSize = 0; + aDevMode->dmFields &= ~DM_PAPERSIZE; + } + + double paperHeight; + double paperWidth; + if (mPaperSizeUnit == kPaperSizeMillimeters) { + paperHeight = mPaperHeight; + paperWidth = mPaperWidth; + } else { + paperHeight = mPaperHeight * MM_PER_INCH_FLOAT; + paperWidth = mPaperWidth * MM_PER_INCH_FLOAT; + } + + // Set a sensible limit on the minimum height and width. + if (paperHeight >= MM_PER_INCH_FLOAT) { + // In DEVMODEs, physical lengths are stored in tenths of millimeters. + aDevMode->dmPaperLength = paperHeight * 10l; + aDevMode->dmFields |= DM_PAPERLENGTH; + } else { + aDevMode->dmPaperLength = 0; + aDevMode->dmFields &= ~DM_PAPERLENGTH; + } + + if (paperWidth >= MM_PER_INCH_FLOAT) { + aDevMode->dmPaperWidth = paperWidth * 10l; + aDevMode->dmFields |= DM_PAPERWIDTH; + } else { + aDevMode->dmPaperWidth = 0; + aDevMode->dmFields &= ~DM_PAPERWIDTH; + } + + // Setup Orientation + aDevMode->dmOrientation = mOrientation == kPortraitOrientation + ? DMORIENT_PORTRAIT : DMORIENT_LANDSCAPE; + aDevMode->dmFields |= DM_ORIENTATION; + + // Setup Number of Copies + aDevMode->dmCopies = mNumCopies; + aDevMode->dmFields |= DM_COPIES; +} + //------------------------------------------- nsresult nsPrintSettingsWin::_Clone(nsIPrintSettings **_retval) @@ -201,7 +369,6 @@ Tester::Tester() ps->SetFooterStrCenter(NS_ConvertUTF8toUTF16("Center").get()); ps->SetFooterStrRight(NS_ConvertUTF8toUTF16("Right").get()); ps->SetPaperName(NS_ConvertUTF8toUTF16("Paper Name").get()); - ps->SetPaperSizeType(10); ps->SetPaperData(1); ps->SetPaperWidth(100.0); ps->SetPaperHeight(50.0); @@ -230,5 +397,4 @@ Tester::Tester() } Tester gTester; -#endif - +#endif \ No newline at end of file diff --git a/widget/windows/nsPrintSettingsWin.h b/widget/windows/nsPrintSettingsWin.h index 7f5d561b18..d4a31b41e2 100644 --- a/widget/windows/nsPrintSettingsWin.h +++ b/widget/windows/nsPrintSettingsWin.h @@ -42,12 +42,16 @@ public: */ nsPrintSettingsWin& operator=(const nsPrintSettingsWin& rhs); + NS_IMETHOD GetEffectivePageSize(double *aWidth, double *aHeight) override; + protected: void CopyDevMode(DEVMODEW* aInDevMode, DEVMODEW *& aOutDevMode); wchar_t* mDeviceName; wchar_t* mDriverName; LPDEVMODEW mDevMode; + double mPrintableWidthInInches = 0l; + double mPrintableHeightInInches = 0l; }; diff --git a/xpcom/threads/BackgroundHangMonitor.cpp b/xpcom/threads/BackgroundHangMonitor.cpp index 7e288a5092..0ec773940c 100644 --- a/xpcom/threads/BackgroundHangMonitor.cpp +++ b/xpcom/threads/BackgroundHangMonitor.cpp @@ -435,7 +435,9 @@ BackgroundHangThread::ReportHang(PRIntervalTime aHangTime) } // Add new histogram newHistogram.Add(aHangTime, Move(mAnnotations)); - mStats.mHangs.append(Move(newHistogram)); + if (!mStats.mHangs.append(Move(newHistogram))) { + MOZ_CRASH(); + } return mStats.mHangs.back(); }