Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c74f45a3 authored by Romain Guy's avatar Romain Guy
Browse files

Properly scale text

This change does not apply to drawPosText() and drawTextOnPath() yet.

Prior to this change, glyphs were always rasterized based on the
font size specified in the paint. All transforms were then applied
on the resulting texture. This creates rather ugly results when
text is scaled and/or rotated.

With this change, the font renderer will apply the current transform
matrix to the glyph before they are rasterized. This generates much
better looking results.

Change-Id: I0141b6ff18db35e1213e7a3ab9db1ecaf03d7a9c
parent d80806b3
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -39,6 +39,11 @@ static const float EPSILON = 0.0000001f;
// Matrix
///////////////////////////////////////////////////////////////////////////////

const Matrix4& Matrix4::identity() {
    static Matrix4 sIdentity;
    return sIdentity;
}

void Matrix4::loadIdentity() {
    data[kScaleX]       = 1.0f;
    data[kSkewY]        = 0.0f;
+2 −0
Original line number Diff line number Diff line
@@ -152,6 +152,8 @@ public:

    void dump() const;

    static const Matrix4& identity();

private:
    mutable uint32_t mType;

+19 −27
Original line number Diff line number Diff line
@@ -1657,13 +1657,13 @@ void OpenGLRenderer::setupDrawModelViewTranslate(float left, float top, float ri
        mCaches.currentProgram->set(mOrthoMatrix, mModelView, *mSnapshot->transform);
        if (mTrackDirtyRegions) dirtyLayer(left, top, right, bottom, *mSnapshot->transform);
    } else {
        mCaches.currentProgram->set(mOrthoMatrix, mModelView, mIdentity);
        mCaches.currentProgram->set(mOrthoMatrix, mModelView, mat4::identity());
        if (mTrackDirtyRegions) dirtyLayer(left, top, right, bottom);
    }
}

void OpenGLRenderer::setupDrawModelViewIdentity(bool offset) {
    mCaches.currentProgram->set(mOrthoMatrix, mIdentity, *mSnapshot->transform, offset);
    mCaches.currentProgram->set(mOrthoMatrix, mat4::identity(), *mSnapshot->transform, offset);
}

void OpenGLRenderer::setupDrawModelView(float left, float top, float right, float bottom,
@@ -1681,7 +1681,7 @@ void OpenGLRenderer::setupDrawModelView(float left, float top, float right, floa
            dirtyLayer(left, top, right, bottom, *mSnapshot->transform);
        }
    } else {
        mCaches.currentProgram->set(mOrthoMatrix, mModelView, mIdentity);
        mCaches.currentProgram->set(mOrthoMatrix, mModelView, mat4::identity());
        if (mTrackDirtyRegions && dirty) dirtyLayer(left, top, right, bottom);
    }
}
@@ -1716,7 +1716,7 @@ void OpenGLRenderer::setupDrawShaderUniforms(bool ignoreTransform) {
void OpenGLRenderer::setupDrawShaderIdentityUniforms() {
    if (mDrawModifiers.mShader) {
        mDrawModifiers.mShader->setupProgram(mCaches.currentProgram,
                mIdentity, *mSnapshot, &mTextureUnit);
                mat4::identity(), *mSnapshot, &mTextureUnit);
    }
}

@@ -2587,7 +2587,7 @@ status_t OpenGLRenderer::drawPosText(const char* text, int bytesCount, int count
    }

    FontRenderer& fontRenderer = mCaches.fontRenderer->getFontRenderer(paint);
    fontRenderer.setFont(paint, *mSnapshot->transform);
    fontRenderer.setFont(paint, mat4::identity());

    int alpha;
    SkXfermode::Mode mode;
@@ -2663,17 +2663,10 @@ status_t OpenGLRenderer::drawText(const char* text, int bytesCount, int count,
        return DrawGlInfo::kStatusDone;
    }

#if DEBUG_GLYPHS
    ALOGD("OpenGLRenderer drawText() with FontID=%d",
            SkTypeface::UniqueID(paint->getTypeface()));
