Android HWUI Cache绘制流程

HWUI Cache绘制流程

RenderBufferCache

RenderBuffer

Represents an OpenGL render buffer. Render buffers are attached to layers to perform stencil work.
渲染缓冲,这里创建的目的是用来执行模板测试。

    void allocate() {
        if (!mAllocated) {
            glRenderbufferStorage(GL_RENDERBUFFER, mFormat, mWidth, mHeight);
            mAllocated = true;
        }
    }

glRenderbufferStorage函数可以创建一个深度和模板渲染缓冲对象。

GradientCache

[转载]在Android中,提供了Shader类专门用来渲染图像以及一些几何图形。

Shader类包括了5个直接子类,分别为:BitmapShader、ComposeShader、LinearGradient、RadialGradient
以及SweepGradient。
RadialGradient用于环形渲染;
而SweepGradient则用于梯度渲染。

使用Shader类进行图像渲染时,首先需要构建Shader对象,然后通过Paint的setShader()方法来设置渲染对象,
最后将这个Paint对象绘制到屏幕上即可。

原文链接:https://www.cnblogs.com/menlsh/archive/2012/12/09/2810372.html

Shader的颜色如何过渡?

[转载]在最常用的RGB模式中,任何颜色都是由红绿蓝3种颜色叠加而成的,只要逐个增减它的RGB值,就是在渐变。
以黄(255,255,0)到蓝(0,0,255)为例,黄色就这么渐变成蓝色了。

这里写图片描述

所以实现渐变时,两种颜色中间需要过渡256次,过渡色彩就要有256个。

原文链接:https://www.zhihu.com/question/28972919/answer/42743881

LinearGradient(线性渲染)
LinearGradient的作用是实现某一区域内颜色的线性渐变效果。

LinearGradient的函数原型为:
    /** 实现某一区域内颜色的线性渐变效果
        @param x0           x0表示渐变的起始点x坐标
        @param y0           y0表示渐变的起始点y坐标
        @param x1           x1表示渐变的终点x坐标
        @param y1           y1表示渐变的终点y坐标
        @param  colors      colors表示渐变的颜色数组
        @param  positions   positions用来指定颜色数组的相对位置
        @param  tile        tile表示平铺方式
    */
  public LinearGradient (float x0, float y0, float x1, float y1, int[] colors, float[] 
  positions, Shader.TileMode tile);
通常,参数positions设为null,表示颜色数组以斜坡线的形式均匀分布。
Shader.TileMode有3种参数可供选择,分别为CLAMP、REPEAT和MIRROR。
CLAMP的作用是如果渲染器超出原始边界范围,则会复制边缘颜色对超出范围的区域进行着色。
REPEAT的作用是在横向和纵向上以平铺的形式重复渲染位图。MIRROR的作用是在横向和纵向上以镜像的方式重复渲染位图。
RadialGradient(环形渲染)
RadialGradient的作用是在某一区域内实现环形的渐变效果。

RadialGradient的函数原型为:
    /** 在某一区域内实现环形的渐变效果
        @param centerX  x表示环形的圆心x坐标
        @param centerY  y表示环形的圆心y坐标
        @param radius   radius表示环形的半径
        @param colors   colors表示环形渐变的颜色数组
        @param stops    positions用来指定颜色数组的相对位置
        @param tileMode tile表示平铺的方式
    */
  public RadialGradient (float x, float y, float radius, int[] colors, float[] positions, 
  Shader.TileMode tile);
SweepGradient(梯度渲染)
SweepGradient也称为扫描渲染,是指在某一中心以x轴正方向逆时针旋转一周而形成的扫描效果的渲染形式。

SweepGradient的函数原型为:
    /**
     * A subclass of Shader that draws a sweep gradient around a center point.
     *
     * @param cx       cx表示扫描的中心x坐标
     * @param cy       cy表示扫描的中心y坐标
     * @param colors   colors表示梯度渐变的颜色数组
     * @param positions positions用来指定颜色数组的相对位置。
     */
  public SweepGradient (float cx, float cy, int[] colors, float[] positions);
GradientCache的执行流程
SkiaShader.cpp

