Ejemplo n.º 1
0
 private static String glyphMetricsString(TextPaint paint) {
   int metricsInt = paint.getFontMetricsInt(paint.getFontMetricsInt());
   float textSize = paint.getTextSize();
   float textScale = paint.getTextScaleX();
   float textSkew = paint.getTextSkewX();
   Typeface typeface = paint.getTypeface();
   return String.format("%i%.2f%.2f%.2f", metricsInt, textSize, textScale, textSkew);
 }
Ejemplo n.º 2
0
 float addStyleRun(TextPaint paint, int len, FontMetricsInt fm) {
   if (fm != null) {
     paint.getFontMetricsInt(fm);
   }
   int p = this.mPos;
   this.mPos = p + len;
   if (this.mEasy) {
     return paint.getTextRunAdvances(this.mChars, p, len, p, len, this.mDir != 1, this.mWidths, p);
   }
   float totalAdvance = 0.0f;
   int level = this.mLevels[p];
   int q = p;
   int i = p + 1;
   int e = p + len;
   while (true) {
     if (i == e || this.mLevels[i] != level) {
       totalAdvance +=
           paint.getTextRunAdvances(
               this.mChars, q, i - q, q, i - q, (level & 1) != 0, this.mWidths, q);
       if (i == e) {
         return totalAdvance;
       }
       q = i;
       level = this.mLevels[i];
     }
     i++;
   }
 }
Ejemplo n.º 3
0
  public ContactChipSpan(
      CharSequence name,
      int height,
      int maxWidth,
      int paddingLeft,
      int paddingRight,
      Typeface typeface,
      int textColor,
      int textSize,
      int backgroundColor) {
    mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    mPaint.setStyle(Paint.Style.FILL);
    mPaint.setColor(textColor);
    mPaint.setTypeface(typeface);
    mPaint.setTextSize(textSize);

    mTextPaint = new TextPaint(mPaint);

    mRect = new RectF();

    mMatrix = new Matrix();

    mContactName = name;
    mPaddingLeft = paddingLeft;
    mPaddingRight = paddingRight;
    mBackgroundColor = backgroundColor;
    mHeight = height;
    mWidth =
        Math.round(
            Math.min(
                maxWidth,
                mPaint.measureText(name, 0, name.length()) + paddingLeft + paddingRight + height));

    int outerWidth = Math.max(0, mWidth - mPaddingLeft - mPaddingRight - mHeight);
    Paint.FontMetricsInt temp = mTextPaint.getFontMetricsInt();
    BoringLayout.Metrics mMetrics = new BoringLayout.Metrics();
    mMetrics.width =
        (int) Math.ceil(mTextPaint.measureText(mContactName, 0, mContactName.length()));
    mMetrics.ascent = temp.ascent;
    mMetrics.bottom = temp.bottom;
    mMetrics.descent = temp.descent;
    mMetrics.top = temp.top;
    mMetrics.leading = temp.leading;
    mBoringLayout =
        BoringLayout.make(
            mContactName,
            mTextPaint,
            outerWidth,
            Layout.Alignment.ALIGN_NORMAL,
            1f,
            1f,
            mMetrics,
            true,
            TextUtils.TruncateAt.END,
            outerWidth);
  }
