I've come up with a test (an empirical estimation) for the maximum font size at which emojis can still be rendered.
The way this function works is it creates a 1x1 bitmap, and tries to draw the Earth Globe emoji () in the center of that. Then it checks that single pixel, whether it's still transparent, or colored.
I've chosen the Earth Globe emoji because I assume we can be fairly certain that no artist would ever draw the Earth with a hole in the middle. (Or we're in huge trouble anyway.)
The tests are done in a binary search - fashion, so the runtime should be logarithmic.
(Fun fact: the max font size on both my test phones turned out to be 256
.)
public static float getMaxEmojiFontSize() {
return getMaxEmojiFontSize(new Paint(), 8, 999999, 1);
}
/**
* Emojis cannot be renderered above a certain font size due to a bug.
* This function tries to estimate what the maximum font size is where emojis can still
* be rendered.
* @param p A Paint object to do the testing with.
* A simple `new Paint()` should do.
* @param minTestSize From what size should we test if the emojis can be rendered.
* We're assuming that at this size, emojis can be rendered.
* A good value for this is 8.
* @param maxTestSize Until what size should we test if the emojis can be rendered.
* This can be the max font size you're planning to use.
* @param maxError How close should we be to the actual number with our estimation.
* For example, if this is 10, and the result from this function is
* 240, then the maximum font size that still renders is guaranteed
* to be under 250. (If maxTestSize is above 250.)
*/
public static float getMaxEmojiFontSize(Paint p, float minTestSize, float maxTestSize, float maxError) {
Bitmap b = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
float sizeLowerLimit = minTestSize; // Start testing from this size
float sizeUpperLimit = maxTestSize;
Canvas c = new Canvas(b);
float size;
for (size = sizeLowerLimit; size < maxTestSize; size *= 2) {
if (!canRenderEmoji(b, c, p, size)) {
sizeUpperLimit = size;
break;
}
sizeLowerLimit = size;
}
// We now have a lower and upper limit for the maximum emoji size.
// Let's proceed with a binary search.
while (sizeUpperLimit - sizeLowerLimit > maxError) {
float middleSize = (sizeUpperLimit + sizeLowerLimit) / 2f;
if (!canRenderEmoji(b, c, p, middleSize)) {
sizeUpperLimit = middleSize;
} else {
sizeLowerLimit = middleSize;
}
}
return sizeLowerLimit;
}
private static boolean canRenderEmoji(Bitmap b, Canvas can, Paint p, float size) {
final String EMOJI = "\uD83C\uDF0D"; // the Earth Globe (Europe, Africa) emoji - should never be transparent in the center.
can.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); // clear the canvas with transparent
p.setTextSize(size);
{ // draw the emoji in the center
float ascent = Math.abs(p.ascent());
float descent = Math.abs(p.descent());
float halfHeight = (ascent + descent) / 2.0f;
p.setTextAlign(Paint.Align.CENTER);
can.drawText(EMOJI, 0.5f, 0.5f + halfHeight - descent, p);
}
return b.getPixel(0, 0) != 0;
}