bool tryStoreGradient(Caches& caches, const SkShader& shader, const Matrix4 modelViewMatrix,
        GLuint* textureUnit, ProgramDescription* description,
        SkiaShaderData::GradientShaderData* outData) {
    SkShader::GradientInfo gradInfo;
    gradInfo.fColorCount = 0;
    gradInfo.fColors = nullptr;
    gradInfo.fColorOffsets = nullptr;

    SkMatrix unitMatrix;
    switch (shader.asAGradient(&gradInfo)) {
        case SkShader::kLinear_GradientType: //对应LinearGradient(线性渲染)
            description->gradientType = ProgramDescription::kGradientLinear;

            toLinearUnitMatrix(gradInfo.fPoint, &unitMatrix);
            break;
        case SkShader::kRadial_GradientType: //对应RadialGradient(线性渲染)
            description->gradientType = ProgramDescription::kGradientCircular;

            toCircularUnitMatrix(gradInfo.fPoint[0].fX, gradInfo.fPoint[0].fY,
                    gradInfo.fRadius[0], &unitMatrix);
            break;
        case SkShader::kSweep_GradientType: //对应SweepGradient(梯度渲染)
            description->gradientType = ProgramDescription::kGradientSweep;

            toSweepUnitMatrix(gradInfo.fPoint[0].fX, gradInfo.fPoint[0].fY, &unitMatrix);
            break;
        default:
            // Do nothing. This shader is unsupported.
            return false;
    }
}
GlopBuilder::build
    SkiaShader::store
        SkiaShader::tryStoreGradient
            GradientCache::get
                GradientCache::addLinearGradient

Texture* GradientCache::addLinearGradient(GradientCacheEntry& gradient,
        uint32_t* colors, float* positions, int count) {
    GradientInfo info;
    //获取渐变的宽度和是否有alpha值
    getGradientInfo(colors, count, info);
    //创建纹理
    Texture* texture = new Texture(Caches::getInstance());
    texture->blend = info.hasAlpha;
    texture->generation = 1;

    // Assume the cache is always big enough
    //渐变图片高度方向是重复的,只需要储存两个像素大小的高度就足够了。
    const uint32_t size = info.width * 2 * bytesPerPixel();
    while (getSize() + size > mMaxSize) {
        LOG_ALWAYS_FATAL_IF(!mCache.removeOldest(),
                "Ran out of things to remove from the cache? getSize() = %" PRIu32
                ", size = %" PRIu32 ", mMaxSize = %" PRIu32 ", width = %" PRIu32,
                getSize(), size, mMaxSize, info.width);
    }
    //将根据shader产生的渐变图片上传为纹理。
    generateTexture(colors, positions, info.width, 2, texture);
    //增加cache大小
    mSize += size;
    LOG_ALWAYS_FATAL_IF((int)size != texture->objectSize(),
            "size != texture->objectSize(), size %" PRIu32 ", objectSize %d"
            " width = %" PRIu32 " bytesPerPixel() = %zu",
            size, texture->objectSize(), info.width, bytesPerPixel());
    //保存cache
    mCache.put(gradient, texture);

    return texture;
}
void GradientCache::getGradientInfo(const uint32_t* colors, const int count,
        GradientInfo& info) {
    uint32_t width = 256 * (count - 1);//两个颜色之间渐变的颜色 * 几次颜色渐变

    // If the npot extension is not supported we cannot use non-clamp
    // wrap modes. We therefore find the nearest largest power of 2
    // unless width is already a power of 2
    if (!mHasNpot && (width & (width - 1)) != 0) {
        width = 1 << (32 - __builtin_clz(width));
    }
    //计算有没有alpha
    bool hasAlpha = false;
    for (int i = 0; i < count; i++) {
        if (((colors[i] >> 24) & 0xff) < 255) {
            hasAlpha = true;
            break;
        }
    }

    info.width = min(width, uint32_t(mMaxTextureSize));
    info.hasAlpha = hasAlpha;
}

getGradientInfo函数会根据colors代表颜色数值的大小,确定渐变的宽度。
下一步就是将根据shader产生的渐变图片上传为纹理。