#endif

    FontRenderer& fontRenderer = mCaches.fontRenderer->getFontRenderer(paint);
    fontRenderer.setFont(paint, *mSnapshot->transform);

    const float oldX = x;
    const float oldY = y;
    const bool pureTranslate = mSnapshot->transform->isPureTranslate();

    if (CC_LIKELY(pureTranslate)) {
        x = (int) floorf(x + mSnapshot->transform->getTranslateX() + 0.5f);
        y = (int) floorf(y + mSnapshot->transform->getTranslateY() + 0.5f);
@@ -2683,16 +2676,19 @@ status_t OpenGLRenderer::drawText(const char* text, int bytesCount, int count,
    SkXfermode::Mode mode;
    getAlphaAndMode(paint, &alpha, &mode);

    FontRenderer& fontRenderer = mCaches.fontRenderer->getFontRenderer(paint);

    if (CC_UNLIKELY(mDrawModifiers.mHasShadow)) {
        drawTextShadow(paint, text, bytesCount, count, positions, fontRenderer, alpha, mode,
                oldX, oldY);
        fontRenderer.setFont(paint, mat4::identity());
        drawTextShadow(paint, text, bytesCount, count, positions, fontRenderer,
                alpha, mode, oldX, oldY);
    }

    fontRenderer.setFont(paint, pureTranslate ? mat4::identity() : *mSnapshot->transform);

    // Pick the appropriate texture filtering
    bool linearFilter = mSnapshot->transform->changesBounds();
    if (pureTranslate && !linearFilter) {
        linearFilter = fabs(y - (int) y) > 0.0f || fabs(x - (int) x) > 0.0f;
    }
    bool linearFilter = !mSnapshot->transform->isPureTranslate() ||
            fabs(y - (int) y) > 0.0f || fabs(x - (int) x) > 0.0f;

    // The font renderer will always use texture unit 0
    mCaches.activeTexture(0);
@@ -2705,17 +2701,16 @@ status_t OpenGLRenderer::drawText(const char* text, int bytesCount, int count,
    setupDrawShader();
    setupDrawBlending(true, mode);
    setupDrawProgram();
    setupDrawModelView(x, y, x, y, pureTranslate, true);
    setupDrawModelView(x, y, x, y, true, true);
    // See comment above; the font renderer must use texture unit 0
    // assert(mTextureUnit == 0)
    setupDrawTexture(fontRenderer.getTexture(linearFilter));
    setupDrawPureColorUniforms();
    setupDrawColorFilterUniforms();
    setupDrawShaderUniforms(pureTranslate);
    setupDrawShaderUniforms(true);
    setupDrawTextGammaUniforms();

    const Rect* clip = pureTranslate ? mSnapshot->clipRect :
            (mSnapshot->hasPerspectiveTransform() ? NULL : &mSnapshot->getLocalClip());
    const Rect* clip = mSnapshot->hasPerspectiveTransform() ? NULL : mSnapshot->clipRect;
    Rect bounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f);

    const bool hasActiveLayer = hasLayer();
@@ -2732,9 +2727,6 @@ status_t OpenGLRenderer::drawText(const char* text, int bytesCount, int count,
    }

    if (status && hasActiveLayer) {
        if (!pureTranslate) {
            mSnapshot->transform->mapRect(bounds);
        }
        dirtyLayerUnchecked(bounds, getRegion());
    }

@@ -2750,7 +2742,7 @@ status_t OpenGLRenderer::drawTextOnPath(const char* text, int bytesCount, int co
    }

    FontRenderer& fontRenderer = mCaches.fontRenderer->getFontRenderer(paint);
    fontRenderer.setFont(paint, *mSnapshot->transform);
    fontRenderer.setFont(paint, mat4::identity());

    int alpha;
    SkXfermode::Mode mode;
+0 −3
Original line number Diff line number Diff line
@@ -936,9 +936,6 @@ private:
    // List of layers to update at the beginning of a frame
    Vector<Layer*> mLayerUpdates;

    // Indentity matrix
    const mat4 mIdentity;

    // Indicates whether the clip must be restored
    bool mDirtyClip;

+43 −28
Original line number Diff line number Diff line
@@ -52,6 +52,10 @@ Font::FontDescription::FontDescription(const SkPaint* paint, const mat4& matrix)
    mStyle = paint->getStyle();
    mStrokeWidth = paint->getStrokeWidth();
    mAntiAliasing = paint->isAntiAlias();
    mLookupTransform[SkMatrix::kMScaleX] = matrix.data[mat4::kScaleX];
    mLookupTransform[SkMatrix::kMScaleY] = matrix.data[mat4::kScaleY];
    mLookupTransform[SkMatrix::kMSkewX] = matrix.data[mat4::kSkewX];
    mLookupTransform[SkMatrix::kMSkewY] = matrix.data[mat4::kSkewY];
}

Font::~Font() {
@@ -71,6 +75,10 @@ hash_t Font::FontDescription::hash() const {
    hash = JenkinsHashMix(hash, android::hash_type(mStyle));
    hash = JenkinsHashMix(hash, android::hash_type(mStrokeWidth));
    hash = JenkinsHashMix(hash, int(mAntiAliasing));
    hash = JenkinsHashMix(hash, android::hash_type(mLookupTransform[SkMatrix::kMScaleX]));
    hash = JenkinsHashMix(hash, android::hash_type(mLookupTransform[SkMatrix::kMScaleY]));
    hash = JenkinsHashMix(hash, android::hash_type(mLookupTransform[SkMatrix::kMSkewX]));
    hash = JenkinsHashMix(hash, android::hash_type(mLookupTransform[SkMatrix::kMSkewY]));
    return JenkinsHashWhiten(hash);
}

@@ -100,6 +108,26 @@ int Font::FontDescription::compare(const Font::FontDescription& lhs,
    deltaInt = int(lhs.mAntiAliasing) - int(rhs.mAntiAliasing);
    if (deltaInt != 0) return deltaInt;

    if (lhs.mLookupTransform[SkMatrix::kMScaleX] <
            rhs.mLookupTransform[SkMatrix::kMScaleX]) return -1;
    if (lhs.mLookupTransform[SkMatrix::kMScaleX] >
            rhs.mLookupTransform[SkMatrix::kMScaleX]) return +1;

    if (lhs.mLookupTransform[SkMatrix::kMScaleY] <
            rhs.mLookupTransform[SkMatrix::kMScaleY]) return -1;
    if (lhs.mLookupTransform[SkMatrix::kMScaleY] >
            rhs.mLookupTransform[SkMatrix::kMScaleY]) return +1;

    if (lhs.mLookupTransform[SkMatrix::kMSkewX] <
            rhs.mLookupTransform[SkMatrix::kMSkewX]) return -1;
    if (lhs.mLookupTransform[SkMatrix::kMSkewX] >
            rhs.mLookupTransform[SkMatrix::kMSkewX]) return +1;

    if (lhs.mLookupTransform[SkMatrix::kMSkewY] <
            rhs.mLookupTransform[SkMatrix::kMSkewY]) return -1;
    if (lhs.mLookupTransform[SkMatrix::kMSkewY] >
            rhs.mLookupTransform[SkMatrix::kMSkewY]) return +1;

    return 0;
}

@@ -169,12 +197,6 @@ void Font::drawCachedGlyphBitmap(CachedGlyphInfo* glyph, int x, int y,
    int32_t bX = 0, bY = 0;
    for (cacheX = glyph->mStartX, bX = nPenX; cacheX < endX; cacheX++, bX++) {
        for (cacheY = glyph->mStartY, bY = nPenY; cacheY < endY; cacheY++, bY++) {
#if DEBUG_FONT_RENDERER
            if (bX < 0 || bY < 0 || bX >= (int32_t) bitmapW || bY >= (int32_t) bitmapH) {
                ALOGE("Skipping invalid index");
                continue;
            }
#endif
            uint8_t tempCol = cacheBuffer[cacheY * cacheWidth + cacheX];
            bitmap[bY * bitmapW + bX] = tempCol;
        }
@@ -226,15 +248,15 @@ CachedGlyphInfo* Font::getCachedGlyph(SkPaint* paint, glyph_t textUnit, bool pre
    ssize_t index = mCachedGlyphs.indexOfKey(textUnit);
    if (index >= 0) {
        cachedGlyph = mCachedGlyphs.valueAt(index);
    } else {
        cachedGlyph = cacheGlyph(paint, textUnit, precaching);
    }

        // Is the glyph still in texture cache?
        if (!cachedGlyph->mIsValid) {
        const SkGlyph& skiaGlyph = GET_METRICS(paint, textUnit, NULL);
            const SkGlyph& skiaGlyph = GET_METRICS(paint, textUnit, &mDescription.mLookupTransform);
            updateGlyphCache(paint, skiaGlyph, cachedGlyph, precaching);
        }
    } else {
        cachedGlyph = cacheGlyph(paint, textUnit, precaching);
    }

    return cachedGlyph;
}
@@ -357,22 +379,14 @@ void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len

        // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
        if (cachedGlyph->mIsValid) {
            int penX = x + positions[(glyphsCount << 1)];
            int penY = y + positions[(glyphsCount << 1) + 1];
            float penX = x + positions[(glyphsCount << 1)];
            float penY = y + positions[(glyphsCount << 1) + 1];

            switch (align) {
                case SkPaint::kRight_Align:
                    penX -= SkFixedToFloat(cachedGlyph->mAdvanceX);
                    penY -= SkFixedToFloat(cachedGlyph->mAdvanceY);
                    break;
                case SkPaint::kCenter_Align:
                    penX -= SkFixedToFloat(cachedGlyph->mAdvanceX >> 1);
                    penY -= SkFixedToFloat(cachedGlyph->mAdvanceY >> 1);
                default:
                    break;
            if (!mTransform.isIdentity()) {
                mTransform.mapPoint(penX, penY);
            }

            (*this.*render)(cachedGlyph, penX, penY,
            (*this.*render)(cachedGlyph, roundf(penX), roundf(penY),
                    bitmap, bitmapW, bitmapH, bounds, positions);
        }

@@ -394,7 +408,7 @@ void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyp

    // Get the bitmap for the glyph
    if (!skiaGlyph.fImage) {
        paint->findImage(skiaGlyph, NULL);
        paint->findImage(skiaGlyph, &mDescription.mLookupTransform);
    }
    mState->cacheBitmap(skiaGlyph, glyph, &startX, &startY, precaching);

@@ -425,7 +439,7 @@ CachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, glyph_t glyph, bool precaching
    CachedGlyphInfo* newGlyph = new CachedGlyphInfo();
    mCachedGlyphs.add(glyph, newGlyph);

    const SkGlyph& skiaGlyph = GET_METRICS(paint, glyph, NULL);
    const SkGlyph& skiaGlyph = GET_METRICS(paint, glyph, &mDescription.mLookupTransform);
    newGlyph->mGlyphIndex = skiaGlyph.fID;
    newGlyph->mIsValid = false;

@@ -439,6 +453,7 @@ Font* Font::create(FontRenderer* state, const SkPaint* paint, const mat4& matrix
    Font* font = state->mActiveFonts.get(description);

    if (font) {
        font->mTransform.load(matrix);
        return font;
    }

Loading