template <class T> void ExtractCommonNonCDMACellInfoItems(nsCOMPtr<T>& cell, nsDataHashtable<nsCStringHashKey, int32_t>& info) { int32_t mcc, mnc, cid, sig; cell->GetMcc(&mcc); cell->GetMnc(&mnc); cell->GetCid(&cid); cell->GetSignalStrength(&sig); info.Put(TEXT_MCC, mcc); info.Put(TEXT_MNC, mnc); info.Put(TEXT_CID, cid); info.Put(TEXT_STRENGTH_ASU, sig); }
nsresult NameSpaceManagerImpl::AddNameSpace(const nsAString& aURI, const PRInt32 aNameSpaceID) { if (aNameSpaceID < 0) { // We've wrapped... Can't do anything else here; just bail. return NS_ERROR_OUT_OF_MEMORY; } NS_ASSERTION(aNameSpaceID - 1 == (PRInt32) mURIArray.Length(), "BAD! AddNameSpace not called in right order!"); nsString* uri = new nsString(aURI); if (!uri || !mURIArray.AppendElement(uri)) { delete uri; return NS_ERROR_OUT_OF_MEMORY; } if (!mURIToIDTable.Put(uri, aNameSpaceID)) { mURIArray.RemoveElementAt(aNameSpaceID - 1); return NS_ERROR_OUT_OF_MEMORY; } return NS_OK; }
nsresult NameSpaceManagerImpl::Init() { nsresult rv = mURIToIDTable.Init(32); NS_ENSURE_SUCCESS(rv, rv); #define REGISTER_NAMESPACE(uri, id) \ rv = AddNameSpace(NS_LITERAL_STRING(uri), id); \ NS_ENSURE_SUCCESS(rv, rv) // Need to be ordered according to ID. REGISTER_NAMESPACE(kXMLNSNameSpaceURI, kNameSpaceID_XMLNS); REGISTER_NAMESPACE(kXMLNameSpaceURI, kNameSpaceID_XML); REGISTER_NAMESPACE(kXHTMLNameSpaceURI, kNameSpaceID_XHTML); REGISTER_NAMESPACE(kXLinkNameSpaceURI, kNameSpaceID_XLink); REGISTER_NAMESPACE(kXSLTNameSpaceURI, kNameSpaceID_XSLT); REGISTER_NAMESPACE(kXBLNameSpaceURI, kNameSpaceID_XBL); REGISTER_NAMESPACE(kMathMLNameSpaceURI, kNameSpaceID_MathML); REGISTER_NAMESPACE(kRDFNameSpaceURI, kNameSpaceID_RDF); REGISTER_NAMESPACE(kXULNameSpaceURI, kNameSpaceID_XUL); REGISTER_NAMESPACE(kSVGNameSpaceURI, kNameSpaceID_SVG); REGISTER_NAMESPACE(kXMLEventsNameSpaceURI, kNameSpaceID_XMLEvents); #undef REGISTER_NAMESPACE return NS_OK; }
PRInt32 NameSpaceManagerImpl::GetNameSpaceID(const nsAString& aURI) { if (aURI.IsEmpty()) { return kNameSpaceID_None; // xmlns="", see bug 75700 for details } PRInt32 nameSpaceID; if (mURIToIDTable.Get(&aURI, &nameSpaceID)) { NS_POSTCONDITION(nameSpaceID >= 0, "Bogus namespace ID"); return nameSpaceID; } return kNameSpaceID_Unknown; }
void BluetoothServiceBluedroid::SspRequestNotification( const nsAString& aRemoteBdAddr, const nsAString& aBdName, uint32_t aCod, BluetoothSspVariant aPairingVariant, uint32_t aPasskey) { MOZ_ASSERT(NS_IsMainThread()); InfallibleTArray<BluetoothNamedValue> propertiesArray; nsAutoString passkey; nsAutoString pairingType; /** * Assign pairing request type and passkey based on the pairing variant. * * passkey value based on pairing request type: * 1) aPasskey: PAIRING_REQ_TYPE_CONFIRMATION and * PAIRING_REQ_TYPE_DISPLAYPASSKEY * 2) empty string: PAIRING_REQ_TYPE_CONSENT */ switch (aPairingVariant) { case SSP_VARIANT_PASSKEY_CONFIRMATION: pairingType.AssignLiteral(PAIRING_REQ_TYPE_CONFIRMATION); passkey.AppendInt(aPasskey); break; case SSP_VARIANT_PASSKEY_NOTIFICATION: pairingType.AssignLiteral(PAIRING_REQ_TYPE_DISPLAYPASSKEY); passkey.AppendInt(aPasskey); break; case SSP_VARIANT_CONSENT: pairingType.AssignLiteral(PAIRING_REQ_TYPE_CONSENT); break; default: BT_WARNING("Unhandled SSP Bonding Variant: %d", aPairingVariant); return; } BT_APPEND_NAMED_VALUE(propertiesArray, "address", nsString(aRemoteBdAddr)); BT_APPEND_NAMED_VALUE(propertiesArray, "name", nsString(aBdName)); BT_APPEND_NAMED_VALUE(propertiesArray, "passkey", passkey); BT_APPEND_NAMED_VALUE(propertiesArray, "type", pairingType); sPairingNameTable.Put(nsString(aRemoteBdAddr), nsString(aBdName)); DistributeSignal(NS_LITERAL_STRING("PairingRequest"), NS_LITERAL_STRING(KEY_PAIRING_LISTENER), BluetoothValue(propertiesArray)); }
void BluetoothServiceBluedroid::PinRequestNotification(const nsAString& aRemoteBdAddr, const nsAString& aBdName, uint32_t aCod) { MOZ_ASSERT(NS_IsMainThread()); InfallibleTArray<BluetoothNamedValue> propertiesArray; BT_APPEND_NAMED_VALUE(propertiesArray, "address", nsString(aRemoteBdAddr)); BT_APPEND_NAMED_VALUE(propertiesArray, "name", nsString(aBdName)); BT_APPEND_NAMED_VALUE(propertiesArray, "passkey", EmptyString()); BT_APPEND_NAMED_VALUE(propertiesArray, "type", NS_LITERAL_STRING(PAIRING_REQ_TYPE_ENTERPINCODE)); sPairingNameTable.Put(nsString(aRemoteBdAddr), nsString(aBdName)); DistributeSignal(NS_LITERAL_STRING("PairingRequest"), NS_LITERAL_STRING(KEY_PAIRING_LISTENER), BluetoothValue(propertiesArray)); }
static nsresult GetSourceIndices(nsSVGFE* aFilterElement, int32_t aCurrentIndex, const nsDataHashtable<nsStringHashKey, int32_t>& aImageTable, nsTArray<int32_t>& aSourceIndices) { nsAutoTArray<nsSVGStringInfo,2> sources; aFilterElement->GetSourceImageNames(sources); for (uint32_t j = 0; j < sources.Length(); j++) { nsAutoString str; sources[j].mString->GetAnimValue(str, sources[j].mElement); int32_t sourceIndex = 0; if (str.EqualsLiteral("SourceGraphic")) { sourceIndex = FilterPrimitiveDescription::kPrimitiveIndexSourceGraphic; } else if (str.EqualsLiteral("SourceAlpha")) { sourceIndex = FilterPrimitiveDescription::kPrimitiveIndexSourceAlpha; } else if (str.EqualsLiteral("FillPaint")) { sourceIndex = FilterPrimitiveDescription::kPrimitiveIndexFillPaint; } else if (str.EqualsLiteral("StrokePaint")) { sourceIndex = FilterPrimitiveDescription::kPrimitiveIndexStrokePaint; } else if (str.EqualsLiteral("BackgroundImage") || str.EqualsLiteral("BackgroundAlpha")) { return NS_ERROR_NOT_IMPLEMENTED; } else if (str.EqualsLiteral("")) { sourceIndex = aCurrentIndex == 0 ? FilterPrimitiveDescription::kPrimitiveIndexSourceGraphic : aCurrentIndex - 1; } else { bool inputExists = aImageTable.Get(str, &sourceIndex); if (!inputExists) return NS_ERROR_FAILURE; } MOZ_ASSERT(sourceIndex < aCurrentIndex); aSourceIndices.AppendElement(sourceIndex); } return NS_OK; }
nsresult nsSVGFilterInstance::GetSourceIndices(nsSVGFE* aPrimitiveElement, nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs, const nsDataHashtable<nsStringHashKey, int32_t>& aImageTable, nsTArray<int32_t>& aSourceIndices) { AutoTArray<nsSVGStringInfo,2> sources; aPrimitiveElement->GetSourceImageNames(sources); for (uint32_t j = 0; j < sources.Length(); j++) { nsAutoString str; sources[j].mString->GetAnimValue(str, sources[j].mElement); int32_t sourceIndex = 0; if (str.EqualsLiteral("SourceGraphic")) { sourceIndex = mSourceGraphicIndex; } else if (str.EqualsLiteral("SourceAlpha")) { sourceIndex = GetOrCreateSourceAlphaIndex(aPrimitiveDescrs); } else if (str.EqualsLiteral("FillPaint")) { sourceIndex = FilterPrimitiveDescription::kPrimitiveIndexFillPaint; } else if (str.EqualsLiteral("StrokePaint")) { sourceIndex = FilterPrimitiveDescription::kPrimitiveIndexStrokePaint; } else if (str.EqualsLiteral("BackgroundImage") || str.EqualsLiteral("BackgroundAlpha")) { return NS_ERROR_NOT_IMPLEMENTED; } else if (str.EqualsLiteral("")) { sourceIndex = GetLastResultIndex(aPrimitiveDescrs); } else { bool inputExists = aImageTable.Get(str, &sourceIndex); if (!inputExists) return NS_ERROR_FAILURE; } aSourceIndices.AppendElement(sourceIndex); } return NS_OK; }
nsresult NameSpaceManagerImpl::RegisterNameSpace(const nsAString& aURI, PRInt32& aNameSpaceID) { if (aURI.IsEmpty()) { aNameSpaceID = kNameSpaceID_None; // xmlns="", see bug 75700 for details return NS_OK; } nsresult rv = NS_OK; if (!mURIToIDTable.Get(&aURI, &aNameSpaceID)) { aNameSpaceID = mURIArray.Length() + 1; // id is index + 1 rv = AddNameSpace(aURI, aNameSpaceID); if (NS_FAILED(rv)) { aNameSpaceID = kNameSpaceID_Unknown; } } NS_POSTCONDITION(aNameSpaceID >= -1, "Bogus namespace ID"); return rv; }
nsresult NameSpaceManagerImpl::AddNameSpace(const nsAString& aURI, const PRInt32 aNameSpaceID) { if (aNameSpaceID < 0) { // We've wrapped... Can't do anything else here; just bail. return NS_ERROR_OUT_OF_MEMORY; } NS_ASSERTION(aNameSpaceID - 1 == mURIArray.Count(), "BAD! AddNameSpace not called in right order!"); if (!mURIArray.AppendString(aURI)) { return NS_ERROR_OUT_OF_MEMORY; } const nsString* uri = mURIArray.StringAt(aNameSpaceID - 1); if (!mURIToIDTable.Put(uri, aNameSpaceID)) { mURIArray.RemoveStringAt(aNameSpaceID - 1); return NS_ERROR_OUT_OF_MEMORY; } return NS_OK; }
void BluetoothServiceBluedroid::BondStateChangedNotification( BluetoothStatus aStatus, const nsAString& aRemoteBdAddr, BluetoothBondState aState) { MOZ_ASSERT(NS_IsMainThread()); if (aState == BOND_STATE_BONDING) { // No need to handle bonding state return; } BT_LOGR("Bond state: %d status: %d", aState, aStatus); bool bonded = (aState == BOND_STATE_BONDED); if (aStatus != STATUS_SUCCESS) { if (!bonded) { // Active/passive pair failed BT_LOGR("Pair failed! Abort pairing."); // Notify adapter of pairing aborted DistributeSignal(NS_LITERAL_STRING(PAIRING_ABORTED_ID), NS_LITERAL_STRING(KEY_ADAPTER)); // Reject pair promise if (!sBondingRunnableArray.IsEmpty()) { DispatchReplyError(sBondingRunnableArray[0], aStatus); sBondingRunnableArray.RemoveElementAt(0); } } else if (!sUnbondingRunnableArray.IsEmpty()) { // Active unpair failed // Reject unpair promise DispatchReplyError(sUnbondingRunnableArray[0], aStatus); sUnbondingRunnableArray.RemoveElementAt(0); } return; } // Retrieve and remove pairing device name from hash table nsString deviceName; bool nameExists = sPairingNameTable.Get(aRemoteBdAddr, &deviceName); if (nameExists) { sPairingNameTable.Remove(aRemoteBdAddr); } // Update bonded address array and append pairing device name InfallibleTArray<BluetoothNamedValue> propertiesArray; nsString remoteBdAddr = nsString(aRemoteBdAddr); if (!bonded) { sAdapterBondedAddressArray.RemoveElement(remoteBdAddr); } else { if (!sAdapterBondedAddressArray.Contains(remoteBdAddr)) { sAdapterBondedAddressArray.AppendElement(remoteBdAddr); } // We don't assert |!deviceName.IsEmpty()| here since empty string is // also a valid name. According to Bluetooth Core Spec. v3.0 - Sec. 6.22, // "a valid Bluetooth name is a UTF-8 encoding string which is up to 248 // bytes in length." MOZ_ASSERT(nameExists); BT_APPEND_NAMED_VALUE(propertiesArray, "Name", deviceName); } // Notify device of attribute changed BT_APPEND_NAMED_VALUE(propertiesArray, "Paired", bonded); DistributeSignal(NS_LITERAL_STRING("PropertyChanged"), aRemoteBdAddr, BluetoothValue(propertiesArray)); // Notify adapter of device paired/unpaired BT_INSERT_NAMED_VALUE(propertiesArray, 0, "Address", remoteBdAddr); DistributeSignal(bonded ? NS_LITERAL_STRING(DEVICE_PAIRED_ID) : NS_LITERAL_STRING(DEVICE_UNPAIRED_ID), NS_LITERAL_STRING(KEY_ADAPTER), BluetoothValue(propertiesArray)); // Resolve existing pair/unpair promise if (bonded && !sBondingRunnableArray.IsEmpty()) { DispatchReplySuccess(sBondingRunnableArray[0]); sBondingRunnableArray.RemoveElementAt(0); } else if (!bonded && !sUnbondingRunnableArray.IsEmpty()) { DispatchReplySuccess(sUnbondingRunnableArray[0]); sUnbondingRunnableArray.RemoveElementAt(0); } }
void BluetoothServiceBluedroid::AdapterStateChangedNotification(bool aState) { MOZ_ASSERT(NS_IsMainThread()); BT_LOGR("BT_STATE: %d", aState); sAdapterEnabled = aState; if (!sAdapterEnabled) { static void (* const sDeinitManager[])(BluetoothProfileResultHandler*) = { BluetoothHfpManager::DeinitHfpInterface, BluetoothA2dpManager::DeinitA2dpInterface, BluetoothGattManager::DeinitGattInterface }; // Return error if BluetoothService is unavailable BluetoothService* bs = BluetoothService::Get(); NS_ENSURE_TRUE_VOID(bs); // Cleanup static adapter properties and notify adapter. sAdapterBdAddress.Truncate(); sAdapterBdName.Truncate(); InfallibleTArray<BluetoothNamedValue> props; BT_APPEND_NAMED_VALUE(props, "Name", sAdapterBdName); BT_APPEND_NAMED_VALUE(props, "Address", sAdapterBdAddress); if (sAdapterDiscoverable) { sAdapterDiscoverable = false; BT_APPEND_NAMED_VALUE(props, "Discoverable", false); } if (sAdapterDiscovering) { sAdapterDiscovering = false; BT_APPEND_NAMED_VALUE(props, "Discovering", false); } bs->DistributeSignal(NS_LITERAL_STRING("PropertyChanged"), NS_LITERAL_STRING(KEY_ADAPTER), BluetoothValue(props)); // Cleanup bluetooth interfaces after BT state becomes BT_STATE_OFF. nsRefPtr<ProfileDeinitResultHandler> res = new ProfileDeinitResultHandler(MOZ_ARRAY_LENGTH(sDeinitManager)); for (size_t i = 0; i < MOZ_ARRAY_LENGTH(sDeinitManager); ++i) { sDeinitManager[i](res); } } BluetoothService::AcknowledgeToggleBt(sAdapterEnabled); if (sAdapterEnabled) { // Bluetooth just enabled, clear profile controllers and runnable arrays. sControllerArray.Clear(); sChangeDiscoveryRunnableArray.Clear(); sSetPropertyRunnableArray.Clear(); sGetDeviceRunnableArray.Clear(); sFetchUuidsRunnableArray.Clear(); sBondingRunnableArray.Clear(); sUnbondingRunnableArray.Clear(); sPairingNameTable.Clear(); // Bluetooth scan mode is SCAN_MODE_CONNECTABLE by default, i.e., it should // be connectable and non-discoverable. NS_ENSURE_TRUE_VOID(sBtInterface); sBtInterface->SetAdapterProperty( BluetoothNamedValue(NS_ConvertUTF8toUTF16("Discoverable"), false), new SetAdapterPropertyDiscoverableResultHandler()); // Trigger BluetoothOppManager to listen BluetoothOppManager* opp = BluetoothOppManager::Get(); if (!opp || !opp->Listen()) { BT_LOGR("Fail to start BluetoothOppManager listening"); } } // Resolve promise if existed if (!sChangeAdapterStateRunnableArray.IsEmpty()) { DispatchReplySuccess(sChangeAdapterStateRunnableArray[0]); sChangeAdapterStateRunnableArray.RemoveElementAt(0); } }
void DottedCornerFinder::FindBestOverlap(Float aMinR, Float aMinBorderRadius, Float aMaxBorderRadius) { // If overlap is not calculateable, find it with binary search, // such that there exists i that C_i == C_n with the given overlap. FourFloats key(aMinR, mMaxR, aMinBorderRadius, aMaxBorderRadius); BestOverlap best; if (DottedCornerCache.Get(key, &best)) { mCount = best.count; mBestOverlap = best.overlap; return; } Float lower = 0.0f; Float upper = 0.5f; // Start from lower bound to find the minimum number of circles. Float overlap = 0.0f; mBestOverlap = overlap; size_t targetCount = 0; const Float OVERLAP_MARGIN = 0.1f; for (size_t j = 0; j < MAX_LOOP; j++) { Reset(); size_t count; Float actualOverlap; if (!GetCountAndLastOverlap(overlap, &count, &actualOverlap)) { if (j == 0) { mCount = mMaxCount; break; } } if (j == 0) { if (count < 3 || (count == 3 && actualOverlap > 0.5f)) { // |count == 3 && actualOverlap > 0.5f| means there could be // a circle but it is too near from both ends. // // if actualOverlap == 0.0 // 1 2 3 // +-------+-------+-------+-------+ // | ##### | ***** | ##### | ##### | // |#######|*******|#######|#######| // |###+###|***+***|###+###|###+###| // |# C_0 #|* C_1 *|# C_2 #|# C_n #| // | ##### | ***** | ##### | ##### | // +-------+-------+-------+-------+ // | // V // +-------+---+-------+---+-------+ // | ##### | | ##### | | ##### | // |#######| |#######| |#######| // |###+###| |###+###| |###+###| Find the best overlap to place // |# C_0 #| |# C_1 #| |# C_n #| C_1 at the middle of them // | ##### | | ##### | | ##### | // +-------+---+-------+---|-------+ // // if actualOverlap == 0.5 // 1 2 3 // +-------+-------+-------+---+ // | ##### | ***** | ##### |## | // |#######|*******|##### C_n #| // |###+###|***+***|###+###+###| // |# C_0 #|* C_1 *|# C_2 #|###| // | ##### | ***** | ##### |## | // +-------+-------+-------+---+ // | // V // +-------+-+-------+-+-------+ // | ##### | | ##### | | ##### | // |#######| |#######| |#######| // |###+###| |###+###| |###+###| Even if we place C_1 at the middle // |# C_0 #| |# C_1 #| |# C_n #| of them, it's too near from them // | ##### | | ##### | | ##### | // +-------+-+-------+-|-------+ // | // V // +-------+-----------+-------+ // | ##### | | ##### | // |#######| |#######| // |###+###| |###+###| Do not draw any circle // |# C_0 #| |# C_n #| // | ##### | | ##### | // +-------+-----------+-------+ mCount = 0; break; } // targetCount should be 2n, as we're searching C_1 to C_n. // // targetCount = 4 // mCount = 1 // 1 2 3 4 // +-------+-------+-------+-------+-------+ // | ##### | ***** | ##### | ***** | ##### | // |#######|*******|#######|*******|#######| // |###+###|***+***|###+###|***+***|###+###| // |# C_0 #|* C_1 *|# C_2 #|* C_3 *|# C_n #| // | ##### | ***** | ##### | ***** | ##### | // +-------+-------+-------+-------+-------+ // 1 // // targetCount = 6 // mCount = 2 // 1 2 3 4 5 6 // +-------+-------+-------+-------+-------+-------+-------+ // | ##### | ***** | ##### | ***** | ##### | ***** | ##### | // |#######|*******|#######|*******|#######|*******|#######| // |###+###|***+***|###+###|***+***|###+###|***+***|###+###| // |# C_0 #|* C_1 *|# C_2 #|* C_3 *|# C_4 #|* C_5 *|# C_n #| // | ##### | ***** | ##### | ***** | ##### | ***** | ##### | // +-------+-------+-------+-------+-------+-------+-------+ // 1 2 if (count % 2) { targetCount = count + 1; } else { targetCount = count; } mCount = targetCount / 2 - 1; } if (count == targetCount) { mBestOverlap = overlap; if (fabs(actualOverlap - overlap) < OVERLAP_MARGIN) { break; } // We started from upper bound, no need to update range when j == 0. if (j > 0) { if (actualOverlap > overlap) { lower = overlap; } else { upper = overlap; } } } else { // |j == 0 && count != targetCount| means that |targetCount = count + 1|, // and we started from upper bound, no need to update range when j == 0. if (j > 0) { if (count > targetCount) { upper = overlap; } else { lower = overlap; } } } overlap = (upper + lower) / 2.0f; } if (DottedCornerCache.Count() > DottedCornerCacheSize) { DottedCornerCache.Clear(); } DottedCornerCache.Put(key, BestOverlap(mBestOverlap, mCount)); }
namespace mozilla { using namespace gfx; static inline Float Square(Float x) { return x * x; } static Point PointRotateCCW90(const Point& aP) { return Point(aP.y, -aP.x); } struct BestOverlap { Float overlap; size_t count; BestOverlap() : overlap(0.0f), count(0) {} BestOverlap(Float aOverlap, size_t aCount) : overlap(aOverlap), count(aCount) {} }; static const size_t DottedCornerCacheSize = 256; nsDataHashtable<FourFloatsHashKey, BestOverlap> DottedCornerCache; DottedCornerFinder::DottedCornerFinder(const Bezier& aOuterBezier, const Bezier& aInnerBezier, Corner aCorner, Float aBorderRadiusX, Float aBorderRadiusY, const Point& aC0, Float aR0, const Point& aCn, Float aRn, const Size& aCornerDim) : mOuterBezier(aOuterBezier), mInnerBezier(aInnerBezier), mCorner(aCorner), mNormalSign((aCorner == C_TL || aCorner == C_BR) ? -1.0f : 1.0f), mC0(aC0), mCn(aCn), mR0(aR0), mRn(aRn), mMaxR(std::max(aR0, aRn)), mCenterCurveOrigin(mC0.x, mCn.y), mInnerCurveOrigin(mInnerBezier.mPoints[0].x, mInnerBezier.mPoints[3].y), mBestOverlap(0.0f), mHasZeroBorderWidth(false), mHasMore(true), mMaxCount(aCornerDim.width + aCornerDim.height), mType(OTHER), mI(0), mCount(0) { NS_ASSERTION(mR0 > 0.0f || mRn > 0.0f, "At least one side should have non-zero radius."); mInnerWidth = fabs(mInnerBezier.mPoints[0].x - mInnerBezier.mPoints[3].x); mInnerHeight = fabs(mInnerBezier.mPoints[0].y - mInnerBezier.mPoints[3].y); DetermineType(aBorderRadiusX, aBorderRadiusY); Reset(); } static bool IsSingleCurve(Float aMinR, Float aMaxR, Float aMinBorderRadius, Float aMaxBorderRadius) { return aMinR > 0.0f && aMinBorderRadius > aMaxR * 4.0f && aMinBorderRadius / aMaxBorderRadius > 0.5f; } void DottedCornerFinder::DetermineType(Float aBorderRadiusX, Float aBorderRadiusY) { // Calculate parameters for the center curve before swap. Float centerCurveWidth = fabs(mC0.x - mCn.x); Float centerCurveHeight = fabs(mC0.y - mCn.y); Point cornerPoint(mCn.x, mC0.y); bool swapped = false; if (mR0 < mRn) { // Always draw from wider side to thinner side. Swap(mC0, mCn); Swap(mR0, mRn); Swap(mInnerBezier.mPoints[0], mInnerBezier.mPoints[3]); Swap(mInnerBezier.mPoints[1], mInnerBezier.mPoints[2]); Swap(mOuterBezier.mPoints[0], mOuterBezier.mPoints[3]); Swap(mOuterBezier.mPoints[1], mOuterBezier.mPoints[2]); mNormalSign = -mNormalSign; swapped = true; } // See the comment at mType declaration for each condition. Float minR = std::min(mR0, mRn); Float minBorderRadius = std::min(aBorderRadiusX, aBorderRadiusY); Float maxBorderRadius = std::max(aBorderRadiusX, aBorderRadiusY); if (IsSingleCurve(minR, mMaxR, minBorderRadius, maxBorderRadius)) { if (mR0 == mRn) { Float borderLength; if (minBorderRadius == maxBorderRadius) { mType = PERFECT; borderLength = M_PI * centerCurveHeight / 2.0f; mCenterCurveR = centerCurveWidth; } else { mType = SINGLE_CURVE_AND_RADIUS; borderLength = GetQuarterEllipticArcLength(centerCurveWidth, centerCurveHeight); } Float diameter = mR0 * 2.0f; size_t count = round(borderLength / diameter); if (count % 2) { count++; } mCount = count / 2 - 1; if (mCount > 0) { mBestOverlap = 1.0f - borderLength / (diameter * count); } } else { mType = SINGLE_CURVE; } } if (mType == SINGLE_CURVE_AND_RADIUS || mType == SINGLE_CURVE) { Size cornerSize(centerCurveWidth, centerCurveHeight); GetBezierPointsForCorner(&mCenterBezier, mCorner, cornerPoint, cornerSize); if (swapped) { Swap(mCenterBezier.mPoints[0], mCenterBezier.mPoints[3]); Swap(mCenterBezier.mPoints[1], mCenterBezier.mPoints[2]); } } if (minR == 0.0f) { mHasZeroBorderWidth = true; } if ((mType == SINGLE_CURVE || mType == OTHER) && !mHasZeroBorderWidth) { FindBestOverlap(minR, minBorderRadius, maxBorderRadius); } } bool DottedCornerFinder::HasMore(void) const { if (mHasZeroBorderWidth) { return mI < mMaxCount && mHasMore; } return mI < mCount; } DottedCornerFinder::Result DottedCornerFinder::Next(void) { mI++; if (mType == PERFECT) { Float phi = mI * 4.0f * mR0 * (1 - mBestOverlap) / mCenterCurveR; if (mCorner == C_TL) { phi = -M_PI / 2.0f - phi; } else if (mCorner == C_TR) { phi = -M_PI / 2.0f + phi; } else if (mCorner == C_BR) { phi = M_PI / 2.0f - phi; } else { phi = M_PI / 2.0f + phi; } Point C(mCenterCurveOrigin.x + mCenterCurveR * cos(phi), mCenterCurveOrigin.y + mCenterCurveR * sin(phi)); return DottedCornerFinder::Result(C, mR0); } // Find unfilled and filled circles. (void)FindNext(mBestOverlap); if (mHasMore) { (void)FindNext(mBestOverlap); } return Result(mLastC, mLastR); } void DottedCornerFinder::Reset(void) { mLastC = mC0; mLastR = mR0; mLastT = 0.0f; mHasMore = true; } void DottedCornerFinder::FindPointAndRadius(Point& C, Float& r, const Point& innerTangent, const Point& normal, Float t) { // Find radius for the given tangent point on the inner curve such that the // circle is also tangent to the outer curve. NS_ASSERTION(mType == OTHER, "Wrong mType"); Float lower = 0.0f; Float upper = mMaxR; const Float DIST_MARGIN = 0.1f; for (size_t i = 0; i < MAX_LOOP; i++) { r = (upper + lower) / 2.0f; C = innerTangent + normal * r; Point Near = FindBezierNearestPoint(mOuterBezier, C, t); Float distSquare = (C - Near).LengthSquare(); if (distSquare > Square(r + DIST_MARGIN)) { lower = r; } else if (distSquare < Square(r - DIST_MARGIN)) { upper = r; } else { break; } } } Float DottedCornerFinder::FindNext(Float overlap) { Float lower = mLastT; Float upper = 1.0f; Float t; Point C = mLastC; Float r = 0.0f; Float factor = (1.0f - overlap); Float circlesDist = 0.0f; Float expectedDist = 0.0f; const Float DIST_MARGIN = 0.1f; if (mType == SINGLE_CURVE_AND_RADIUS) { r = mR0; expectedDist = (r + mLastR) * factor; // Find C_i on the center curve. for (size_t i = 0; i < MAX_LOOP; i++) { t = (upper + lower) / 2.0f; C = GetBezierPoint(mCenterBezier, t); // Check overlap along arc. circlesDist = GetBezierLength(mCenterBezier, mLastT, t); if (circlesDist < expectedDist - DIST_MARGIN) { lower = t; } else if (circlesDist > expectedDist + DIST_MARGIN) { upper = t; } else { break; } } } else if (mType == SINGLE_CURVE) { // Find C_i on the center curve, and calculate r_i. for (size_t i = 0; i < MAX_LOOP; i++) { t = (upper + lower) / 2.0f; C = GetBezierPoint(mCenterBezier, t); Point Diff = GetBezierDifferential(mCenterBezier, t); Float DiffLength = Diff.Length(); if (DiffLength == 0.0f) { // Basically this shouldn't happen. // If differential is 0, we cannot calculate tangent circle, // skip this point. t = (t + upper) / 2.0f; continue; } Point normal = PointRotateCCW90(Diff / DiffLength) * (-mNormalSign); r = CalculateDistanceToEllipticArc(C, normal, mInnerCurveOrigin, mInnerWidth, mInnerHeight); // Check overlap along arc. circlesDist = GetBezierLength(mCenterBezier, mLastT, t); expectedDist = (r + mLastR) * factor; if (circlesDist < expectedDist - DIST_MARGIN) { lower = t; } else if (circlesDist > expectedDist + DIST_MARGIN) { upper = t; } else { break; } } } else { Float distSquareMax = Square(mMaxR * 3.0f); Float circlesDistSquare = 0.0f; // Find C_i and r_i. for (size_t i = 0; i < MAX_LOOP; i++) { t = (upper + lower) / 2.0f; Point innerTangent = GetBezierPoint(mInnerBezier, t); if ((innerTangent - mLastC).LengthSquare() > distSquareMax) { // It's clear that this tangent point is too far, skip it. upper = t; continue; } Point Diff = GetBezierDifferential(mInnerBezier, t); Float DiffLength = Diff.Length(); if (DiffLength == 0.0f) { // Basically this shouldn't happen. // If differential is 0, we cannot calculate tangent circle, // skip this point. t = (t + upper) / 2.0f; continue; } Point normal = PointRotateCCW90(Diff / DiffLength) * mNormalSign; FindPointAndRadius(C, r, innerTangent, normal, t); // Check overlap with direct distance. circlesDistSquare = (C - mLastC).LengthSquare(); expectedDist = (r + mLastR) * factor; if (circlesDistSquare < Square(expectedDist - DIST_MARGIN)) { lower = t; } else if (circlesDistSquare > Square(expectedDist + DIST_MARGIN)) { upper = t; } else { break; } } circlesDist = sqrt(circlesDistSquare); } if (mHasZeroBorderWidth) { // When calculating circle around r=0, it may result in wrong radius that // is bigger than previous circle. Detect it and stop calculating. const Float R_MARGIN = 0.1f; if (mLastR < R_MARGIN && r > mLastR) { mHasMore = false; mLastR = 0.0f; return 0.0f; } } mLastT = t; mLastC = C; mLastR = r; if (mHasZeroBorderWidth) { const Float T_MARGIN = 0.001f; if (mLastT >= 1.0f - T_MARGIN || (mLastC - mCn).LengthSquare() < Square(mLastR)) { mHasMore = false; } } if (expectedDist == 0.0f) { return 0.0f; } return 1.0f - circlesDist * factor / expectedDist; } void DottedCornerFinder::FindBestOverlap(Float aMinR, Float aMinBorderRadius, Float aMaxBorderRadius) { // If overlap is not calculateable, find it with binary search, // such that there exists i that C_i == C_n with the given overlap. FourFloats key(aMinR, mMaxR, aMinBorderRadius, aMaxBorderRadius); BestOverlap best; if (DottedCornerCache.Get(key, &best)) { mCount = best.count; mBestOverlap = best.overlap; return; } Float lower = 0.0f; Float upper = 0.5f; // Start from lower bound to find the minimum number of circles. Float overlap = 0.0f; mBestOverlap = overlap; size_t targetCount = 0; const Float OVERLAP_MARGIN = 0.1f; for (size_t j = 0; j < MAX_LOOP; j++) { Reset(); size_t count; Float actualOverlap; if (!GetCountAndLastOverlap(overlap, &count, &actualOverlap)) { if (j == 0) { mCount = mMaxCount; break; } } if (j == 0) { if (count < 3 || (count == 3 && actualOverlap > 0.5f)) { // |count == 3 && actualOverlap > 0.5f| means there could be // a circle but it is too near from both ends. // // if actualOverlap == 0.0 // 1 2 3 // +-------+-------+-------+-------+ // | ##### | ***** | ##### | ##### | // |#######|*******|#######|#######| // |###+###|***+***|###+###|###+###| // |# C_0 #|* C_1 *|# C_2 #|# C_n #| // | ##### | ***** | ##### | ##### | // +-------+-------+-------+-------+ // | // V // +-------+---+-------+---+-------+ // | ##### | | ##### | | ##### | // |#######| |#######| |#######| // |###+###| |###+###| |###+###| Find the best overlap to place // |# C_0 #| |# C_1 #| |# C_n #| C_1 at the middle of them // | ##### | | ##### | | ##### | // +-------+---+-------+---|-------+ // // if actualOverlap == 0.5 // 1 2 3 // +-------+-------+-------+---+ // | ##### | ***** | ##### |## | // |#######|*******|##### C_n #| // |###+###|***+***|###+###+###| // |# C_0 #|* C_1 *|# C_2 #|###| // | ##### | ***** | ##### |## | // +-------+-------+-------+---+ // | // V // +-------+-+-------+-+-------+ // | ##### | | ##### | | ##### | // |#######| |#######| |#######| // |###+###| |###+###| |###+###| Even if we place C_1 at the middle // |# C_0 #| |# C_1 #| |# C_n #| of them, it's too near from them // | ##### | | ##### | | ##### | // +-------+-+-------+-|-------+ // | // V // +-------+-----------+-------+ // | ##### | | ##### | // |#######| |#######| // |###+###| |###+###| Do not draw any circle // |# C_0 #| |# C_n #| // | ##### | | ##### | // +-------+-----------+-------+ mCount = 0; break; } // targetCount should be 2n, as we're searching C_1 to C_n. // // targetCount = 4 // mCount = 1 // 1 2 3 4 // +-------+-------+-------+-------+-------+ // | ##### | ***** | ##### | ***** | ##### | // |#######|*******|#######|*******|#######| // |###+###|***+***|###+###|***+***|###+###| // |# C_0 #|* C_1 *|# C_2 #|* C_3 *|# C_n #| // | ##### | ***** | ##### | ***** | ##### | // +-------+-------+-------+-------+-------+ // 1 // // targetCount = 6 // mCount = 2 // 1 2 3 4 5 6 // +-------+-------+-------+-------+-------+-------+-------+ // | ##### | ***** | ##### | ***** | ##### | ***** | ##### | // |#######|*******|#######|*******|#######|*******|#######| // |###+###|***+***|###+###|***+***|###+###|***+***|###+###| // |# C_0 #|* C_1 *|# C_2 #|* C_3 *|# C_4 #|* C_5 *|# C_n #| // | ##### | ***** | ##### | ***** | ##### | ***** | ##### | // +-------+-------+-------+-------+-------+-------+-------+ // 1 2 if (count % 2) { targetCount = count + 1; } else { targetCount = count; } mCount = targetCount / 2 - 1; } if (count == targetCount) { mBestOverlap = overlap; if (fabs(actualOverlap - overlap) < OVERLAP_MARGIN) { break; } // We started from upper bound, no need to update range when j == 0. if (j > 0) { if (actualOverlap > overlap) { lower = overlap; } else { upper = overlap; } } } else { // |j == 0 && count != targetCount| means that |targetCount = count + 1|, // and we started from upper bound, no need to update range when j == 0. if (j > 0) { if (count > targetCount) { upper = overlap; } else { lower = overlap; } } } overlap = (upper + lower) / 2.0f; } if (DottedCornerCache.Count() > DottedCornerCacheSize) { DottedCornerCache.Clear(); } DottedCornerCache.Put(key, BestOverlap(mBestOverlap, mCount)); } bool DottedCornerFinder::GetCountAndLastOverlap(Float aOverlap, size_t* aCount, Float* aActualOverlap) { // Return the number of circles and the last circles' overlap for the // given overlap. Reset(); const Float T_MARGIN = 0.001f; const Float DIST_MARGIN = 0.1f; const Float DIST_MARGIN_SQUARE = Square(DIST_MARGIN); for (size_t i = 0; i < mMaxCount; i++) { Float actualOverlap = FindNext(aOverlap); if (mLastT >= 1.0f - T_MARGIN || (mLastC - mCn).LengthSquare() < DIST_MARGIN_SQUARE) { *aCount = i + 1; *aActualOverlap = actualOverlap; return true; } } return false; } } // namespace mozilla