Android Paint:.measureText() 与 .getTextBounds() [英] Android Paint: .measureText() vs .getTextBounds()

查看:42
本文介绍了Android Paint:.measureText() 与 .getTextBounds()的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用 Paint.getTextBounds() 测量文本,因为我对获取要呈现的文本的高度和宽度感兴趣.然而,实际呈现的文本总是比getTextBounds()填充的Rect信息的.width()宽一点.>

令我惊讶的是,我测试了 .measureText(),发现它返回一个不同的(更高的)值.我试了一下,发现它是正确的.

为什么他们报告不同的宽度?如何正确获取高度和宽度?我的意思是,我可以使用 .measureText(),但是我不知道我是否应该相信由返回的 .height()getTextBounds().

根据要求,这里是重现问题的最少代码:

final String someText = "你好.我相信我是一些文本!";油漆 p = 新油漆();矩形边界 = new Rect();for (float f = 10; f <40; f += 1f) {p.setTextSize(f);p.getTextBounds(someText, 0, someText.length(), bounds);Log.d("测试", String.format("大小 %f,measureText %f,getTextBounds %d",F,p.measureText(someText),bounds.width()));}

输出显示差异不仅大于 1(并且没有最后一分钟的舍入错误),而且似乎随着大小的增加而增加(我正要得出更多结论,但它可能完全取决于字体):

D/Test (607):大小 10.000000,measureText 135.000000,getTextBounds 134D/测试(607):大小 11.000000,measureText 149.000000,getTextBounds 148D/测试(607):大小 12.000000,measureText 156.000000,getTextBounds 155D/测试(607):大小 13.000000,measureText 171.000000,getTextBounds 169D/测试(607):大小 14.000000,measureText 195.000000,getTextBounds 193D/测试(607):大小 15.000000,measureText 201.000000,getTextBounds 199D/测试(607):大小 16.000000,measureText 211.000000,getTextBounds 210D/测试(607):大小 17.000000,measureText 225.000000,getTextBounds 223D/测试(607):大小 18.000000,measureText 245.000000,getTextBounds 243D/测试(607):大小 19.000000,measureText 251.000000,getTextBounds 249D/测试(607):大小 20.000000,measureText 269.000000,getTextBounds 267D/测试(607):大小 21.000000,measureText 275.000000,getTextBounds 272D/测试(607):大小 22.000000,measureText 297.000000,getTextBounds 294D/测试(607):大小 23.000000,measureText 305.000000,getTextBounds 302D/测试(607):大小 24.000000,measureText 319.000000,getTextBounds 316D/测试(607):大小 25.000000,measureText 330.000000,getTextBounds 326D/测试(607):大小 26.000000,measureText 349.000000,getTextBounds 346D/测试(607):大小 27.000000,measureText 357.000000,getTextBounds 354D/测试(607):大小 28.000000,measureText 369.000000,getTextBounds 365D/测试(607):大小 29.000000,measureText 396.000000,getTextBounds 392D/测试(607):大小 30.000000,measureText 401.000000,getTextBounds 397D/测试(607):大小 31.000000,measureText 418.000000,getTextBounds 414D/测试(607):大小 32.000000,measureText 423.000000,getTextBounds 418D/测试(607):大小 33.000000,measureText 446.000000,getTextBounds 441D/测试(607):大小 34.000000,measureText 455.000000,getTextBounds 450D/测试(607):大小 35.000000,measureText 468.000000,getTextBounds 463D/测试(607):大小 36.000000,measureText 474.000000,getTextBounds 469D/测试(607):大小 37.000000,measureText 500.000000,getTextBounds 495D/测试(607):大小 38.000000,measureText 506.000000,getTextBounds 501D/测试(607):大小 39.000000,measureText 521.000000,getTextBounds 515

解决方案

您可以按照我所做的来检查此类问题:

研究Android源码,Paint.java源码,同时查看measureText和getTextBounds方法.您将了解到 measureText 调用 native_measureText,而 getTextBounds 调用 nativeGetStringBounds,它们是在 C++ 中实现的本机方法.

所以你会继续研究 Paint.cpp,它实现了两者.

native_measureText -> SkPaintGlue::measureText_CII