Ejemplo n.º 4
0
  /// M: new FontMetrics method for complex text support. @{
  private static void expandMetricsFromPaint(FontMetricsInt fmi, TextPaint wp, CharSequence text) {
    final int previousTop = fmi.top;
    final int previousAscent = fmi.ascent;
    final int previousDescent = fmi.descent;
    final int previousBottom = fmi.bottom;
    final int previousLeading = fmi.leading;

    wp.getFontMetricsInt(text, fmi);

    updateMetrics(
        fmi, previousTop, previousAscent, previousDescent, previousBottom, previousLeading);
  }
  /**
   * Returns the advance widths for a uniform left-to-right run of text with no style changes in the
   * middle of the run. If any style is replacement text, the first character will isCancelled the
   * width of the replacement and the remaining characters will isCancelled a width of 0.
   *
   * @param paint the paint, will not be modified
   * @param workPaint a paint to modify; on return will reflect the original paint plus the effect
   *     of all spans on the run
   * @param text the text
   * @param start the start of the run
   * @param end the limit of the run
   * @param widths array to receive the advance widths of the characters. Must be at least a large
   *     as (end - start).
   * @param fmi FontMetrics information; can be null
   * @return the actual number of widths returned
   */
  public static int getTextWidths(
      TextPaint paint,
      TextPaint workPaint,
      Spanned text,
      int start,
      int end,
      float[] widths,
      Paint.FontMetricsInt fmi) {
    MetricAffectingSpan[] spans = text.getSpans(start, end, MetricAffectingSpan.class);

    ReplacementSpan replacement = null;
    workPaint.set(paint);

    for (MetricAffectingSpan span : spans) {
      if (span instanceof ReplacementSpan) {
        replacement = (ReplacementSpan) span;
      } else {
        span.updateMeasureState(workPaint);
      }
    }

    if (replacement == null) {
      workPaint.getFontMetricsInt(fmi);
      workPaint.getTextWidths(text, start, end, widths);
    } else {
      int wid = replacement.getSize(workPaint, text, start, end, fmi);

      if (end > start) {
        widths[0] = wid;
        for (int i = start + 1; i < end; i++) {
          widths[i - start] = 0;
        }
      }
    }
    return end - start;
  }