void GradientCache::generateTexture(uint32_t* colors, float* positions,
        const uint32_t width, const uint32_t height, Texture* texture) {
    const GLsizei rowBytes = width * bytesPerPixel();
    uint8_t pixels[rowBytes * height];

    static ChannelSplitter gSplitters[] = {
            &android::uirenderer::GradientCache::splitToBytes,
            &android::uirenderer::GradientCache::splitToFloats,
    };
    ChannelSplitter split = gSplitters[mUseFloatTexture];

    static ChannelMixer gMixers[] = {
            &android::uirenderer::GradientCache::mixBytes,
            &android::uirenderer::GradientCache::mixFloats,
    };
    ChannelMixer mix = gMixers[mUseFloatTexture];//根据bool型mUseFloatTexture确定是那个函数

    GradientColor start;
    (this->*split)(colors[0], start);

    GradientColor end;
    (this->*split)(colors[1], end);

    int currentPos = 1;
    float startPos = positions[0];
    float distance = positions[1] - startPos;

    uint8_t* dst = pixels;
    for (uint32_t x = 0; x < width; x++) {
        float pos = x / float(width - 1);
        if (pos > positions[currentPos]) {
            start = end;
            startPos = positions[currentPos];

            currentPos++;

            (this->*split)(colors[currentPos], end);
            distance = positions[currentPos] - startPos;
        }

        float amount = (pos - startPos) / distance;
        (this->*mix)(start, end, amount, dst);
    }

    memcpy(pixels + rowBytes, pixels, rowBytes);
    //upload()中实现纹理上传
    if (mUseFloatTexture) {
        // We have to use GL_RGBA16F because GL_RGBA32F does not support filtering
        texture->upload(GL_RGBA16F, width, height, GL_RGBA, GL_FLOAT, pixels);
    } else {
        texture->upload(GL_RGBA, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
    }

    texture->setFilter(GL_LINEAR);
    //GL_CLAMP_TO_EDGE  纹理坐标会被约束在0到1之间,超出的部分会重复纹理坐标的边缘,产生一种边缘被拉伸的效果。
    //这个值用来拉伸纵向图形
    texture->setWrap(GL_CLAMP_TO_EDGE);
}

再看bytesPerPixel()函数,目前版本mUseFloatTexture = true,那么sizeof(float) = 4。

size_t GradientCache::bytesPerPixel() const {
    // We use 4 channels (RGBA)
    return 4 * (mUseFloatTexture ? sizeof(float) : sizeof(uint8_t));
}

所以cache的计算公式为:
size = 256 * (count - 1) * 2 * 4 * 4

PathCache

PathCache中使用lru算法维护cache生命周期,使用PathProcessor单独开一个线程进行资源预加载。

PathProcessor如何运作,需要了解TaskManager逻辑:

先看一下PathProcessor的类图:

这里写图片描述
调用流程:

FrameBuilder::deferPathOp
    PathCache::precache

precache之后的调用流程图如下:

这里写图片描述

TaskManager中创建了一个线程池,线程名称为hwuiTask+order

TaskManager::TaskManager() {
    // Get the number of available CPUs. This value does not change over time.
    int cpuCount = sysconf(_SC_NPROCESSORS_CONF);

    // Really no point in making more than 2 of these worker threads, but
    // we do want to limit ourselves to 1 worker thread on dual-core devices.
    int workerCount = cpuCount > 2 ? 2 : 1;//cpu数量大于2,两个线程
    for (int i = 0; i < workerCount; i++) {
        String8 name;
        name.appendFormat("hwuiTask%d", i + 1);//hwuiTask1和hwuiTask2
        mThreads.push_back(new WorkerThread(name));
    }
}

当有任务入队时,开启线程:

bool TaskManager::WorkerThread::addTask(const TaskWrapper& task) {
    if (!isRunning()) {
        run(mName.string(), PRIORITY_DEFAULT);//开启线程
    } else if (exitPending()) {
        return false;
    }
    ... ...
}

线程运行时,会回调Task的onProcess方法:

bool TaskManager::WorkerThread::threadLoop() {
    mSignal.wait();
    ... ...

    for (size_t i = 0; i < tasks.size(); i++) {
        const TaskWrapper& task = tasks[i];
        task.mProcessor->process(task.mTask);
    }

    return true;
}
void PathCache::PathProcessor::onProcess(const sp<Task<SkBitmap*> >& task) {
    PathTask* t = static_cast<PathTask*>(task.get());
    ATRACE_NAME("pathPrecache");
    float left, top, offset;
    uint32_t width, height;
    PathCache::computePathBounds(&t->path, &t->paint, left, top, offset, width, height);

    PathTexture* texture = t->texture;
    texture->left = left;
    texture->top = top;
    texture->offset = offset;
    if (width <= mMaxTextureSize && height <= mMaxTextureSize) {
        SkBitmap* bitmap = new SkBitmap();
        drawPath(&t->path, &t->paint, *bitmap, left, top, offset, width, height);
        t->setResult(bitmap);
    } else {
        t->setResult(nullptr);
    }
}

pathTexture大小计算公式:

void PathCache::computeBounds(const SkRect& bounds, const SkPaint* paint,
        float& left, float& top, float& offset, uint32_t& width, uint32_t& height) {
    const float pathWidth = std::max(bounds.width(), 1.0f);
    const float pathHeight = std::max(bounds.height(), 1.0f);

    left = bounds.fLeft;
    top = bounds.fTop;
    //1、先取画笔getStrokeWidth和1.0中的最大
    //2、使用floorf取计算出的最大的不超过计算值的整数
    offset = (int) floorf(std::max(paint->getStrokeWidth(), 1.0f) * 1.5f + 0.5f);

    width = uint32_t(pathWidth + offset * 2.0 + 0.5);
    height = uint32_t(pathHeight + offset * 2.0 + 0.5);
}

比如我的代码如下:

        mPaint.setStrokeWidth(1);

        Path path = new Path();
        path.lineTo(100, 100);
        path.close();
        canvas.drawPath(path,mPaint);

那么width=100 + floorf((1 * 1.5f) + 0.5f) * 2.0 + 0.5 = 104

PatchCache

.9图片的cache

调用流程:

RecordingCanvas::drawNinePatch --- addOp(PatchOp)
    ... ...
    BakedOpDispatcher::onPatchOp
        PatchCache::get

FontRender

先给大家看一张字体的纹理方便理解:

这里写图片描述

Demo code

canvas.drawText("字体", 200,500,mPaint);

调用流程:

//addOp
Canvas::drawText
    DrawTextFunctor::operator
        RecordingCanvas::drawGlyphs
             addOp(alloc().create_trivial<TextOp>)
//precache
FontRenderer::precache
    Font::precache
        Font::getCachedGlyph
            Font::cacheGlyph 或者
            Font::updateGlyphCache
                FontRenderer::cacheBitmap

//defer结束,绘制开始,cache结束
FrameBuilder::replayBakedOps
    FrameBuilder::finishDefer
        GammaFontRenderer::endPrecaching
            FontRenderer::endPrecaching
                FontRenderer::checkTextureUpdate
                    FontRenderer::checkTextureUpdateForCache
                        CacheTexture::upload
                            GpuPixelBuffer::upload或
                            CpuPixelBuffer::upload

//绘制
BakedOpDispatcher::onMergedTextOps
    BakedOpDispatcher::renderText
        FontRenderer::setFont
        FontRenderer::renderPosText
            Font::render
                Font::drawCachedGlyph
                    FontRenderer::appendRotatedMeshQuad
                        FontRenderer::issueDrawCommand
                            TextDrawFunctor::draw
                                GlopBuilder
1.addOp
void Canvas::drawText(const uint16_t* text, int start, int count, int contextCount,
        float x, float y, int bidiFlags, const Paint& origPaint, Typeface* typeface) {
    // minikin may modify the original paint
    Paint paint(origPaint);

    Layout layout;
    MinikinUtils::doLayout(&layout, &paint, bidiFlags, typeface, text, start, count, contextCount);//调用Minikin和Skia获取字体参数

    size_t nGlyphs = layout.nGlyphs();
    std::unique_ptr<uint16_t[]> glyphs(new uint16_t[nGlyphs]);
    std::unique_ptr<float[]> pos(new float[nGlyphs * 2]);

    x += MinikinUtils::xOffsetForTextAlign(&paint, layout);

    MinikinRect bounds;
    layout.getBounds(&bounds);
    if (!drawTextAbsolutePos()) {
        bounds.offset(x, y);
    }

    // Set align to left for drawing, as we don't want individual
    // glyphs centered or right-aligned; the offset above takes
    // care of all alignment.
    paint.setTextAlign(Paint::kLeft_Align);

    DrawTextFunctor f(layout, this, glyphs.get(), pos.get(),
            paint, x, y, bounds, layout.getAdvance());
    MinikinUtils::forFontRun(layout, &paint, f);//获取字形等参数
}
2.precache
2.1 init

FontRenderer在初始化时,会根据配置创建CacheTexture

FontUtil

#define DEFAULT_TEXT_SMALL_CACHE_WIDTH 1024
#define DEFAULT_TEXT_SMALL_CACHE_HEIGHT 512
#define DEFAULT_TEXT_LARGE_CACHE_WIDTH 2048
#define DEFAULT_TEXT_LARGE_CACHE_HEIGHT 512
FontRenderer::FontRenderer(const uint8_t* gammaTable)
    ... ...
    mSmallCacheWidth = property_get_int32(PROPERTY_TEXT_SMALL_CACHE_WIDTH,
            DEFAULT_TEXT_SMALL_CACHE_WIDTH);
    mSmallCacheHeight = property_get_int32(PROPERTY_TEXT_SMALL_CACHE_HEIGHT,
            DEFAULT_TEXT_SMALL_CACHE_HEIGHT);

    mLargeCacheWidth = property_get_int32(PROPERTY_TEXT_LARGE_CACHE_WIDTH,
            DEFAULT_TEXT_LARGE_CACHE_WIDTH);
    mLargeCacheHeight = property_get_int32(PROPERTY_TEXT_LARGE_CACHE_HEIGHT,
            DEFAULT_TEXT_LARGE_CACHE_HEIGHT);
    ... ...
std::vector<CacheTexture*> mACacheTextures;
std::vector<CacheTexture*> mRGBACacheTextures;

void FontRenderer::initTextTexture() {
    clearCacheTextures(mACacheTextures);
    clearCacheTextures(mRGBACacheTextures);

    mUploadTexture = false;
    mACacheTextures.push_back(createCacheTexture(mSmallCacheWidth, mSmallCacheHeight,
            GL_ALPHA, true));
    mACacheTextures.push_back(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1,
            GL_ALPHA, false));
    mACacheTextures.push_back(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1,
            GL_ALPHA, false));
    mACacheTextures.push_back(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight,
            GL_ALPHA, false));
    mRGBACacheTextures.push_back(createCacheTexture(mSmallCacheWidth, mSmallCacheHeight,
            GL_RGBA, false));
    mRGBACacheTextures.push_back(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1,
            GL_RGBA, false));
    mCurrentCacheTexture = mACacheTextures[0];
}
2.2 cache
CacheTexture* FontRenderer::createCacheTexture(int width, int height, GLenum format,
        bool allocate) {
    CacheTexture* cacheTexture = new CacheTexture(width, height, format, kMaxNumberOfQuads);
    // allocate=false,创建对象但是未分配内存
    if (allocate) {
        Caches::getInstance().textureState().activateTexture(0);
        cacheTexture->allocatePixelBuffer();
        cacheTexture->allocateMesh();
    }

    return cacheTexture;
}

