public class

TypefaceEmojiRasterizer

extends java.lang.Object

 java.lang.Object

↳androidx.emoji2.text.TypefaceEmojiRasterizer

Gradle dependencies

compile group: 'androidx.emoji2', name: 'emoji2', version: '1.5.0'

  • groupId: androidx.emoji2
  • artifactId: emoji2
  • version: 1.5.0

Artifact androidx.emoji2:emoji2:1.5.0 it located at Google repository (https://maven.google.com/)

Overview

Information about a single emoji. To draw this emoji on a canvas using use draw. To draw this emoji using custom code, use getCodepointAt and getTypeface to access the underlying emoji font and look up the glyph.

Summary

Methods
public voiddraw(Canvas canvas, float x, float y, Paint paint)

Draws the emoji onto a canvas with origin at (x,y), using the specified paint.

public intgetCodepointAt(int index)

public intgetCodepointsLength()

public shortgetCompatAdded()

public intgetHasGlyph()

Returns the value set by setHasGlyph This is intended to be used as a cache on this emoji to avoid repeatedly calling PaintCompat#hasGlyph on the same codepoint sequence, which is expensive.

public intgetHeight()

public intgetId()

Unique id for the emoji, as loaded from the font file.

public shortgetSdkAdded()

public TypefacegetTypeface()

public intgetWidth()

public booleanisDefaultEmoji()

public booleanisPreferredSystemRender()

If the platform requested that this emoji not be rendered using emojicompat.

public voidresetHasGlyphCache()

Reset any cached values of hasGlyph on this metadata.

public voidsetExclusion(boolean exclude)

If this emoji is excluded due to CodepointExclusions.getExcludedCodpoints()

public voidsetHasGlyph(boolean hasGlyph)

Set whether the system can render the emoji.

public java.lang.StringtoString()

from java.lang.Objectclone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait

Methods

public void draw(Canvas canvas, float x, float y, Paint paint)

Draws the emoji onto a canvas with origin at (x,y), using the specified paint.

Parameters:

canvas: Canvas to be drawn
x: x-coordinate of the origin of the emoji being drawn
y: y-coordinate of the baseline of the emoji being drawn
paint: Paint used for the text (e.g. color, size, style)

public Typeface getTypeface()

Returns:

return typeface to be used to render

public int getId()

Unique id for the emoji, as loaded from the font file.

Returns:

unique id for the emoji

public int getWidth()

Returns:

width of the emoji image

public int getHeight()

Returns:

height of the emoji image

public short getCompatAdded()

Returns:

in which metadata version the emoji was added

public short getSdkAdded()

Returns:

first SDK that the support for this emoji was added

public int getHasGlyph()

Returns the value set by setHasGlyph This is intended to be used as a cache on this emoji to avoid repeatedly calling PaintCompat#hasGlyph on the same codepoint sequence, which is expensive.

Returns:

the set value of hasGlyph for this metadata item

See also: TypefaceEmojiRasterizer.setHasGlyph(boolean)

public void resetHasGlyphCache()

Reset any cached values of hasGlyph on this metadata. This is only useful for testing, and will make the next display of this emoji slower.

public void setHasGlyph(boolean hasGlyph)

Set whether the system can render the emoji.

Parameters:

hasGlyph: true if system can render the emoji

See also:

public void setExclusion(boolean exclude)

If this emoji is excluded due to CodepointExclusions.getExcludedCodpoints()

Parameters:

exclude: if the emoji should never be rendered by emojicompat

public boolean isPreferredSystemRender()

If the platform requested that this emoji not be rendered using emojicompat.

Returns:

true if this emoji should be drawn by the system instead of this renderer

public boolean isDefaultEmoji()

Returns:

whether the emoji is in Emoji Presentation by default (without emoji style selector 0xFE0F)

public int getCodepointAt(int index)

Parameters:

index: index of the codepoint

Returns:

the codepoint at index

public int getCodepointsLength()

Returns:

the length of the codepoints for this emoji

public java.lang.String toString()

Source

/*
 * Copyright 2021 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package androidx.emoji2.text;

import static androidx.annotation.RestrictTo.Scope.LIBRARY;
import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;

import android.annotation.SuppressLint;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Typeface;

import androidx.annotation.AnyThread;
import androidx.annotation.IntDef;
import androidx.annotation.IntRange;
import androidx.annotation.NonNull;
import androidx.annotation.RestrictTo;
import androidx.annotation.VisibleForTesting;
import androidx.emoji2.text.flatbuffer.MetadataItem;
import androidx.emoji2.text.flatbuffer.MetadataList;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

/**
 * Information about a single emoji.
 *
 * To draw this emoji on a canvas using use draw.
 *
 * To draw this emoji using custom code, use getCodepointAt and getTypeface to access the
 * underlying emoji font and look up the glyph.
 *
 * @see TypefaceEmojiRasterizer#draw
 * @see TypefaceEmojiRasterizer#getCodepointAt
 * @see TypefaceEmojiRasterizer#getTypeface
 *
 */
@AnyThread
public class TypefaceEmojiRasterizer {
    /**
     * Defines whether the system can render the emoji.
     */
    @IntDef({HAS_GLYPH_UNKNOWN, HAS_GLYPH_ABSENT, HAS_GLYPH_EXISTS})
    @Retention(RetentionPolicy.SOURCE)
    @RestrictTo(LIBRARY_GROUP)
    public @interface HasGlyph {
    }

    /**
     * Not calculated on device yet.
     */
    @RestrictTo(LIBRARY)
    static final int HAS_GLYPH_UNKNOWN = 0;

    /**
     * Device cannot render the emoji.
     */
    @RestrictTo(LIBRARY)
    static final int HAS_GLYPH_ABSENT = 1;

    /**
     * Device can render the emoji.
     */
    @RestrictTo(LIBRARY)
    static final int HAS_GLYPH_EXISTS = 2;

    /**
     * @see #getMetadataItem()
     */
    private static final ThreadLocal<MetadataItem> sMetadataItem = new ThreadLocal<>();

    /**
     * Index of the TypefaceEmojiRasterizer in {@link MetadataList}.
     */
    private final int mIndex;

    /**
     * MetadataRepo that holds this instance.
     */
    @NonNull
    private final MetadataRepo mMetadataRepo;

    /**
     * Stores hasGlyph as well as exclusion values
     *
     * mCache & 0b0011 is hasGlyph result
     * mCache & 0b0100 is exclusion value
     */
    private volatile int mCache = 0;

    /**
     */
    @RestrictTo(LIBRARY)
    TypefaceEmojiRasterizer(@NonNull final MetadataRepo metadataRepo,
            @IntRange(from = 0) final int index) {
        mMetadataRepo = metadataRepo;
        mIndex = index;
    }

    /**
     * Draws the emoji onto a canvas with origin at (x,y), using the specified paint.
     *
     * @param canvas Canvas to be drawn
     * @param x x-coordinate of the origin of the emoji being drawn
     * @param y y-coordinate of the baseline of the emoji being drawn
     * @param paint Paint used for the text (e.g. color, size, style)
     */
    public void draw(@NonNull final Canvas canvas, final float x, final float y,
            @NonNull final Paint paint) {
        final Typeface typeface = mMetadataRepo.getTypeface();
        final Typeface oldTypeface = paint.getTypeface();
        paint.setTypeface(typeface);
        // MetadataRepo.getEmojiCharArray() is a continuous array of chars that is used to store the
        // chars for emojis. since all emojis are mapped to a single codepoint, and since it is 2
        // chars wide, we assume that the start index of the current emoji is mIndex * 2, and it is
        // 2 chars long.
        final int charArrayStartIndex = mIndex * 2;
        canvas.drawText(mMetadataRepo.getEmojiCharArray(), charArrayStartIndex, 2, x, y, paint);
        paint.setTypeface(oldTypeface);
    }

    /**
     * @return return typeface to be used to render
     */
    @NonNull
    public Typeface getTypeface() {
        return mMetadataRepo.getTypeface();
    }

    /**
     * @return a ThreadLocal instance of MetadataItem for this TypefaceEmojiRasterizer
     */
    private MetadataItem getMetadataItem() {
        MetadataItem result = sMetadataItem.get();
        if (result == null) {
            result = new MetadataItem();
            sMetadataItem.set(result);
        }
        // MetadataList is a wrapper around the metadata ByteBuffer. MetadataItem is a wrapper with
        // an index (pointer) on this ByteBuffer that represents a single emoji. Both are FlatBuffer
        // classes that wraps a ByteBuffer and gives access to the information in it. In order not
        // to create a wrapper class for each TypefaceEmojiRasterizer, we use mIndex as the index
        // of the MetadataItem in the ByteBuffer. We need to reiniitalize the current thread
        // local instance by executing the statement below. All the statement does is to set an
        // int index in MetadataItem. the same instance is used by all TypefaceEmojiRasterizer
        // classes in the same thread.
        mMetadataRepo.getMetadataList().list(result, mIndex);
        return result;
    }

    /**
     * Unique id for the emoji, as loaded from the font file.
     *
     * @return unique id for the emoji
     */
    @RestrictTo(LIBRARY)
    public int getId() {
        return getMetadataItem().id();
    }

    /**
     * @return width of the emoji image
     */
    public int getWidth() {
        return getMetadataItem().width();
    }

    /**
     * @return height of the emoji image
     */
    public int getHeight() {
        return getMetadataItem().height();
    }

    /**
     * @return in which metadata version the emoji was added
     */
    @RestrictTo(LIBRARY)
    public short getCompatAdded() {
        return getMetadataItem().compatAdded();
    }

    /**
     * @return first SDK that the support for this emoji was added
     */
    @RestrictTo(LIBRARY)
    public short getSdkAdded() {
        return getMetadataItem().sdkAdded();
    }

    /**
     * Returns the value set by setHasGlyph
     *
     * This is intended to be used as a cache on this emoji to avoid repeatedly calling
     * PaintCompat#hasGlyph on the same codepoint sequence, which is expensive.
     *
     * @see TypefaceEmojiRasterizer#setHasGlyph
     * @return the set value of hasGlyph for this metadata item
     */
    @HasGlyph
    @SuppressLint("KotlinPropertyAccess")
    @RestrictTo(LIBRARY)
    public int getHasGlyph() {
        return (int) (mCache & 0b0011);
    }

    /**
     * Reset any cached values of hasGlyph on this metadata.
     *
     * This is only useful for testing, and will make the next display of this emoji slower.
     *
     */
    @RestrictTo(LIBRARY)
    @VisibleForTesting
    public void resetHasGlyphCache() {
        boolean willExclude = isPreferredSystemRender();
        if (willExclude) {
            mCache = 0b0100;
        } else {
            mCache = 0b0000;
        }
    }

    /**
     * Set whether the system can render the emoji.
     *
     * @see PaintCompat#hasGlyph
     * @param hasGlyph {@code true} if system can render the emoji
     */
    @SuppressLint("KotlinPropertyAccess")
    @RestrictTo(LIBRARY)
    public void setHasGlyph(boolean hasGlyph) {
        int newValue = mCache & 0b0100; /* keep the exclusion bit */
        if (hasGlyph) {
            newValue |= 0b0010;
        } else {
            newValue |= 0b0001;
        }
        mCache =  newValue;
    }

    /**
     * If this emoji is excluded due to CodepointExclusions.getExcludedCodpoints()
     *
     * @param exclude if the emoji should never be rendered by emojicompat
     */
    @RestrictTo(LIBRARY)
    public void setExclusion(boolean exclude) {
        int hasGlyphBits = getHasGlyph();
        if (exclude) {
            mCache = hasGlyphBits | 0b0100;
        } else {
            mCache = hasGlyphBits;
        }
    }

    /**
     * If the platform requested that this emoji not be rendered using emojicompat.
     *
     * @return true if this emoji should be drawn by the system instead of this renderer
     */
    public boolean isPreferredSystemRender() {
        return (mCache & 0b0100) > 0;
    }

    /**
     * @return whether the emoji is in Emoji Presentation by default (without emoji
     *         style selector 0xFE0F)
     */
    public boolean isDefaultEmoji() {
        return getMetadataItem().emojiStyle();
    }

    /**
     * @param index index of the codepoint
     *
     * @return the codepoint at index
     */
    public int getCodepointAt(int index) {
        return getMetadataItem().codepoints(index);
    }

    /**
     * @return the length of the codepoints for this emoji
     */
    public int getCodepointsLength() {
        return getMetadataItem().codepointsLength();
    }

    @NonNull
    @Override
    public String toString() {
        final StringBuilder builder = new StringBuilder();
        builder.append(super.toString());
        builder.append(", id:");
        builder.append(Integer.toHexString(getId()));
        builder.append(", codepoints:");
        final int codepointsLength = getCodepointsLength();
        for (int i = 0; i < codepointsLength; i++) {
            builder.append(Integer.toHexString(getCodepointAt(i)));
            builder.append(" ");
        }
        return builder.toString();
    }
}