Ejemplo n.º 6
0
  public SmileProcessor(Application application) {
    long start = System.currentTimeMillis();
    this.application = application;
    processor = this;
    density = application.getResources().getDisplayMetrics().density;

    emojiSideSize = (int) (density * 20);

    Logger.d(TAG, "Emoji phase 0 in " + (System.currentTimeMillis() - start) + " ms");

    if (density >= 2 || density == 1) {
      if (density >= 2) {
        // XHDPI and more
        if (SmileysPack.PACK_2) {
          layoutType = LAYOUT_2X_1;
        } else if (SmileysPack.PACK_15) {
          layoutType = LAYOUT_15X_1;
        } else if (SmileysPack.PACK_1) {
          layoutType = LAYOUT_1X;
        } else {
          throw new RuntimeException("Unable to find smileys pack");
        }
      } else {
        // MDPI
        if (SmileysPack.PACK_1) {
          layoutType = LAYOUT_1X;
        } else if (SmileysPack.PACK_15) {
          layoutType = LAYOUT_15X_1;
        } else if (SmileysPack.PACK_2) {
          layoutType = LAYOUT_2X_2;
        } else {
          throw new RuntimeException("Unable to find smileys pack");
        }
      }
    } else {
      if (density > 1) { // 1.3333 and 1.5
        // HDPI & TVDPI
        if (SmileysPack.PACK_15) {
          layoutType = LAYOUT_15X_1;
        } else if (SmileysPack.PACK_2) {
          layoutType = LAYOUT_2X_1;
        } else if (SmileysPack.PACK_1) {
          layoutType = LAYOUT_1X;
        } else {
          throw new RuntimeException("Unable to find smileys pack");
        }
      } else { // 0.75
        // LDPI
        if (SmileysPack.PACK_15) {
          layoutType = LAYOUT_15X_2;
        } else if (SmileysPack.PACK_1) {
          layoutType = LAYOUT_1X;
        } else if (SmileysPack.PACK_2) {
          layoutType = LAYOUT_2X_2;
        } else {
          throw new RuntimeException("Unable to find smileys pack");
        }
      }
    }

    Logger.d(TAG, "Emoji phase 1 in " + (System.currentTimeMillis() - start) + " ms");
    start = System.currentTimeMillis();

    switch (layoutType) {
      default:
      case LAYOUT_1X:
        rectSize = 28;
        break;
      case LAYOUT_15X_1:
        rectSize = 36;
        break;
      case LAYOUT_15X_2:
        rectSize = 18;
        break;
      case LAYOUT_2X_1:
        rectSize = 56;
        break;
      case LAYOUT_2X_2:
        rectSize = 28;
        break;
    }

    indexes = new HashMap<Long, Integer>();
    emojiMap = new HashMap<Integer, Bitmap>();
    originalMetrics = new HashMap<Integer, Paint.FontMetricsInt>();

    TextPaint bodyPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG | Paint.SUBPIXEL_TEXT_FLAG);
    // bodyPaint.setTypeface(FontController.loadTypeface(application, "normal"));
    bodyPaint.setTextSize(getSp(16));
    bodyPaint.setColor(0xff000000);
    originalMetrics.put(CONFIGURATION_BUBBLES, bodyPaint.getFontMetricsInt());

    bodyPaint = new TextPaint(TextPaint.ANTI_ALIAS_FLAG | Paint.SUBPIXEL_TEXT_FLAG);
    // bodyPaint.setTypeface(FontController.loadTypeface(application, "light"));
    bodyPaint.setColor(0xff808080);
    bodyPaint.setTextSize(getSp(15.5f));
    originalMetrics.put(CONFIGURATION_DIALOGS, bodyPaint.getFontMetricsInt());

    Logger.d(TAG, "Emoji phase 2 in " + (System.currentTimeMillis() - start) + " ms");
    start = System.currentTimeMillis();

    for (int i = 0; i < EMOJI_MAP.length; i++) {
      indexes.put(EMOJI_MAP[i], i);
    }

    Logger.d(TAG, "Emoji phase 3 in " + (System.currentTimeMillis() - start) + " ms");
  }
  /**
   * Make a layout for the transformed text (password transformation being the primary example of a
   * transformation) that will be updated as the base text is changed. If ellipsize is non-null, the
   * Layout will ellipsize the text down to ellipsizedWidth. * *@hide
   */
  public DynamicLayout(
      CharSequence base,
      CharSequence display,
      TextPaint paint,
      int width,
      Alignment align,
      TextDirectionHeuristic textDir,
      float spacingmult,
      float spacingadd,
      boolean includepad,
      TextUtils.TruncateAt ellipsize,
      int ellipsizedWidth) {
    super(
        (ellipsize == null)
            ? display
            : (display instanceof Spanned)
                ? new SpannedEllipsizer(display)
                : new Ellipsizer(display),
        paint,
        width,
        align,
        textDir,
        spacingmult,
        spacingadd);

    mBase = base;
    mDisplay = display;

    if (ellipsize != null) {
      mInts = new PackedIntVector(COLUMNS_ELLIPSIZE);
      mEllipsizedWidth = ellipsizedWidth;
      mEllipsizeAt = ellipsize;
    } else {
      mInts = new PackedIntVector(COLUMNS_NORMAL);
      mEllipsizedWidth = width;
      mEllipsizeAt = null;
    }

    mObjects = new PackedObjectVector<Directions>(1);

    mIncludePad = includepad;

    /*
     * This is annoying, but we can't refer to the layout until
     * superclass construction is finished, and the superclass
     * constructor wants the reference to the display text.
     *
     * This will break if the superclass constructor ever actually
     * cares about the content instead of just holding the reference.
     */
    if (ellipsize != null) {
      Ellipsizer e = (Ellipsizer) getText();

      e.mLayout = this;
      e.mWidth = ellipsizedWidth;
      e.mMethod = ellipsize;
      mEllipsize = true;
    }

    // Initial state is a single line with 0 characters (0 to 0),
    // with top at 0 and bottom at whatever is natural, and
    // undefined ellipsis.

    int[] start;

    if (ellipsize != null) {
      start = new int[COLUMNS_ELLIPSIZE];
      start[ELLIPSIS_START] = ELLIPSIS_UNDEFINED;
    } else {
      start = new int[COLUMNS_NORMAL];
    }

    Directions[] dirs = new Directions[] {DIRS_ALL_LEFT_TO_RIGHT};

    Paint.FontMetricsInt fm = paint.getFontMetricsInt();
    int asc = fm.ascent;
    int desc = fm.descent;

    start[DIR] = DIR_LEFT_TO_RIGHT << DIR_SHIFT;
    start[TOP] = 0;
    start[DESCENT] = desc;
    mInts.insertAt(0, start);

    start[TOP] = desc - asc;
    mInts.insertAt(1, start);

    mObjects.insertAt(0, dirs);

    // Update from 0 characters to whatever the real text is

    reflow(base, 0, 0, base.length());

    if (base instanceof Spannable) {
      if (mWatcher == null) mWatcher = new ChangeWatcher(this);

      // Strip out any watchers for other DynamicLayouts.
      Spannable sp = (Spannable) base;
      ChangeWatcher[] spans = sp.getSpans(0, sp.length(), ChangeWatcher.class);
      for (int i = 0; i < spans.length; i++) sp.removeSpan(spans[i]);

      sp.setSpan(
          mWatcher,
          0,
          base.length(),
          Spannable.SPAN_INCLUSIVE_INCLUSIVE | (PRIORITY << Spannable.SPAN_PRIORITY_SHIFT));
    }
  }
  /**
   * Draws and/or measures a uniform run of text on a single line. No span of interest should start
   * or end in the middle of this run (if not drawing, character spans that don't affect metrics can
   * be ignored). Neither should the run direction change in the middle of the run.
   *
   * <p>
   *
   * <p>The x position is the leading edge of the text. In a right-to-left paragraph, this will be
   * to the right of the text to be drawn. Paint should not have an Align value other than LEFT or
   * positioning will isCancelled confused.
   *
   * <p>
   *
   * <p>On return, workPaint will reflect the original paint plus any modifications made by
   * character styles on the run.
   *
   * <p>
   *
   * <p>The returned width is signed and will be < 0 if the paragraph direction is right-to-left.
   */
  private static float drawUniformRun(
      Canvas canvas,
      Spanned text,
      int start,
      int end,
      int dir,
      boolean runIsRtl,
      float x,
      int top,
      int y,
      int bottom,
      Paint.FontMetricsInt fmi,
      TextPaint paint,
      TextPaint workPaint,
      boolean needWidth) {

    boolean haveWidth = false;
    float ret = 0;
    CharacterStyle[] spans = text.getSpans(start, end, CharacterStyle.class);

    ReplacementSpan replacement = null;

    // XXX: This shouldn't be modifying paint, only workPaint.
    // However, the members belonging to TextPaint should have default
    // values anyway.  Better to ensure this in the Layout constructor.
    paint.bgColor = 0;
    paint.baselineShift = 0;
    workPaint.set(paint);

    if (spans.length > 0) {
      for (CharacterStyle span : spans) {
        if (span instanceof ReplacementSpan) {
          replacement = (ReplacementSpan) span;
        } else {
          span.updateDrawState(workPaint);
        }
      }
    }

    if (replacement == null) {
      CharSequence tmp;
      int tmpstart, tmpend;

      if (runIsRtl) {
        tmp = TextUtils.getReverse(text, start, end);
        tmpstart = 0;
        // XXX: assumes getReverse doesn't change the length of the text
        tmpend = end - start;
      } else {
        tmp = text;
        tmpstart = start;
        tmpend = end;
      }

      if (fmi != null) {
        workPaint.getFontMetricsInt(fmi);
      }

      if (canvas != null) {
        if (workPaint.bgColor != 0) {
          int c = workPaint.getColor();
          Paint.Style s = workPaint.getStyle();
          workPaint.setColor(workPaint.bgColor);
          workPaint.setStyle(Paint.Style.FILL);

          if (!haveWidth) {
            ret = workPaint.measureText(tmp, tmpstart, tmpend);
            haveWidth = true;
          }

          if (dir == Layout.DIR_RIGHT_TO_LEFT) {
            canvas.drawRect(x - ret, top, x, bottom, workPaint);
          } else {
            canvas.drawRect(x, top, x + ret, bottom, workPaint);
          }

          workPaint.setStyle(s);
          workPaint.setColor(c);
        }

        if (dir == Layout.DIR_RIGHT_TO_LEFT) {
          if (!haveWidth) {
            ret = workPaint.measureText(tmp, tmpstart, tmpend);
            haveWidth = true;
          }

          canvas.drawText(tmp, tmpstart, tmpend, x - ret, y + workPaint.baselineShift, workPaint);
        } else {
          if (needWidth) {
            if (!haveWidth) {
              ret = workPaint.measureText(tmp, tmpstart, tmpend);
              haveWidth = true;
            }
          }

          canvas.drawText(tmp, tmpstart, tmpend, x, y + workPaint.baselineShift, workPaint);
        }
      } else {
        if (needWidth && !haveWidth) {
          ret = workPaint.measureText(tmp, tmpstart, tmpend);
          haveWidth = true;
        }
      }
    } else {
      ret = replacement.getSize(workPaint, text, start, end, fmi);

      if (canvas != null) {
        if (dir == Layout.DIR_RIGHT_TO_LEFT) {
          replacement.draw(canvas, text, start, end, x - ret, top, y, bottom, workPaint);
        } else {
          replacement.draw(canvas, text, start, end, x, top, y, bottom, workPaint);
        }
      }
    }

    if (dir == Layout.DIR_RIGHT_TO_LEFT) {
      return -ret;
    } else {
      return ret;
    }
  }
  /**
   * Renders and/or measures a directional run of text on a single line. Unlike {@link
   * #drawUniformRun}, this can render runs that cross style boundaries. Returns the signed advance
   * width, if requested.
   *
   * <p>
   *
   * <p>The x position is the leading edge of the text. In a right-to-left paragraph, this will be
   * to the right of the text to be drawn. Paint should not have an Align value other than LEFT or
   * positioning will isCancelled confused.
   *
   * <p>
   *
   * <p>This optimizes for unstyled text and so workPaint might not be modified by this call.
   *
   * <p>
   *
   * <p>The returned advance width will be < 0 if the paragraph direction is right-to-left.
   */
  private static float drawDirectionalRun(
      Canvas canvas,
      CharSequence text,
      int start,
      int end,
      int dir,
      boolean runIsRtl,
      float x,
      int top,
      int y,
      int bottom,
      Paint.FontMetricsInt fmi,
      TextPaint paint,
      TextPaint workPaint,
      boolean needWidth) {

    // XXX: It looks like all calls to this API match dir and runIsRtl, so
    // having both parameters is redundant and confusing.

    // fast path for unstyled text
    if (!(text instanceof Spanned)) {
      float ret = 0;

      if (runIsRtl) {
        CharSequence tmp = TextUtils.getReverse(text, start, end);
        // XXX: this assumes getReverse doesn't tweak the length of
        // the text
        int tmpend = end - start;

        if (canvas != null || needWidth) {
          ret = paint.measureText(tmp, 0, tmpend);
        }

        if (canvas != null) {
          canvas.drawText(tmp, 0, tmpend, x - ret, y, paint);
        }
      } else {
        if (needWidth) {
          ret = paint.measureText(text, start, end);
        }

        if (canvas != null) {
          canvas.drawText(text, start, end, x, y, paint);
        }
      }

      if (fmi != null) {
        paint.getFontMetricsInt(fmi);
      }

      return ret * dir; // Layout.DIR_RIGHT_TO_LEFT == -1
    }

    float ox = x;
    int minAscent = 0, maxDescent = 0, minTop = 0, maxBottom = 0;

    Spanned sp = (Spanned) text;
    Class<?> division;

    if (canvas == null) {
      division = MetricAffectingSpan.class;
    } else {
      division = CharacterStyle.class;
    }

    int next;
    for (int i = start; i < end; i = next) {
      next = sp.nextSpanTransition(i, end, division);

      // XXX: if dir and runIsRtl were not the same, this would onDraw
      // spans in the wrong order, but no one appears to call it this
      // way.
      x +=
          drawUniformRun(
              canvas,
              sp,
              i,
              next,
              dir,
              runIsRtl,
              x,
              top,
              y,
              bottom,
              fmi,
              paint,
              workPaint,
              needWidth || next != end);

      if (fmi != null) {
        if (fmi.ascent < minAscent) {
          minAscent = fmi.ascent;
        }
        if (fmi.descent > maxDescent) {
          maxDescent = fmi.descent;
        }

        if (fmi.top < minTop) {
          minTop = fmi.top;
        }
        if (fmi.bottom > maxBottom) {
          maxBottom = fmi.bottom;
        }
      }
    }

    if (fmi != null) {
      if (start == end) {
        paint.getFontMetricsInt(fmi);
      } else {
        fmi.ascent = minAscent;
        fmi.descent = maxDescent;
        fmi.top = minTop;
        fmi.bottom = maxBottom;
      }
    }

    return x - ox;
  }