cacheBitmap用来cache字形对应的图片和位置信息

void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph,
        uint32_t* retOriginX, uint32_t* retOriginY, bool precaching) {

    ... ...

    //所有的字体保存在同一张纹理中,有CacheTexture维护
    //1、获取CacheTexture
    //2、计算字体在大纹理中的大小和坐标
    CacheTexture* cacheTexture = cacheBitmapInTexture(*cacheTextures, glyph, &startX, &startY);

    if (!cacheTexture) {
        if (!precaching) {
            // If the new glyph didn't fit and we are not just trying to precache it,
            // clear out the cache and try again
            flushAllAndInvalidate();
            cacheTexture = cacheBitmapInTexture(*cacheTextures, glyph, &startX, &startY);
        }

        if (!cacheTexture) {
            // either the glyph didn't fit or we're precaching and will cache it when we draw
            return;
        }
    }

    cachedGlyph->mCacheTexture = cacheTexture;

    *retOriginX = startX;
    *retOriginY = startY;

    uint32_t endX = startX + glyph.fWidth;
    uint32_t endY = startY + glyph.fHeight;

    uint32_t cacheWidth = cacheTexture->getWidth();

    if (!cacheTexture->getPixelBuffer()) {
        //hwui使用的是多重纹理贴图,字体的纹理是GL_TEXTURE0
        Caches::getInstance().textureState().activateTexture(0);
        // Large-glyph texture memory is allocated only as needed
        cacheTexture->allocatePixelBuffer();
    }
    if (!cacheTexture->mesh()) {
        cacheTexture->allocateMesh();
    }
    //获取PixelBuffer指向的内存指针
    uint8_t* cacheBuffer = cacheTexture->getPixelBuffer()->map();
    uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
    int srcStride = glyph.rowBytes();
    //将glyph imagecopy到内存中
    // Copy the glyph image, taking the mask format into account
    switch (format) {
        case SkMask::kA8_Format: {
            uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
            uint32_t row = (startY - TEXTURE_BORDER_SIZE) * cacheWidth + startX
                    - TEXTURE_BORDER_SIZE;
            // write leading border line
            memset(&cacheBuffer[row], 0, glyph.fWidth + 2 * TEXTURE_BORDER_SIZE);
            // write glyph data
            if (mGammaTable) {
                for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += srcStride) {
                    row = cacheY * cacheWidth;
                    cacheBuffer[row + startX - TEXTURE_BORDER_SIZE] = 0;
                    for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
                        uint8_t tempCol = bitmapBuffer[bY + bX];
                        cacheBuffer[row + cacheX] = mGammaTable[tempCol];
                    }
                    cacheBuffer[row + endX + TEXTURE_BORDER_SIZE - 1] = 0;
                }
            } else {
                for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += srcStride) {
                    row = cacheY * cacheWidth;
                    memcpy(&cacheBuffer[row + startX], &bitmapBuffer[bY], glyph.fWidth);
                    cacheBuffer[row + startX - TEXTURE_BORDER_SIZE] = 0;
                    cacheBuffer[row + endX + TEXTURE_BORDER_SIZE - 1] = 0;
                }
            }
            // write trailing border line
            row = (endY + TEXTURE_BORDER_SIZE - 1) * cacheWidth + startX - TEXTURE_BORDER_SIZE;
            memset(&cacheBuffer[row], 0, glyph.fWidth + 2 * TEXTURE_BORDER_SIZE);
            break;
        }
    ... ...
}

