float VirtualFont::getOffsetX(const LineLayout &layout, Alignment align) const
 {
     switch (align)
     {
         case ALIGN_MIDDLE:
             return -0.5f * getAdvance(layout);
             
         case ALIGN_RIGHT:
             return -getAdvance(layout);
             
         default:
             return 0;
     }
 }
Beispiel #2
0
	float Font::getAdvance( const char c)
	{
		char string[2];
		string[0] = c;
		string[1] = 0;
		return getAdvance(string);
	}
Beispiel #3
0
	float Font::getAdvance( const wchar_t c)
	{
		wchar_t string[2];
		string[0] = c;
		string[1] = 0;
		return getAdvance(string);
	}
Beispiel #4
0
void EngineState::periodicFastCallback(DECLARE_ENGINE_PARAMETER_F) {
	int rpm = ENGINE(rpmCalculator.rpmValue);

	sparkDwell = getSparkDwell(rpm PASS_ENGINE_PARAMETER);
	dwellAngle = sparkDwell / getOneDegreeTimeMs(rpm);

	iatFuelCorrection = getIatCorrection(iat PASS_ENGINE_PARAMETER);
	cltFuelCorrection = getCltCorrection(clt PASS_ENGINE_PARAMETER);

	engineNoiseHipLevel = interpolate2d(rpm, engineConfiguration->knockNoiseRpmBins,
					engineConfiguration->knockNoise, ENGINE_NOISE_CURVE_SIZE);

	baroCorrection = getBaroCorrection(PASS_ENGINE_PARAMETER_F);

	injectionOffset = getinjectionOffset(rpm PASS_ENGINE_PARAMETER);
	float engineLoad = getEngineLoadT(PASS_ENGINE_PARAMETER_F);
	timingAdvance = getAdvance(rpm, engineLoad PASS_ENGINE_PARAMETER);

	if (engineConfiguration->algorithm == LM_SPEED_DENSITY) {
		float coolantC = ENGINE(engineState.clt);
		float intakeC = ENGINE(engineState.iat);
		float tps = getTPS(PASS_ENGINE_PARAMETER_F);
		tChargeK = convertCelsiusToKelvin(getTCharge(rpm, tps, coolantC, intakeC));
		float map = getMap();

		/**
		 * *0.01 because of https://sourceforge.net/p/rusefi/tickets/153/
		 */
		currentVE = baroCorrection * veMap.getValue(map, rpm) * 0.01;
		targerAFR = afrMap.getValue(map, rpm);
	} else {
		baseTableFuel = getBaseTableFuel(engineConfiguration, rpm, engineLoad);
	}

}
Beispiel #5
0
float Font::getAdvance(uint16_t charcode, float fontSize) const {
  MetricsData::const_iterator itr = mMetrics.find(charcode);
  if (itr != mMetrics.end())
    return getAdvance(itr->second, fontSize);

  return 0.0f;
}
float ofxTextAlign::getWidth(const char *str, bool single_line)
{
	float ret = 0;
	float tmp = 0;
	const char *ptr = str;
	char prev = -1;
	while(*ptr != '\0' && !(single_line && *ptr == '\n')) {
		switch(*ptr) {
		case '\n':
			ret = max(ret, tmp);
			tmp = 0;
			prev = -1;
			break;
		default:
			if(prev != -1) {
				tmp += getKerning(*ptr, prev);
			}
			prev = *ptr;
			tmp += getAdvance(*ptr);
			break;
		}
		++ptr;
	}
	return max(ret, tmp);
}
Beispiel #7
0
// Devanagari ka+virama+ka
TEST(Measurement, getRunAdvance_kka) {
  const float unligated[] = {30.0, 0.0, 30.0};
  EXPECT_EQ(0.0, getAdvance(unligated, "| U+0915 U+094D U+0915"));
  EXPECT_EQ(30.0, getAdvance(unligated, "U+0915 | U+094D U+0915"));
  EXPECT_EQ(30.0, getAdvance(unligated, "U+0915 U+094D | U+0915"));
  EXPECT_EQ(60.0, getAdvance(unligated, "U+0915 U+094D U+0915 |"));

  const float ligated[] = {30.0, 0.0, 0.0};
  EXPECT_EQ(0.0, getAdvance(ligated, "| U+0915 U+094D U+0915"));
  EXPECT_EQ(30.0, getAdvance(ligated, "U+0915 | U+094D U+0915"));
  EXPECT_EQ(30.0, getAdvance(ligated, "U+0915 U+094D | U+0915"));
  EXPECT_EQ(30.0, getAdvance(ligated, "U+0915 U+094D U+0915 |"));
}
const char* ofxTextAlign::drawLine(const char *str, float x, float y)
{
	const char *ptr = str;
	int letter_count = getLetterCount(str, true);
	float extra_spacing = letter_count>1?(getDrawWidth(str, true)-getWidth(str, true))/(float)(letter_count-1):0;
	while(*ptr != '\0') {
		if(*ptr=='\n') {
			++ptr;
			break;
		}
		float interval = getAdvance(*ptr) + getKerning(*(ptr+1), *ptr) + extra_spacing;
		ptr = drawChar(ptr, x, y);
		x += interval;
	}
	return ptr;
}
Beispiel #9
0
// Latin fi
TEST(Measurement, getRunAdvance_fi) {
  const float unligated[] = {30.0, 20.0};
  EXPECT_EQ(0.0, getAdvance(unligated, "| 'f' 'i'"));
  EXPECT_EQ(30.0, getAdvance(unligated, "'f' | 'i'"));
  EXPECT_EQ(50.0, getAdvance(unligated, "'f' 'i' |"));

  const float ligated[] = {40.0, 0.0};
  EXPECT_EQ(0.0, getAdvance(ligated, "| 'f' 'i'"));
  EXPECT_EQ(20.0, getAdvance(ligated, "'f' | 'i'"));
  EXPECT_EQ(40.0, getAdvance(ligated, "'f' 'i' |"));
}
float ofxTextAlign::getOffsetX(const char *str, unsigned int flags, bool single_line)
{
	unsigned int flag = flags&HORIZONTAL_ALIGN_MASK;
	if(HORIZONTAL_ALIGN_LEFT == flag) {
		return 0;
	}
	else if(single_line && getLetterCount(str, true) < 2) {
		float width = getAdvance(*str);
		switch(flag) {
			case HORIZONTAL_ALIGN_CENTER:	return -width/2.f;
			case HORIZONTAL_ALIGN_RIGHT:	return -width;
		}
	}
	else {
		float width = getDrawWidth(str, single_line);
		switch(flag) {
			case HORIZONTAL_ALIGN_CENTER:	return -width/2.f;
			case HORIZONTAL_ALIGN_RIGHT:	return -width;
		}
	}
	return 0;
}
Beispiel #11
0
static void printAdvance(int rpm, int maf100) {
	float advance = getAdvance(rpm, maf100 / 100.0);
	print("advance for %d rpm %d maf100: %f\r\n", rpm, maf100, advance);
}
SkAdvancedTypefaceMetrics::AdvanceMetric<Data>* getAdvanceData(
        FontHandle fontHandle,
        int num_glyphs,
        const uint32_t* subsetGlyphIDs,
        uint32_t subsetGlyphIDsLength,
        bool (*getAdvance)(FontHandle fontHandle, int gId, Data* data)) {
    // Assuming that on average, the ASCII representation of an advance plus
    // a space is 8 characters and the ASCII representation of a glyph id is 3
    // characters, then the following cut offs for using different range types
    // apply:
    // The cost of stopping and starting the range is 7 characers
    //  a. Removing 4 0's or don't care's is a win
    // The cost of stopping and starting the range plus a run is 22
    // characters
    //  b. Removing 3 repeating advances is a win
    //  c. Removing 2 repeating advances and 3 don't cares is a win
    // When not currently in a range the cost of a run over a range is 16
    // characaters, so:
    //  d. Removing a leading 0/don't cares is a win because it is omitted
    //  e. Removing 2 repeating advances is a win

    SkTScopedPtr<SkAdvancedTypefaceMetrics::AdvanceMetric<Data> > result;
    SkAdvancedTypefaceMetrics::AdvanceMetric<Data>* curRange;
    SkAdvancedTypefaceMetrics::AdvanceMetric<Data>* prevRange = NULL;
    Data lastAdvance = kInvalidAdvance;
    int repeatedAdvances = 0;
    int wildCardsInRun = 0;
    int trailingWildCards = 0;
    uint32_t subsetIndex = 0;

    // Limit the loop count to glyph id ranges provided.
    int firstIndex = 0;
    int lastIndex = num_glyphs;
    if (subsetGlyphIDs) {
        firstIndex = static_cast<int>(subsetGlyphIDs[0]);
        lastIndex =
                static_cast<int>(subsetGlyphIDs[subsetGlyphIDsLength - 1]) + 1;
    }
    curRange = appendRange(&result, firstIndex);

    for (int gId = firstIndex; gId <= lastIndex; gId++) {
        Data advance = kInvalidAdvance;
        if (gId < lastIndex) {
            // Get glyph id only when subset is NULL, or the id is in subset.
            if (!subsetGlyphIDs ||
                (subsetIndex < subsetGlyphIDsLength &&
                 static_cast<uint32_t>(gId) == subsetGlyphIDs[subsetIndex])) {
                SkAssertResult(getAdvance(fontHandle, gId, &advance));
                ++subsetIndex;
            } else {
                advance = kDontCareAdvance;
            }
        }
        if (advance == lastAdvance) {
            repeatedAdvances++;
            trailingWildCards = 0;
        } else if (advance == kDontCareAdvance) {
            wildCardsInRun++;
            trailingWildCards++;
        } else if (curRange->fAdvance.count() ==
                   repeatedAdvances + 1 + wildCardsInRun) {  // All in run.
            if (lastAdvance == 0) {
                resetRange(curRange, gId);
                trailingWildCards = 0;
            } else if (repeatedAdvances + 1 >= 2 || trailingWildCards >= 4) {
                finishRange(curRange, gId - 1,
                            SkAdvancedTypefaceMetrics::WidthRange::kRun);
                prevRange = curRange;
                curRange = appendRange(&curRange->fNext, gId);
                trailingWildCards = 0;
            }
            repeatedAdvances = 0;
            wildCardsInRun = trailingWildCards;
            trailingWildCards = 0;
        } else {
            if (lastAdvance == 0 &&
                    repeatedAdvances + 1 + wildCardsInRun >= 4) {
                finishRange(curRange,
                            gId - repeatedAdvances - wildCardsInRun - 2,
                            SkAdvancedTypefaceMetrics::WidthRange::kRange);
                prevRange = curRange;
                curRange = appendRange(&curRange->fNext, gId);
                trailingWildCards = 0;
            } else if (trailingWildCards >= 4 && repeatedAdvances + 1 < 2) {
                finishRange(curRange,
                            gId - trailingWildCards - 1,
                            SkAdvancedTypefaceMetrics::WidthRange::kRange);
                prevRange = curRange;
                curRange = appendRange(&curRange->fNext, gId);
                trailingWildCards = 0;
            } else if (lastAdvance != 0 &&
                       (repeatedAdvances + 1 >= 3 ||
                        (repeatedAdvances + 1 >= 2 && wildCardsInRun >= 3))) {
                finishRange(curRange,
                            gId - repeatedAdvances - wildCardsInRun - 2,
                            SkAdvancedTypefaceMetrics::WidthRange::kRange);
                curRange =
                    appendRange(&curRange->fNext,
                                gId - repeatedAdvances - wildCardsInRun - 1);
                curRange->fAdvance.append(1, &lastAdvance);
                finishRange(curRange, gId - 1,
                            SkAdvancedTypefaceMetrics::WidthRange::kRun);
                prevRange = curRange;
                curRange = appendRange(&curRange->fNext, gId);
                trailingWildCards = 0;
            }
            repeatedAdvances = 0;
            wildCardsInRun = trailingWildCards;
            trailingWildCards = 0;
        }
        curRange->fAdvance.append(1, &advance);
        if (advance != kDontCareAdvance) {
            lastAdvance = advance;
        }
    }
    if (curRange->fStartId == lastIndex) {
        SkASSERT(prevRange);
        SkASSERT(prevRange->fNext->fStartId == lastIndex);
        prevRange->fNext.reset();
    } else {
        finishRange(curRange, lastIndex - 1,
                    SkAdvancedTypefaceMetrics::WidthRange::kRange);
    }
    return result.release();
}
Beispiel #13
0
void EngineState::periodicFastCallback(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
	efitick_t nowNt = getTimeNowNt();
	if (ENGINE(rpmCalculator).isCranking(PASS_ENGINE_PARAMETER_SIGNATURE)) {
		crankingTime = nowNt;
		timeSinceCranking = 0.0f;
	} else {
		timeSinceCranking = nowNt - crankingTime;
	}
	updateAuxValves(PASS_ENGINE_PARAMETER_SIGNATURE);

	int rpm = ENGINE(rpmCalculator).getRpm(PASS_ENGINE_PARAMETER_SIGNATURE);
	sparkDwell = getSparkDwell(rpm PASS_ENGINE_PARAMETER_SUFFIX);
	dwellAngle = sparkDwell / getOneDegreeTimeMs(rpm);
	if (hasAfrSensor(PASS_ENGINE_PARAMETER_SIGNATURE)) {
		engine->sensors.currentAfr = getAfr(PASS_ENGINE_PARAMETER_SIGNATURE);
	}

	// todo: move this into slow callback, no reason for IAT corr to be here
	iatFuelCorrection = getIatFuelCorrection(engine->sensors.iat PASS_ENGINE_PARAMETER_SUFFIX);
	// todo: move this into slow callback, no reason for CLT corr to be here
	if (boardConfiguration->useWarmupPidAfr && engine->sensors.clt < engineConfiguration->warmupAfrThreshold) {
		if (rpm < 200) {
			cltFuelCorrection = 1;
			warmupAfrPid.reset();
		} else {
			cltFuelCorrection = warmupAfrPid.getValue(warmupTargetAfr, engine->sensors.currentAfr, 1);
		}
#if ! EFI_UNIT_TEST || defined(__DOXYGEN__)
		if (engineConfiguration->debugMode == DBG_WARMUP_ENRICH) {
			tsOutputChannels.debugFloatField1 = warmupTargetAfr;
			warmupAfrPid.postState(&tsOutputChannels);
		}
#endif

	} else {
		cltFuelCorrection = getCltFuelCorrection(PASS_ENGINE_PARAMETER_SIGNATURE);
	}

	// update fuel consumption states
	fuelConsumption.update(nowNt PASS_ENGINE_PARAMETER_SUFFIX);

	// Fuel cut-off isn't just 0 or 1, it can be tapered
	fuelCutoffCorrection = getFuelCutOffCorrection(nowNt, rpm PASS_ENGINE_PARAMETER_SUFFIX);
	
	// post-cranking fuel enrichment.
	// for compatibility reasons, apply only if the factor is greater than zero (0.01 margin used)
	if (engineConfiguration->postCrankingFactor > 0.01f) {
		// convert to microsecs and then to seconds
		float timeSinceCrankingInSecs = NT2US(timeSinceCranking) / 1000000.0f;
		// use interpolation for correction taper
		postCrankingFuelCorrection = interpolateClamped(0.0f, engineConfiguration->postCrankingFactor, 
			engineConfiguration->postCrankingDurationSec, 1.0f, timeSinceCrankingInSecs);
	} else {
		postCrankingFuelCorrection = 1.0f;
	}

	cltTimingCorrection = getCltTimingCorrection(PASS_ENGINE_PARAMETER_SIGNATURE);

	engineNoiseHipLevel = interpolate2d("knock", rpm, engineConfiguration->knockNoiseRpmBins,
					engineConfiguration->knockNoise, ENGINE_NOISE_CURVE_SIZE);

	baroCorrection = getBaroCorrection(PASS_ENGINE_PARAMETER_SIGNATURE);

	injectionOffset = getinjectionOffset(rpm PASS_ENGINE_PARAMETER_SUFFIX);
	float engineLoad = getEngineLoadT(PASS_ENGINE_PARAMETER_SIGNATURE);
	timingAdvance = getAdvance(rpm, engineLoad PASS_ENGINE_PARAMETER_SUFFIX);

	if (engineConfiguration->fuelAlgorithm == LM_SPEED_DENSITY) {
		float coolantC = ENGINE(sensors.clt);
		float intakeC = ENGINE(sensors.iat);
		float tps = getTPS(PASS_ENGINE_PARAMETER_SIGNATURE);
		tChargeK = convertCelsiusToKelvin(getTCharge(rpm, tps, coolantC, intakeC PASS_ENGINE_PARAMETER_SUFFIX));
		float map = getMap();

		/**
		 * *0.01 because of https://sourceforge.net/p/rusefi/tickets/153/
		 */
		float rawVe = veMap.getValue(rpm, map);
		// get VE from the separate table for Idle
		if (CONFIG(useSeparateVeForIdle)) {
			float idleVe = interpolate2d("idleVe", rpm, config->idleVeBins, config->idleVe, IDLE_VE_CURVE_SIZE);
			// interpolate between idle table and normal (running) table using TPS threshold
			rawVe = interpolateClamped(0.0f, idleVe, boardConfiguration->idlePidDeactivationTpsThreshold, rawVe, tps);
		}
		currentVE = baroCorrection * rawVe * 0.01;
		targetAFR = afrMap.getValue(rpm, map);
	} else {
		baseTableFuel = getBaseTableFuel(rpm, engineLoad);
	}
}
Beispiel #14
0
void EngineState::periodicFastCallback(DECLARE_ENGINE_PARAMETER_F) {
	int rpm = ENGINE(rpmCalculator.rpmValue);

	efitick_t nowNt = getTimeNowNt();
	if (isCrankingR(rpm)) {
		crankingTime = nowNt;
	} else {
		timeSinceCranking = nowNt - crankingTime;
	}

	sparkDwell = getSparkDwell(rpm PASS_ENGINE_PARAMETER);
	dwellAngle = sparkDwell / getOneDegreeTimeMs(rpm);

	// todo: move this into slow callback, no reason for IAT corr to be here
	iatFuelCorrection = getIatCorrection(iat PASS_ENGINE_PARAMETER);
	// todo: move this into slow callback, no reason for CLT corr to be here
	if (boardConfiguration->useWarmupPidAfr && clt < engineConfiguration->warmupAfrThreshold) {
		if (rpm < 200) {
			cltFuelCorrection = 1;
			warmupAfrPid.reset();
		} else {
			cltFuelCorrection = warmupAfrPid.getValue(warmupTargetAfr, getAfr(PASS_ENGINE_PARAMETER_F), 1);
		}
#if ! EFI_UNIT_TEST || defined(__DOXYGEN__)
		if (engineConfiguration->debugMode == WARMUP_ENRICH) {
			tsOutputChannels.debugFloatField1 = warmupTargetAfr;
			warmupAfrPid.postState(&tsOutputChannels);
		}
#endif

	} else {
		cltFuelCorrection = getCltFuelCorrection(clt PASS_ENGINE_PARAMETER);
	}

	cltTimingCorrection = getCltTimingCorrection(clt PASS_ENGINE_PARAMETER);

	engineNoiseHipLevel = interpolate2d(rpm, engineConfiguration->knockNoiseRpmBins,
					engineConfiguration->knockNoise, ENGINE_NOISE_CURVE_SIZE);

	baroCorrection = getBaroCorrection(PASS_ENGINE_PARAMETER_F);

	injectionOffset = getinjectionOffset(rpm PASS_ENGINE_PARAMETER);
	float engineLoad = getEngineLoadT(PASS_ENGINE_PARAMETER_F);
	timingAdvance = getAdvance(rpm, engineLoad PASS_ENGINE_PARAMETER);

	if (engineConfiguration->fuelAlgorithm == LM_SPEED_DENSITY) {
		float coolantC = ENGINE(engineState.clt);
		float intakeC = ENGINE(engineState.iat);
		float tps = getTPS(PASS_ENGINE_PARAMETER_F);
		tChargeK = convertCelsiusToKelvin(getTCharge(rpm, tps, coolantC, intakeC PASS_ENGINE_PARAMETER));
		float map = getMap();

		/**
		 * *0.01 because of https://sourceforge.net/p/rusefi/tickets/153/
		 */
		currentVE = baroCorrection * veMap.getValue(rpm, map) * 0.01;
		targetAFR = afrMap.getValue(rpm, map);
	} else {
		baseTableFuel = getBaseTableFuel(engineConfiguration, rpm, engineLoad);
	}

}
void SkAdvancedTypefaceMetrics::setGlyphWidths(
        FontHandle fontHandle,
        int num_glyphs,
        const uint32_t* subsetGlyphIDs,
        uint32_t subsetGlyphIDsLength,
        bool (*getAdvance)(FontHandle fontHandle, int gId, int16_t* data)) {
    // Assuming that on average, the ASCII representation of an advance plus
    // a space is 8 characters and the ASCII representation of a glyph id is 3
    // characters, then the following cut offs for using different range types
    // apply:
    // The cost of stopping and starting the range is 7 characers
    //  a. Removing 4 0's or don't care's is a win
    // The cost of stopping and starting the range plus a run is 22
    // characters
    //  b. Removing 3 repeating advances is a win
    //  c. Removing 2 repeating advances and 3 don't cares is a win
    // When not currently in a range the cost of a run over a range is 16
    // characaters, so:
    //  d. Removing a leading 0/don't cares is a win because it is omitted
    //  e. Removing 2 repeating advances is a win

    WidthRange* prevRange = nullptr;
    int16_t lastAdvance = kInvalidAdvance;
    int repeatedAdvances = 0;
    int wildCardsInRun = 0;
    int trailingWildCards = 0;
    uint32_t subsetIndex = 0;

    // Limit the loop count to glyph id ranges provided.
    int firstIndex = 0;
    int lastIndex = num_glyphs;
    if (subsetGlyphIDs) {
        firstIndex = static_cast<int>(subsetGlyphIDs[0]);
        lastIndex =
                static_cast<int>(subsetGlyphIDs[subsetGlyphIDsLength - 1]) + 1;
    }
    WidthRange curRange(firstIndex);

    for (int gId = firstIndex; gId <= lastIndex; gId++) {
        int16_t advance = kInvalidAdvance;
        if (gId < lastIndex) {
            // Get glyph id only when subset is nullptr, or the id is in subset.
            SkASSERT(!subsetGlyphIDs || (subsetIndex < subsetGlyphIDsLength &&
                    static_cast<uint32_t>(gId) <= subsetGlyphIDs[subsetIndex]));
            if (!subsetGlyphIDs ||
                (subsetIndex < subsetGlyphIDsLength &&
                 static_cast<uint32_t>(gId) == subsetGlyphIDs[subsetIndex])) {
                SkAssertResult(getAdvance(fontHandle, gId, &advance));
                ++subsetIndex;
            } else {
                advance = kDontCareAdvance;
            }
        }
        if (advance == lastAdvance) {
            repeatedAdvances++;
            trailingWildCards = 0;
        } else if (advance == kDontCareAdvance) {
            wildCardsInRun++;
            trailingWildCards++;
        } else if (curRange.fAdvance.count() ==
                   repeatedAdvances + 1 + wildCardsInRun) {  // All in run.
            if (lastAdvance == 0) {
                curRange.fStartId = gId;  // reset
                curRange.fAdvance.setCount(0);
                trailingWildCards = 0;
            } else if (repeatedAdvances + 1 >= 2 || trailingWildCards >= 4) {
                FinishRange(&curRange, gId - 1, WidthRange::kRun);
                prevRange = fGlyphWidths.emplace_back(std::move(curRange));
                curRange = WidthRange(gId);
                trailingWildCards = 0;
            }
            repeatedAdvances = 0;
            wildCardsInRun = trailingWildCards;
            trailingWildCards = 0;
        } else {
            if (lastAdvance == 0 &&
                    repeatedAdvances + 1 + wildCardsInRun >= 4) {
                FinishRange(&curRange,
                            gId - repeatedAdvances - wildCardsInRun - 2,
                            WidthRange::kRange);
                prevRange = fGlyphWidths.emplace_back(std::move(curRange));
                curRange = WidthRange(gId);
                trailingWildCards = 0;
            } else if (trailingWildCards >= 4 && repeatedAdvances + 1 < 2) {
                FinishRange(&curRange, gId - trailingWildCards - 1,
                            WidthRange::kRange);
                prevRange = fGlyphWidths.emplace_back(std::move(curRange));
                curRange = WidthRange(gId);
                trailingWildCards = 0;
            } else if (lastAdvance != 0 &&
                       (repeatedAdvances + 1 >= 3 ||
                        (repeatedAdvances + 1 >= 2 && wildCardsInRun >= 3))) {
                FinishRange(&curRange,
                            gId - repeatedAdvances - wildCardsInRun - 2,
                            WidthRange::kRange);
                (void)fGlyphWidths.emplace_back(std::move(curRange));
                curRange =
                        WidthRange(gId - repeatedAdvances - wildCardsInRun - 1);
                curRange.fAdvance.append(1, &lastAdvance);
                FinishRange(&curRange, gId - 1, WidthRange::kRun);
                prevRange = fGlyphWidths.emplace_back(std::move(curRange));
                curRange = WidthRange(gId);
                trailingWildCards = 0;
            }
            repeatedAdvances = 0;
            wildCardsInRun = trailingWildCards;
            trailingWildCards = 0;
        }
        curRange.fAdvance.append(1, &advance);
        if (advance != kDontCareAdvance) {
            lastAdvance = advance;
        }
    }
    if (curRange.fStartId == lastIndex) {
        SkASSERT(prevRange);
        if (!prevRange) {
            fGlyphWidths.reset();
            return;  // https://crbug.com/567031
        }
    } else {
        FinishRange(&curRange, lastIndex - 1, WidthRange::kRange);
        fGlyphWidths.emplace_back(std::move(curRange));
    }
}