nativeGetStringBounds -> SkPaintGlue::getStringBounds

现在您的研究检查这些方法的不同之处.经过一些参数检查,两者都调用了 Skia Lib(Android 的一部分)中的函数 SkPaint::measureText,但它们都调用了不同的重载形式.

深入了解 Skia,我发现两个调用都在同一函数中产生相同的计算,只是返回结果不同.

回答您的问题:您的两个调用都进行相同的计算.结果的可能差异实际上在于 getTextBounds 以整数形式返回边界,而 measureText 返回浮点值.

因此,在将 float 转换为 int 期间,您得到的是舍入错误,这发生在调用函数 SkRect::roundOut 时 SkPaintGlue::doTextBounds 中的 Paint.cpp 中.

这两次调用的计算宽度之间的差异最大可能为 1.

编辑 2011 年 10 月 4 日

有什么比可视化更好的了.我付出了努力,为了自己的探索,为了应得的赏金:)

这是字体大小 60,红色是 bounds 矩形,紫色是 measureText 的结果.

可以看到bounds left 部分从左边开始一些像素,并且measureText 的值在左右两边都增加了这个值.这就是所谓的 Glyph 的 AdvanceX 值.(我在 SkPaint.cpp 的 Skia 资源中发现了这一点)

所以测试的结果是 measureText 为两侧的文本添加一些预先值,而 getTextBounds 计算给定文本适合的最小边界.

希望这个结果对你有用.

测试代码:

 protected void onDraw(Canvas canvas){final String s = "你好.我是一些文字!";油漆 p = 新油漆();矩形边界 = new Rect();p.setTextSize(60);p.getTextBounds(s, 0, s.length(), bounds);float mt = p.measureText(s);int bw = bounds.width();Log.i("LCG", String.format("measureText %f, getTextBounds %d (%s)",公吨,体重,bounds.toShortString()));bounds.offset(0, -bounds.top);p.setStyle(Style.STROKE);canvas.drawColor(0xff000080);p.setColor(0xffff0000);canvas.drawRect(bounds, p);p.setColor(0xff00ff00);canvas.drawText(s, 0, bounds.bottom, p);}

I'm measuring text using Paint.getTextBounds(), since I'm interested in getting both the height and width of the text to be rendered. However, the actual text rendered is always a bit wider than the .width() of the Rect information filled by getTextBounds().

To my surprise, I tested .measureText(), and found that it returns a different (higher) value. I gave it a try, and found it correct.

Why do they report different widths? How can I correctly obtain the height and width? I mean, I can use .measureText(), but then I wouldn't know if I should trust the .height() returned by getTextBounds().

As requested, here is minimal code to reproduce the problem:

final String someText = "Hello. I believe I'm some text!";

Paint p = new Paint();
Rect bounds = new Rect();

for (float f = 10; f < 40; f += 1f) {
    p.setTextSize(f);

    p.getTextBounds(someText, 0, someText.length(), bounds);

    Log.d("Test", String.format(
        "Size %f, measureText %f, getTextBounds %d",
        f,
        p.measureText(someText),
        bounds.width())
    );
}

The output shows that the difference not only gets greater than 1 (and is no last-minute rounding error), but also seems to increase with size (I was about to draw more conclusions, but it may be entirely font-dependent):

