// TODO crbug.com/542701: This should be a method on ShapeResult.
void HarfBuzzShaper::insertRunIntoShapeResult(ShapeResult* result,
    PassOwnPtr<ShapeResult::RunInfo> runToInsert, unsigned startGlyph, unsigned numGlyphs,
    hb_buffer_t* harfBuzzBuffer)
{
    ASSERT(numGlyphs > 0);
    OwnPtr<ShapeResult::RunInfo> run(std::move(runToInsert));
    ASSERT(numGlyphs == run->m_glyphData.size());

    const SimpleFontData* currentFontData = run->m_fontData.get();
    const hb_glyph_info_t* glyphInfos = hb_buffer_get_glyph_infos(harfBuzzBuffer, 0);
    const hb_glyph_position_t* glyphPositions = hb_buffer_get_glyph_positions(harfBuzzBuffer, 0);
    const unsigned startCluster = HB_DIRECTION_IS_FORWARD(hb_buffer_get_direction(harfBuzzBuffer))
        ? glyphInfos[startGlyph].cluster : glyphInfos[startGlyph + numGlyphs - 1].cluster;

    float totalAdvance = 0.0f;
    FloatPoint glyphOrigin;
    bool hasVerticalOffsets = !HB_DIRECTION_IS_HORIZONTAL(run->m_direction);

    // HarfBuzz returns result in visual order, no need to flip for RTL.
    for (unsigned i = 0; i < numGlyphs; ++i) {
        uint16_t glyph = glyphInfos[startGlyph + i].codepoint;
        float offsetX = harfBuzzPositionToFloat(glyphPositions[startGlyph + i].x_offset);
        float offsetY = -harfBuzzPositionToFloat(glyphPositions[startGlyph + i].y_offset);

        // One out of x_advance and y_advance is zero, depending on
        // whether the buffer direction is horizontal or vertical.
        float advance = harfBuzzPositionToFloat(glyphPositions[startGlyph + i].x_advance - glyphPositions[startGlyph + i].y_advance);
        RELEASE_ASSERT(m_normalizedBufferLength > glyphInfos[startGlyph + i].cluster);

        // The characterIndex of one ShapeResult run is normalized to the run's
        // startIndex and length.  TODO crbug.com/542703: Consider changing that
        // and instead pass the whole run to hb_buffer_t each time.
        run->m_glyphData[i].characterIndex = glyphInfos[startGlyph + i].cluster - startCluster;

        run->setGlyphAndPositions(i, glyph, advance, offsetX, offsetY);
        totalAdvance += advance;
        hasVerticalOffsets |= (offsetY != 0);

        FloatRect glyphBounds = currentFontData->boundsForGlyph(glyph);
        glyphBounds.move(glyphOrigin.x(), glyphOrigin.y());
        result->m_glyphBoundingBox.unite(glyphBounds);
        glyphOrigin += FloatSize(advance + offsetX, offsetY);
    }
    run->m_width = std::max(0.0f, totalAdvance);
    result->m_width += run->m_width;
    result->m_numGlyphs += numGlyphs;
    ASSERT(result->m_numGlyphs >= numGlyphs); // no overflow
    result->m_hasVerticalOffsets |= hasVerticalOffsets;

    // The runs are stored in result->m_runs in visual order. For LTR, we place
    // the run to be inserted before the next run with a bigger character
    // start index. For RTL, we place the run before the next run with a lower
    // character index. Otherwise, for both directions, at the end.
    if (HB_DIRECTION_IS_FORWARD(run->m_direction)) {
        for (size_t pos = 0; pos < result->m_runs.size(); ++pos) {
            if (result->m_runs.at(pos)->m_startIndex > run->m_startIndex) {
                result->m_runs.insert(pos, run.release());
                break;
            }
        }
    } else {
        for (size_t pos = 0; pos < result->m_runs.size(); ++pos) {
            if (result->m_runs.at(pos)->m_startIndex < run->m_startIndex) {
                result->m_runs.insert(pos, run.release());
                break;
            }
        }
    }
    // If we didn't find an existing slot to place it, append.
    if (run) {
        result->m_runs.append(run.release());
    }
}
Пример #2
0
bool HarfBuzzShaper::extractShapeResults(hb_buffer_t* harfBuzzBuffer,
    ShapeResult* shapeResult,
    bool& fontCycleQueued, const HolesQueueItem& currentQueueItem,
    const SimpleFontData* currentFont,
    UScriptCode currentRunScript,
    bool isLastResort)
{
    enum ClusterResult {
        Shaped,
        NotDef,
        Unknown
    };
    ClusterResult currentClusterResult = Unknown;
    ClusterResult previousClusterResult = Unknown;
    unsigned previousCluster = 0;
    unsigned currentCluster = 0;

    // Find first notdef glyph in harfBuzzBuffer.
    unsigned numGlyphs = hb_buffer_get_length(harfBuzzBuffer);
    hb_glyph_info_t* glyphInfo = hb_buffer_get_glyph_infos(harfBuzzBuffer, 0);

    unsigned lastChangePosition = 0;

    if (!numGlyphs) {
        DLOG(ERROR) << "HarfBuzz returned empty glyph buffer after shaping.";
        return false;
    }

    for (unsigned glyphIndex = 0; glyphIndex <= numGlyphs; ++glyphIndex) {
        // Iterating by clusters, check for when the state switches from shaped
        // to non-shaped and vice versa. Taking into account the edge cases of
        // beginning of the run and end of the run.
        previousCluster = currentCluster;
        currentCluster = glyphInfo[glyphIndex].cluster;

        if (glyphIndex < numGlyphs) {
            // Still the same cluster, merge shaping status.
            if (previousCluster == currentCluster && glyphIndex != 0) {
                if (glyphInfo[glyphIndex].codepoint == 0) {
                    currentClusterResult = NotDef;
                } else {
                    // We can only call the current cluster fully shapped, if
                    // all characters that are part of it are shaped, so update
                    // currentClusterResult to Shaped only if the previous
                    // characters have been shaped, too.
                    currentClusterResult = currentClusterResult == Shaped ? Shaped : NotDef;
                }
                continue;
            }
            // We've moved to a new cluster.
            previousClusterResult = currentClusterResult;
            currentClusterResult = glyphInfo[glyphIndex].codepoint == 0 ? NotDef : Shaped;
        } else {
            // The code below operates on the "flanks"/changes between NotDef
            // and Shaped. In order to keep the code below from explictly
            // dealing with character indices and run end, we explicitly
            // terminate the cluster/run here by setting the result value to the
            // opposite of what it was, leading to atChange turning true.
            previousClusterResult = currentClusterResult;
            currentClusterResult = currentClusterResult == NotDef ? Shaped : NotDef;
        }

        bool atChange = (previousClusterResult != currentClusterResult) && previousClusterResult != Unknown;
        if (!atChange)
            continue;

        // Compute the range indices of consecutive shaped or .notdef glyphs.
        // Cluster information for RTL runs becomes reversed, e.g. character 0
        // has cluster index 5 in a run of 6 characters.
        unsigned numCharacters = 0;
        unsigned numGlyphsToInsert = 0;
        unsigned startIndex = 0;
        if (HB_DIRECTION_IS_FORWARD(hb_buffer_get_direction(harfBuzzBuffer))) {
            startIndex = glyphInfo[lastChangePosition].cluster;
            if (glyphIndex == numGlyphs) {
                numCharacters = currentQueueItem.m_startIndex + currentQueueItem.m_numCharacters - glyphInfo[lastChangePosition].cluster;
                numGlyphsToInsert = numGlyphs - lastChangePosition;
            } else {
                numCharacters = glyphInfo[glyphIndex].cluster - glyphInfo[lastChangePosition].cluster;
                numGlyphsToInsert = glyphIndex - lastChangePosition;
            }
        } else {
            // Direction Backwards
            startIndex = glyphInfo[glyphIndex - 1].cluster;
            if (lastChangePosition == 0) {
                numCharacters = currentQueueItem.m_startIndex + currentQueueItem.m_numCharacters - glyphInfo[glyphIndex - 1].cluster;
            } else {
                numCharacters = glyphInfo[lastChangePosition - 1].cluster - glyphInfo[glyphIndex - 1].cluster;
            }
            numGlyphsToInsert = glyphIndex - lastChangePosition;
        }

        if (currentClusterResult == Shaped && !isLastResort) {
            // Now it's clear that we need to continue processing.
            if (!fontCycleQueued) {
                appendToHolesQueue(HolesQueueNextFont, 0, 0);
                fontCycleQueued = true;
            }

            // Here we need to put character positions.
            ASSERT(numCharacters);
            appendToHolesQueue(HolesQueueRange, startIndex, numCharacters);
        }

        // If numCharacters is 0, that means we hit a NotDef before shaping the
        // whole grapheme. We do not append it here. For the next glyph we
        // encounter, atChange will be true, and the characters corresponding to
        // the grapheme will be added to the TODO queue again, attempting to
        // shape the whole grapheme with the next font.
        // When we're getting here with the last resort font, we have no other
        // choice than adding boxes to the ShapeResult.
        if ((currentClusterResult == NotDef && numCharacters) || isLastResort) {
            hb_direction_t direction = TextDirectionToHBDirection(
                m_textRun.direction(),
                m_font->getFontDescription().orientation(), currentFont);
            // Here we need to specify glyph positions.
            ShapeResult::RunInfo* run = new ShapeResult::RunInfo(currentFont,
                direction, ICUScriptToHBScript(currentRunScript),
                startIndex, numGlyphsToInsert, numCharacters);
            shapeResult->insertRun(adoptPtr(run), lastChangePosition,
                numGlyphsToInsert,
                harfBuzzBuffer);
        }
        lastChangePosition = glyphIndex;
    }
    return true;
}
Пример #3
0
void ShapeResult::insertRun(std::unique_ptr<ShapeResult::RunInfo> runToInsert,
                            unsigned startGlyph,
                            unsigned numGlyphs,
                            hb_buffer_t* harfBuzzBuffer) {
  ASSERT(numGlyphs > 0);
  std::unique_ptr<ShapeResult::RunInfo> run(std::move(runToInsert));
  ASSERT(numGlyphs == run->m_glyphData.size());

  const SimpleFontData* currentFontData = run->m_fontData.get();
  const hb_glyph_info_t* glyphInfos =
      hb_buffer_get_glyph_infos(harfBuzzBuffer, 0);
  const hb_glyph_position_t* glyphPositions =
      hb_buffer_get_glyph_positions(harfBuzzBuffer, 0);
  const unsigned startCluster =
      HB_DIRECTION_IS_FORWARD(hb_buffer_get_direction(harfBuzzBuffer))
          ? glyphInfos[startGlyph].cluster
          : glyphInfos[startGlyph + numGlyphs - 1].cluster;

  float totalAdvance = 0.0f;
  FloatPoint glyphOrigin;
  bool hasVerticalOffsets = !HB_DIRECTION_IS_HORIZONTAL(run->m_direction);

  // HarfBuzz returns result in visual order, no need to flip for RTL.
  for (unsigned i = 0; i < numGlyphs; ++i) {
    uint16_t glyph = glyphInfos[startGlyph + i].codepoint;
    hb_glyph_position_t pos = glyphPositions[startGlyph + i];

    float offsetX = harfBuzzPositionToFloat(pos.x_offset);
    float offsetY = -harfBuzzPositionToFloat(pos.y_offset);

    // One out of x_advance and y_advance is zero, depending on
    // whether the buffer direction is horizontal or vertical.
    // Convert to float and negate to avoid integer-overflow for ULONG_MAX.
    float advance;
    if (LIKELY(pos.x_advance))
      advance = harfBuzzPositionToFloat(pos.x_advance);
    else
      advance = -harfBuzzPositionToFloat(pos.y_advance);

    run->m_glyphData[i].characterIndex =
        glyphInfos[startGlyph + i].cluster - startCluster;

    run->setGlyphAndPositions(i, glyph, advance, offsetX, offsetY);
    totalAdvance += advance;
    hasVerticalOffsets |= (offsetY != 0);

    FloatRect glyphBounds = currentFontData->boundsForGlyph(glyph);
    glyphBounds.move(glyphOrigin.x(), glyphOrigin.y());
    m_glyphBoundingBox.unite(glyphBounds);
    glyphOrigin += FloatSize(advance + offsetX, offsetY);
  }

  run->m_width = std::max(0.0f, totalAdvance);
  m_width += run->m_width;
  m_numGlyphs += numGlyphs;
  ASSERT(m_numGlyphs >= numGlyphs);
  m_hasVerticalOffsets |= hasVerticalOffsets;

  // The runs are stored in result->m_runs in visual order. For LTR, we place
  // the run to be inserted before the next run with a bigger character
  // start index. For RTL, we place the run before the next run with a lower
  // character index. Otherwise, for both directions, at the end.
  if (HB_DIRECTION_IS_FORWARD(run->m_direction)) {
    for (size_t pos = 0; pos < m_runs.size(); ++pos) {
      if (m_runs.at(pos)->m_startIndex > run->m_startIndex) {
        m_runs.insert(pos, std::move(run));
        break;
      }
    }
  } else {
    for (size_t pos = 0; pos < m_runs.size(); ++pos) {
      if (m_runs.at(pos)->m_startIndex < run->m_startIndex) {
        m_runs.insert(pos, std::move(run));
        break;
      }
    }
  }
  // If we didn't find an existing slot to place it, append.
  if (run)
    m_runs.append(std::move(run));
}