CacheTexture

    PixelBuffer* mPixelBuffer = nullptr; //储存字形对应的像素信息
    Texture mTexture; //大纹理,储存所有的纹理信息
    uint32_t mWidth, mHeight; //纹理宽高
    GLenum mFormat;
    bool mLinearFiltering = false;
    bool mDirty = false;
    uint16_t mNumGlyphs = 0;
    TextureVertex* mMesh = nullptr;
    uint32_t mCurrentQuad = 0;
    uint32_t mMaxQuadCount;
    Caches& mCaches;
    CacheBlock* mCacheBlocks; //单个字形对应的宽高信息
    bool mHasUnpackRowLength;
    Rect mDirtyRect;
3. endPrecaching

endPrecaching的操作主要是纹理上传,以GpuPixelBuffer为例子:

void GpuPixelBuffer::upload(uint32_t x, uint32_t y, uint32_t width, uint32_t height, int offset) {
    // If the buffer is not mapped, unmap() will not bind it
    mCaches.pixelBufferState().bind(mBuffer);
    unmap();
    glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, mFormat,
            GL_UNSIGNED_BYTE, reinterpret_cast<void*>(offset));
    mCaches.pixelBufferState().unbind();
}

glTexSubImage2D函数

提供修改图像函数,因为修改一个纹理比重新创建一个纹理开销小得多,对于一些视频捕捉程序可以先将视频图像存储在更大
的初始图像中(该图像大小要求是2的次方,OGL 2.0后没有这个限制),创建一个渲染用的纹理, 然后反复调用
glTexSubImage2D(修改的图像区域不用是2的次方)函数从图像视频图像区域读取数据到渲染纹理图像中。渲染用的纹理图
像只需要创建一次即可。
4. render