D/Test    (  607): Size 10.000000, measureText 135.000000, getTextBounds 134
D/Test    (  607): Size 11.000000, measureText 149.000000, getTextBounds 148
D/Test    (  607): Size 12.000000, measureText 156.000000, getTextBounds 155
D/Test    (  607): Size 13.000000, measureText 171.000000, getTextBounds 169
D/Test    (  607): Size 14.000000, measureText 195.000000, getTextBounds 193
D/Test    (  607): Size 15.000000, measureText 201.000000, getTextBounds 199
D/Test    (  607): Size 16.000000, measureText 211.000000, getTextBounds 210
D/Test    (  607): Size 17.000000, measureText 225.000000, getTextBounds 223
D/Test    (  607): Size 18.000000, measureText 245.000000, getTextBounds 243
D/Test    (  607): Size 19.000000, measureText 251.000000, getTextBounds 249
D/Test    (  607): Size 20.000000, measureText 269.000000, getTextBounds 267
D/Test    (  607): Size 21.000000, measureText 275.000000, getTextBounds 272
D/Test    (  607): Size 22.000000, measureText 297.000000, getTextBounds 294
D/Test    (  607): Size 23.000000, measureText 305.000000, getTextBounds 302
D/Test    (  607): Size 24.000000, measureText 319.000000, getTextBounds 316
D/Test    (  607): Size 25.000000, measureText 330.000000, getTextBounds 326
D/Test    (  607): Size 26.000000, measureText 349.000000, getTextBounds 346
D/Test    (  607): Size 27.000000, measureText 357.000000, getTextBounds 354
D/Test    (  607): Size 28.000000, measureText 369.000000, getTextBounds 365
D/Test    (  607): Size 29.000000, measureText 396.000000, getTextBounds 392
D/Test    (  607): Size 30.000000, measureText 401.000000, getTextBounds 397
D/Test    (  607): Size 31.000000, measureText 418.000000, getTextBounds 414
D/Test    (  607): Size 32.000000, measureText 423.000000, getTextBounds 418
D/Test    (  607): Size 33.000000, measureText 446.000000, getTextBounds 441
D/Test    (  607): Size 34.000000, measureText 455.000000, getTextBounds 450
D/Test    (  607): Size 35.000000, measureText 468.000000, getTextBounds 463
D/Test    (  607): Size 36.000000, measureText 474.000000, getTextBounds 469
D/Test    (  607): Size 37.000000, measureText 500.000000, getTextBounds 495
D/Test    (  607): Size 38.000000, measureText 506.000000, getTextBounds 501
D/Test    (  607): Size 39.000000, measureText 521.000000, getTextBounds 515

解决方案

You can do what I did to inspect such problem:

Study Android source code, Paint.java source, see both measureText and getTextBounds methods. You'd learn that measureText calls native_measureText, and getTextBounds calls nativeGetStringBounds, which are native methods implemented in C++.

So you'd continue to study Paint.cpp, which implements both.

native_measureText -> SkPaintGlue::measureText_CII

nativeGetStringBounds -> SkPaintGlue::getStringBounds

Now your study checks where these methods differ. After some param checks, both call function SkPaint::measureText in Skia Lib (part of Android), but they both call different overloaded form.

Digging further into Skia, I see that both calls result into same computation in same function, only return result differently.

To answer your question: Both your calls do same computation. Possible difference of result lies in fact that getTextBounds returns bounds as integer, while measureText returns float value.

So what you get is rounding error during conversion of float to int, and this happens in Paint.cpp in SkPaintGlue::doTextBounds in call to function SkRect::roundOut.

The difference between computed width of those two calls may be maximally 1.

EDIT 4 Oct 2011

What may be better than visualization. I took the effort, for own exploring, and for deserving bounty :)

This is font size 60, in red is bounds rectangle, in purple is result of measureText.

It's seen that bounds left part starts some pixels from left, and value of measureText is incremented by this value on both left and right. This is something called Glyph's AdvanceX value. (I've discovered this in Skia sources in SkPaint.cpp)

So the outcome of the test is that measureText adds some advance value to the text on both sides, while getTextBounds computes minimal bounds where given text will fit.

Hope this result is useful to you.

Testing code:

  protected void onDraw(Canvas canvas){
     final String s = "Hello. I'm some text!";

     Paint p = new Paint();
     Rect bounds = new Rect();
     p.setTextSize(60);

     p.getTextBounds(s, 0, s.length(), bounds);
     float mt = p.measureText(s);
     int bw = bounds.width();

     Log.i("LCG", String.format(
          "measureText %f, getTextBounds %d (%s)",
          mt,
          bw, bounds.toShortString())
      );
     bounds.offset(0, -bounds.top);
     p.setStyle(Style.STROKE);
     canvas.drawColor(0xff000080);
     p.setColor(0xffff0000);
     canvas.drawRect(bounds, p);
     p.setColor(0xff00ff00);
     canvas.drawText(s, 0, bounds.bottom, p);
  }

这篇关于Android Paint:.measureText() 与 .getTextBounds()的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