zoukankan      html  css  js  c++  java
  • Chrome RenderText分析(2)

     

    Chrome RenderText分析(1)

    继续分析以下步骤

    image

     

    一.TextRun结构

    struct TextRun {
      TextRun();
      ~TextRun();
    
      ui::Range range;
      Font font;
      // A gfx::Font::FontStyle flag to specify bold and italic styles.
      // Supersedes |font.GetFontStyle()|. Stored separately to avoid calling
      // |font.DeriveFont()|, which is expensive on Windows.
      int font_style;
    
      // TODO(msw): Disambiguate color/style from TextRuns for proper glyph shaping.
      //            See an example: http://www.catch22.net/tuts/uniscribe-mysteries
      SkColor foreground;
      bool strike;
      bool diagonal_strike;
      bool underline;
    
      int width;
      // The cumulative widths of preceding runs.
      int preceding_run_widths;
    
      SCRIPT_ANALYSIS script_analysis;
    
      scoped_ptr<WORD[]> glyphs;
      scoped_ptr<WORD[]> logical_clusters;
      scoped_ptr<SCRIPT_VISATTR[]> visible_attributes;
      int glyph_count;
    
      scoped_ptr<int[]> advance_widths;
      scoped_ptr<GOFFSET[]> offsets;
      ABC abc_widths;
      SCRIPT_CACHE script_cache;
    
     private:
      DISALLOW_COPY_AND_ASSIGN(TextRun);
    };
    

    TextRun可以理解为一个输出结果

    1. ScriptItemize输出script_analysis
    2. ScriptShape输出glyphs,logical_clusters,visible_attributes,glyph_count
    3. ScriptPlace输出advance_widths,offsets,abc_widths(真正想要的宽度结果)

    二.ScriptShape

    1. 根据文字的长度初始化相关的缓冲区
    2. 选择文字字体
    3. 调用ScriptShape来填充TextRun
    4. 如果调用ScriptShape失败的话则使用SCRIPT_FONTPROPERTIES的默认值来填充
    void RenderTextWin::LayoutTextRun(internal::TextRun* run) {
      const size_t run_length = run->range.length();
      const wchar_t* run_text = &(GetLayoutText()[run->range.start()]);
      Font original_font = run->font;
      LinkedFontsIterator fonts(original_font);
      bool tried_cached_font = false;
      bool tried_fallback = false;
      // Keep track of the font that is able to display the greatest number of
      // characters for which ScriptShape() returned S_OK. This font will be used
      // in the case where no font is able to display the entire run.
      int best_partial_font_missing_char_count = INT_MAX;
      Font best_partial_font = original_font;
      bool using_best_partial_font = false;
      Font current_font;
    
      run->logical_clusters.reset(new WORD[run_length]);
      while (fonts.NextFont(&current_font)) {
        HRESULT hr = ShapeTextRunWithFont(run, current_font);
    
        bool glyphs_missing = false;
        if (hr == USP_E_SCRIPT_NOT_IN_FONT) {
          glyphs_missing = true;
        } else if (hr == S_OK) {
          // If |hr| is S_OK, there could still be missing glyphs in the output.
          // http://msdn.microsoft.com/en-us/library/windows/desktop/dd368564.aspx
          const int missing_count = CountCharsWithMissingGlyphs(run);
          // Track the font that produced the least missing glyphs.
          if (missing_count < best_partial_font_missing_char_count) {
            best_partial_font_missing_char_count = missing_count;
            best_partial_font = run->font;
          }
          glyphs_missing = (missing_count != 0);
        } else {
          NOTREACHED() << hr;
        }
    
        // Use the font if it had glyphs for all characters.
        if (!glyphs_missing) {
          // Save the successful fallback font that was chosen.
          if (tried_fallback)
            successful_substitute_fonts_[original_font.GetFontName()] = run->font;
          return;
        }
    
        // First, try the cached font from previous runs, if any.
        if (!tried_cached_font) {
          tried_cached_font = true;
    
          std::map<std::string, Font>::const_iterator it =
              successful_substitute_fonts_.find(original_font.GetFontName());
          if (it != successful_substitute_fonts_.end()) {
            fonts.SetNextFont(it->second);
            continue;
          }
        }
    
        // If there are missing glyphs, first try finding a fallback font using a
        // meta file, if it hasn't yet been attempted for this run.
        // TODO(msw|asvitkine): Support RenderText's font_list()?
        if (!tried_fallback) {
          tried_fallback = true;
    
          Font fallback_font;
          if (ChooseFallbackFont(cached_hdc_, run->font, run_text, run_length,
                                 &fallback_font)) {
            fonts.SetNextFont(fallback_font);
            continue;
          }
        }
      }
    
      // If a font was able to partially display the run, use that now.
      if (best_partial_font_missing_char_count < static_cast<int>(run_length)) {
        // Re-shape the run only if |best_partial_font| differs from the last font.
        if (best_partial_font.GetNativeFont() != run->font.GetNativeFont())
          ShapeTextRunWithFont(run, best_partial_font);
        return;
      }
    
      // If no font was able to partially display the run, replace all glyphs
      // with |wgDefault| from the original font to ensure to they don't hold
      // garbage values.
      // First, clear the cache and select the original font on the HDC.
      ScriptFreeCache(&run->script_cache);
      run->font = original_font;
      SelectObject(cached_hdc_, run->font.GetNativeFont());
    
      // Now, get the font's properties.
      SCRIPT_FONTPROPERTIES properties;
      memset(&properties, 0, sizeof(properties));
      properties.cBytes = sizeof(properties);
      HRESULT hr = ScriptGetFontProperties(cached_hdc_, &run->script_cache,
                                           &properties);
      if (hr == S_OK) {
        // Finally, initialize |glyph_count|, |glyphs| and |visible_attributes| on
        // the run (since they may not have been set yet).
        run->glyph_count = run_length;
        memset(run->visible_attributes.get(), 0,
               run->glyph_count * sizeof(SCRIPT_VISATTR));
        for (int i = 0; i < run->glyph_count; ++i) {
          run->glyphs[i] = IsWhitespace(run_text[i]) ? properties.wgBlank :
                                                       properties.wgDefault;
        }
      }
    
      // TODO(msw): Don't use SCRIPT_UNDEFINED. Apparently Uniscribe can
      //            crash on certain surrogate pairs with SCRIPT_UNDEFINED.
      //            See https://bugzilla.mozilla.org/show_bug.cgi?id=341500
      //            And http://maxradi.us/documents/uniscribe/
      run->script_analysis.eScript = SCRIPT_UNDEFINED;
    }
    
    HRESULT RenderTextWin::ShapeTextRunWithFont(internal::TextRun* run,
                                                const Font& font) {
      // Update the run's font only if necessary. If the two fonts wrap the same
      // PlatformFontWin object, their native fonts will have the same value.
      if (run->font.GetNativeFont() != font.GetNativeFont()) {
        const int font_size = run->font.GetFontSize();
        const int font_height = run->font.GetHeight();
        run->font = font;
        DeriveFontIfNecessary(font_size, font_height, run->font_style, &run->font);
        ScriptFreeCache(&run->script_cache);
      }
    
      // Select the font desired for glyph generation.
      SelectObject(cached_hdc_, run->font.GetNativeFont());
    
      HRESULT hr = E_OUTOFMEMORY;
      const size_t run_length = run->range.length();
      const wchar_t* run_text = &(GetLayoutText()[run->range.start()]);
      // Max glyph guess: http://msdn.microsoft.com/en-us/library/dd368564.aspx
      size_t max_glyphs = static_cast<size_t>(1.5 * run_length + 16);
      while (hr == E_OUTOFMEMORY && max_glyphs < kMaxGlyphs) {
        run->glyph_count = 0;
        run->glyphs.reset(new WORD[max_glyphs]);
        run->visible_attributes.reset(new SCRIPT_VISATTR[max_glyphs]);
        hr = ScriptShape(cached_hdc_,
                         &run->script_cache,
                         run_text,
                         run_length,
                         max_glyphs,
                         &run->script_analysis,
                         run->glyphs.get(),
                         run->logical_clusters.get(),
                         run->visible_attributes.get(),
                         &run->glyph_count);
        max_glyphs *= 2;
      }
      return hr;
    }
    

    三.ScriptPlace

    run->abc_widths是计算的结果

    void RenderTextWin::LayoutVisualText() {
      DCHECK(!runs_.empty());
    
      if (!cached_hdc_)
        cached_hdc_ = CreateCompatibleDC(NULL);
    
      HRESULT hr = E_FAIL;
      string_size_.set_height(0);
      for (size_t i = 0; i < runs_.size(); ++i) {
        internal::TextRun* run = runs_[i];
        LayoutTextRun(run);
    
        string_size_.set_height(std::max(string_size_.height(),
                                         run->font.GetHeight()));
        common_baseline_ = std::max(common_baseline_, run->font.GetBaseline());
    
        if (run->glyph_count > 0) {
          run->advance_widths.reset(new int[run->glyph_count]);
          run->offsets.reset(new GOFFSET[run->glyph_count]);
          hr = ScriptPlace(cached_hdc_,
                           &run->script_cache,
                           run->glyphs.get(),
                           run->glyph_count,
                           run->visible_attributes.get(),
                           &(run->script_analysis),
                           run->advance_widths.get(),
                           run->offsets.get(),
                           &(run->abc_widths));
          DCHECK(SUCCEEDED(hr));
        }
      }
    
      // Build the array of bidirectional embedding levels.
      scoped_ptr<BYTE[]> levels(new BYTE[runs_.size()]);
      for (size_t i = 0; i < runs_.size(); ++i)
        levels[i] = runs_[i]->script_analysis.s.uBidiLevel;
    
      // Get the maps between visual and logical run indices.
      visual_to_logical_.reset(new int[runs_.size()]);
      logical_to_visual_.reset(new int[runs_.size()]);
      hr = ScriptLayout(runs_.size(),
                        levels.get(),
                        visual_to_logical_.get(),
                        logical_to_visual_.get());
      DCHECK(SUCCEEDED(hr));
    
      // Precalculate run width information.
      size_t preceding_run_widths = 0;
      for (size_t i = 0; i < runs_.size(); ++i) {
        internal::TextRun* run = runs_[visual_to_logical_[i]];
        run->preceding_run_widths = preceding_run_widths;
        const ABC& abc = run->abc_widths;
        run->width = abc.abcA + abc.abcB + abc.abcC;
        preceding_run_widths += run->width;
      }
      string_size_.set_width(preceding_run_widths);
    }
    
  • 相关阅读:
    [对android程序作代码混淆]
    用smali实现Android apk的简单汉化
    防止java反编译的一些常用方法
    http抓包工具推荐WSockExpert/httpwatch/HttpAnalyzer/DebugBar
    JSP动态网站环境搭建应用中的详细步骤(Tomcat和Apache/IIS的整合)
    22、Secondary Tiles
    21、磁贴和磁贴通知(tile)(下)
    19、Context Menu
    代码管理工具
    20、磁贴和磁贴通知(tile)(上)
  • 原文地址:https://www.cnblogs.com/Clingingboy/p/3459219.html
Copyright © 2011-2022 走看看