根据glyph获取字形对应的bitmap的u1,u2,v1,v2坐标信息,储存在CacheTexture储存的mesh中。
渲染时,GlopBuilder时再去取这些信息。

void TextDrawFunctor::draw(CacheTexture& texture, bool linearFiltering) {
    ... ...
    GlopBuilder(renderer->renderState(), renderer->caches(), &glop)
            .setRoundRectClipState(bakedState->roundRectClipState)
            .setMeshTexturedIndexedQuads(texture.mesh(), texture.meshElementCount())
            .setFillTexturePaint(texture.getTexture(), textureFillFlags, paint, bakedState->alpha)
            .setTransform(bakedState->computedState.transform, transformFlags)
            .setModelViewIdentityEmptyBounds()
            .build();
    ... ...
}

TextDropShadowCache

Demo代码:

        mPaint.setAntiAlias(true);
        mPaint.setColor(Color.RED);
        mPaint.setStyle(Paint.Style.STROKE);//设置为空心
        mPaint.setStrokeWidth(1);
        mPaint.setTextSize(50);
        mPaint.setShadowLayer(10, 3, 3, 0xFFFF00FF);
        canvas.drawText("阴影", 200,500,mPaint);

调用堆栈:

BakedOpDispatcher::renderTextShadow
    TextDropShadowCache::get
        FontRenderer::renderDropShadow
            Font::measure
                Font::render //RenderMode = MEASURE
                    Font::measureCachedGlyph
            Font::render //RenderMode = BITMAP
            FontRenderer::blurImage
static void renderTextShadow(BakedOpRenderer& renderer,
        const TextOp& op, const BakedOpState& textOpState) {
    ... ...
    FontRenderer& fontRenderer = renderer.caches().fontRenderer.getFontRenderer();
    fontRenderer.setFont(op.paint, SkMatrix::I());
    renderer.caches().textureState().activateTexture(0);
    ... ...
    ShadowTexture* texture = renderer.caches().dropShadowCache.get(
            op.paint, op.glyphs, op.glyphCount, textShadow.radius, op.positions);

    ... ...
}
ShadowTexture* TextDropShadowCache::get(const SkPaint* paint, const glyph_t* glyphs, int numGlyphs,
        float radius, const float* positions) {
    ShadowText entry(paint, radius, numGlyphs, glyphs, positions);
    ShadowTexture* texture = mCache.get(entry);

    if (!texture) {
        SkPaint paintCopy(*paint);
        paintCopy.setTextAlign(SkPaint::kLeft_Align);
        FontRenderer::DropShadow shadow = mRenderer->renderDropShadow(&paintCopy, glyphs, numGlyphs,
                radius, positions);

        if (!shadow.image) {
            return nullptr;
        }

        Caches& caches = Caches::getInstance();
        //创建ShadowTexture
        texture = new ShadowTexture(caches);
        texture->left = shadow.penX;
        texture->top = shadow.penY;
        texture->generation = 0;
        texture->blend = true;

        const uint32_t size = shadow.width * shadow.height;

        // Don't even try to cache a bitmap that's bigger than the cache
        if (size < mMaxSize) {
            while (mSize + size > mMaxSize) {
                LOG_ALWAYS_FATAL_IF(!mCache.removeOldest(),
                        "Failed to remove oldest from cache. mSize = %"
                        PRIu32 ", mCache.size() = %zu", mSize, mCache.size());
            }
        }

        //上传ShadowTexture
        // Textures are Alpha8
        texture->upload(GL_ALPHA, shadow.width, shadow.height,
                GL_ALPHA, GL_UNSIGNED_BYTE, shadow.image);
        texture->setFilter(GL_LINEAR);
        texture->setWrap(GL_CLAMP_TO_EDGE);

        if (size < mMaxSize) {
            if (mDebugEnabled) {
                ALOGD("Shadow texture created, size = %d", texture->bitmapSize);
            }

            entry.copyTextLocally();

            mSize += texture->objectSize();
            mCache.put(entry, texture);
        } else {
            texture->cleanup = true;
        }

        // Cleanup shadow
        free(shadow.image);
    }

    return texture;
}
FontRenderer::DropShadow FontRenderer::renderDropShadow(const SkPaint* paint, const glyph_t *glyphs,
        int numGlyphs, float radius, const float* positions) {
    ... ...
    mCurrentFont->measure(paint, glyphs, numGlyphs, &bounds, positions);

    ... ...
    uint8_t* dataBuffer = (uint8_t*) malloc(size);

    memset(dataBuffer, 0, size);

    int penX = intRadius - bounds.left;
    int penY = intRadius - bounds.bottom;

    if ((bounds.right > bounds.left) && (bounds.top > bounds.bottom)) {
        // text has non-whitespace, so draw and blur to create the shadow
        // NOTE: bounds.isEmpty() can't be used here, since vertical coordinates are inverted
        // TODO: don't draw pure whitespace in the first place, and avoid needing this check
        //渲染字体到Bitmap,用于之后的模糊操作(类似于离屏渲染)
        mCurrentFont->render(paint, glyphs, numGlyphs, penX, penY,
                Font::BITMAP, dataBuffer, paddedWidth, paddedHeight, nullptr, positions);

        // Unbind any PBO we might have used
        Caches::getInstance().pixelBufferState().unbind();
        //创建模糊阴影
        blurImage(&dataBuffer, paddedWidth, paddedHeight, radius);
    }

    image.width = paddedWidth;
    image.height = paddedHeight;
    image.image = dataBuffer;
    image.penX = penX;
    image.penY = penY;

    return image;
}
void Font::measure(const SkPaint* paint, const glyph_t* glyphs,
        int numGlyphs, Rect *bounds, const float* positions) {
    if (bounds == nullptr) {
        ALOGE("No return rectangle provided to measure text");
        return;
    }
    bounds->set(1e6, -1e6, -1e6, 1e6);
    //第六个参数是RenderMode mode=MEASURE
    render(paint, glyphs, numGlyphs, 0, 0, MEASURE, nullptr, 0, 0, bounds, positions);
}

RenderMode的定义

    enum RenderMode {
        FRAMEBUFFER,
        BITMAP,
        MEASURE,
    };
  • FRAMEBUFFER 绘制字体到主屏,真正的绘制字体操作
  • BITMAP 将字体的cache copy到bitmap中,用于做blur操作
  • MEASURE 确定字体的属性
void Font::render(const SkPaint* paint, const glyph_t* glyphs,
        int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap,
        uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* positions) {
    //RenderMode对应的函数如下:
    static RenderGlyph gRenderGlyph[] = {
            &android::uirenderer::Font::drawCachedGlyph,
            &android::uirenderer::Font::drawCachedGlyphTransformed,
            &android::uirenderer::Font::drawCachedGlyphBitmap,
            &android::uirenderer::Font::drawCachedGlyphBitmap,
            &android::uirenderer::Font::measureCachedGlyph,
            &android::uirenderer::Font::measureCachedGlyph
    };
}

绘制阴影时,现在执行RenderMode = MEASURE的Render,确定字体的属性;再执行RenderMode = BITMAP的render,产生用于blur的字体。

字体模糊
执行字体模糊的函数是FontRenderer::blurImage,这个函数使用RenderScript进行模糊操作。

另外注意的一点是,当TextDropShadowCache的mMaxSize配置的太小时,会引起应用崩溃。

ShadowTexture* TextDropShadowCache::get(const SkPaint* paint, const glyph_t* glyphs, int numGlyphs,
        float radius, const float* positions) {
    if (size < mMaxSize) {
        while (mSize + size > mMaxSize) {
            LOG_ALWAYS_FATAL_IF(!mCache.removeOldest(),
                    "Failed to remove oldest from cache. mSize = %"
                    PRIu32 ", mCache.size() = %zu", mSize, mCache.size());
        }
    }
}

LayerCache

已经弃用

TextureCache

TextureCache的作用是缓存已经上传的图片,通过图片的fStableID获取已经上传过的Texture。
TextureCache使用Lru算法维护缓存的生命周期。

Texture* TextureCache::getCachedTexture(const SkBitmap* bitmap, AtlasUsageType atlasUsageType) {
    //如果使用的是Atlas中的资源,返回Atlas中的Texture
    if (CC_LIKELY(mAssetAtlas != nullptr) && atlasUsageType == AtlasUsageType::Use) {
        AssetAtlas::Entry* entry = mAssetAtlas->getEntry(bitmap->pixelRef());
        if (CC_UNLIKELY(entry)) {
            return entry->texture;
        }
    }
    //根据bitmap的fStableID获取对应的cache
    Texture* texture = mCache.get(bitmap->pixelRef()->getStableID());

    if (!texture) {
        //这个判断是:如果图片过大,不进行缓存和上传纹理操作
        if (!canMakeTextureFromBitmap(bitmap)) {
            return nullptr;
        }
        //判断是否超过了mMaxSize,超过了使用Lru策略清理旧的缓存
        const uint32_t size = bitmap->rowBytes() * bitmap->height();
        bool canCache = size < mMaxSize;
        // Don't even try to cache a bitmap that's bigger than the cache
        while (canCache && mSize + size > mMaxSize) {
            Texture* oldest = mCache.peekOldestValue();
            if (oldest && !oldest->isInUse) {
                mCache.removeOldest();
            } else {
                canCache = false;
            }
        }

        if (canCache) {
            //对于新的bitmap,获取fStableID,创建对应的Texture并上传纹理
            texture = new Texture(Caches::getInstance());
            texture->bitmapSize = size;
            texture->generation = bitmap->getGenerationID();
            texture->upload(*bitmap);

            mSize += size;
            TEXTURE_LOGD("TextureCache::get: create texture(%p): name, size, mSize = %d, %d, %d",
                     bitmap, texture->id, size, mSize);
            if (mDebugEnabled) {
                ALOGD("Texture created, size = %d", size);
            }
            mCache.put(bitmap->pixelRef()->getStableID(), texture);
        }
    } else if (!texture->isInUse && bitmap->getGenerationID() != texture->generation) {
        // Texture was in the cache but is dirty, re-upload
        // TODO: Re-adjust the cache size if the bitmap's dimensions have changed
        texture->upload(*bitmap);
        texture->generation = bitmap->getGenerationID();
    }

    return texture;
}

TessellationCache

demo代码:

        RectF r1=new RectF();                        //RectF对象
        r1.left=250;                                 //左边
        r1.top=250;                                  //上边
        r1.right=350;                                //右边
        r1.bottom=350;                               //下边
        canvas.drawRoundRect(r1,10,10,mPaint);

调用流程:

RecordingCanvas::drawRoundRect
     addOp(alloc().create_trivial<RoundRectOp>
        FrameBuilder::deferRoundRectOp
            TessellationCache::precacheRoundRect
                TessellationCache::getRoundRectBuffer(
                     TessellationCache::getOrCreateBuffer
                        TessellationProcessor::onProcess

FrameBuilder::defer3dChildren
    FrameBuilder::deferShadow
        TessellationCache::getShadowTask
            TessellationCache::precacheShadows
                ShadowProcessor::onProcess
                    TessellationCache::tessellateShadows
                        PathTessellator::approximatePathOutlineVertices
                            PathTessellator::approximatePathOutlineVertices //递归二次贝塞尔顶点
                        ShadowTessellator::tessellateAmbientShadow
                            AmbientShadow::createAmbientShadow //将阴影计算为三角形,阴影值为alpha
                        ShadowTessellator::tessellateSpotShadow
                            SpotShadow::createSpotShadow //从光中心计算每个多边形顶点的投影轮廓
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值