diff options
Diffstat (limited to 'libs/hwui')
| -rw-r--r-- | libs/hwui/FontRenderer.cpp | 24 | ||||
| -rw-r--r-- | libs/hwui/FontRenderer.h | 3 | ||||
| -rw-r--r-- | libs/hwui/Layer.h | 13 | ||||
| -rw-r--r-- | libs/hwui/LayerCache.cpp | 24 | ||||
| -rw-r--r-- | libs/hwui/LayerCache.h | 4 | ||||
| -rw-r--r-- | libs/hwui/OpenGLRenderer.cpp | 226 | ||||
| -rw-r--r-- | libs/hwui/OpenGLRenderer.h | 26 | ||||
| -rw-r--r-- | libs/hwui/ProgramCache.cpp | 5 | ||||
| -rw-r--r-- | libs/hwui/ProgramCache.h | 13 | ||||
| -rw-r--r-- | libs/hwui/Snapshot.h | 74 |
10 files changed, 234 insertions, 178 deletions
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp index 5d7f8bf..2959814 100644 --- a/libs/hwui/FontRenderer.cpp +++ b/libs/hwui/FontRenderer.cpp @@ -166,6 +166,8 @@ void Font::measureUTF(SkPaint* paint, const char* text, uint32_t start, uint32_t renderUTF(paint, text, start, len, numGlyphs, 0, 0, MEASURE, NULL, 0, 0, bounds); } +#define SkAutoKern_AdjustF(prev, next) (((next) - (prev) + 32) >> 6 << 16) + void Font::renderUTF(SkPaint* paint, const char* text, uint32_t start, uint32_t len, int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH,Rect *bounds) { @@ -173,12 +175,16 @@ void Font::renderUTF(SkPaint* paint, const char* text, uint32_t start, uint32_t return; } - int penX = x, penY = y; + SkFixed penX = SkIntToFixed(x); + int penY = y; int glyphsLeft = 1; if (numGlyphs > 0) { glyphsLeft = numGlyphs; } + SkFixed prevRsbDelta = 0; + penX += SK_Fixed1 / 2; + text += start; while (glyphsLeft > 0) { @@ -190,23 +196,25 @@ void Font::renderUTF(SkPaint* paint, const char* text, uint32_t start, uint32_t } CachedGlyphInfo* cachedGlyph = getCachedUTFChar(paint, utfChar); + penX += SkAutoKern_AdjustF(prevRsbDelta, cachedGlyph->mLsbDelta); + prevRsbDelta = cachedGlyph->mRsbDelta; // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage if (cachedGlyph->mIsValid) { switch(mode) { case FRAMEBUFFER: - drawCachedGlyph(cachedGlyph, penX, penY); + drawCachedGlyph(cachedGlyph, SkFixedFloor(penX), penY); break; case BITMAP: - drawCachedGlyph(cachedGlyph, penX, penY, bitmap, bitmapW, bitmapH); + drawCachedGlyph(cachedGlyph, SkFixedFloor(penX), penY, bitmap, bitmapW, bitmapH); break; case MEASURE: - measureCachedGlyph(cachedGlyph, penX, penY, bounds); + measureCachedGlyph(cachedGlyph, SkFixedFloor(penX), penY, bounds); break; } } - penX += SkFixedFloor(cachedGlyph->mAdvanceX); + penX += cachedGlyph->mAdvanceX; // If we were given a specific number of glyphs, decrement if (numGlyphs > 0) { @@ -220,6 +228,8 @@ void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyp glyph->mAdvanceY = skiaGlyph.fAdvanceY; glyph->mBitmapLeft = skiaGlyph.fLeft; glyph->mBitmapTop = skiaGlyph.fTop; + glyph->mLsbDelta = skiaGlyph.fLsbDelta; + glyph->mRsbDelta = skiaGlyph.fRsbDelta; uint32_t startX = 0; uint32_t startY = 0; @@ -352,7 +362,7 @@ void FontRenderer::flushAllAndInvalidate() { bool FontRenderer::cacheBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint32_t* retOriginY) { // If the glyph is too tall, don't cache it - if (glyph.fWidth > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) { + if (glyph.fHeight > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) { LOGE("Font size to large to fit in cache. width, height = %i, %i", (int) glyph.fWidth, (int) glyph.fHeight); return false; @@ -616,7 +626,7 @@ void FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) { const float maxPrecacheFontSize = 40.0f; bool isNewFont = currentNumFonts != mActiveFonts.size(); - if(isNewFont && fontSize <= maxPrecacheFontSize ){ + if (isNewFont && fontSize <= maxPrecacheFontSize) { precacheLatin(paint); } } diff --git a/libs/hwui/FontRenderer.h b/libs/hwui/FontRenderer.h index a03ea92..de5c019 100644 --- a/libs/hwui/FontRenderer.h +++ b/libs/hwui/FontRenderer.h @@ -94,6 +94,9 @@ protected: // Values below contain a glyph's origin in the bitmap int32_t mBitmapLeft; int32_t mBitmapTop; + // Auto-kerning + SkFixed mLsbDelta; + SkFixed mRsbDelta; }; Font(FontRenderer* state, uint32_t fontId, float fontSize); diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h index d4db782..c527038 100644 --- a/libs/hwui/Layer.h +++ b/libs/hwui/Layer.h @@ -63,28 +63,19 @@ struct LayerSize { */ struct Layer { /** - * Coordinates of the layer corresponding to this snapshot. - * Only set when the flag kFlagIsLayer is set. + * Coordinates of the layer. */ Rect layer; /** * Name of the texture used to render the layer. - * Only set when the flag kFlagIsLayer is set. */ GLuint texture; /** - * Name of the FBO used to render the layer. - * Only set when the flag kFlagIsLayer is set. - */ - GLuint fbo; - /** * Opacity of the layer. - * Only set when the flag kFlagIsLayer is set. */ - float alpha; + int alpha; /** * Blending mode of the layer. - * Only set when the flag kFlagIsLayer is set. */ SkXfermode::Mode mode; /** diff --git a/libs/hwui/LayerCache.cpp b/libs/hwui/LayerCache.cpp index a204778..1a18766 100644 --- a/libs/hwui/LayerCache.cpp +++ b/libs/hwui/LayerCache.cpp @@ -87,7 +87,6 @@ void LayerCache::deleteLayer(Layer* layer) { if (layer) { mSize -= layer->layer.getWidth() * layer->layer.getHeight() * 4; - glDeleteFramebuffers(1, &layer->fbo); glDeleteTextures(1, &layer->texture); delete layer; } @@ -99,7 +98,7 @@ void LayerCache::clear() { mCache.setOnEntryRemovedListener(NULL); } -Layer* LayerCache::get(LayerSize& size, GLuint previousFbo) { +Layer* LayerCache::get(LayerSize& size) { Layer* layer = mCache.remove(size); if (layer) { LAYER_LOGD("Reusing layer"); @@ -111,10 +110,6 @@ Layer* LayerCache::get(LayerSize& size, GLuint previousFbo) { layer = new Layer; layer->blend = true; - // Generate the FBO and attach the texture - glGenFramebuffers(1, &layer->fbo); - glBindFramebuffer(GL_FRAMEBUFFER, layer->fbo); - // Generate the texture in which the FBO will draw glGenTextures(1, &layer->texture); glBindTexture(GL_TEXTURE_2D, layer->texture); @@ -128,23 +123,6 @@ Layer* LayerCache::get(LayerSize& size, GLuint previousFbo) { glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size.width, size.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); - - // Bind texture to FBO - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, - layer->texture, 0); - - GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); - if (status != GL_FRAMEBUFFER_COMPLETE) { - LOGE("Framebuffer incomplete (GL error code 0x%x)", status); - - glBindFramebuffer(GL_FRAMEBUFFER, previousFbo); - - glDeleteFramebuffers(1, &layer->fbo); - glDeleteTextures(1, &layer->texture); - delete layer; - - return NULL; - } } return layer; diff --git a/libs/hwui/LayerCache.h b/libs/hwui/LayerCache.h index 9860994..c0c7542 100644 --- a/libs/hwui/LayerCache.h +++ b/libs/hwui/LayerCache.h @@ -62,10 +62,8 @@ public: * size of the cache goes down. * * @param size The dimensions of the desired layer - * @param previousFbo The name of the FBO to bind to if creating a new - * layer fails */ - Layer* get(LayerSize& size, GLuint previousFbo); + Layer* get(LayerSize& size); /** * Adds the layer to the cache. The layer will not be added if there is * not enough space available. diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp index 6c90704..b1f5f6b 100644 --- a/libs/hwui/OpenGLRenderer.cpp +++ b/libs/hwui/OpenGLRenderer.cpp @@ -80,6 +80,24 @@ static const Blender gBlends[] = { { SkXfermode::kXor_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA } }; +// This array contains the swapped version of each SkXfermode. For instance +// this array's SrcOver blending mode is actually DstOver. You can refer to +// createLayer() for more information on the purpose of this array. +static const Blender gBlendsSwap[] = { + { SkXfermode::kClear_Mode, GL_ZERO, GL_ZERO }, + { SkXfermode::kSrc_Mode, GL_ZERO, GL_ONE }, + { SkXfermode::kDst_Mode, GL_ONE, GL_ZERO }, + { SkXfermode::kSrcOver_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ONE }, + { SkXfermode::kDstOver_Mode, GL_ONE, GL_ONE_MINUS_SRC_ALPHA }, + { SkXfermode::kSrcIn_Mode, GL_ZERO, GL_SRC_ALPHA }, + { SkXfermode::kDstIn_Mode, GL_DST_ALPHA, GL_ZERO }, + { SkXfermode::kSrcOut_Mode, GL_ZERO, GL_ONE_MINUS_SRC_ALPHA }, + { SkXfermode::kDstOut_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ZERO }, + { SkXfermode::kSrcATop_Mode, GL_ONE_MINUS_DST_ALPHA, GL_SRC_ALPHA }, + { SkXfermode::kDstATop_Mode, GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA }, + { SkXfermode::kXor_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA } +}; + static const GLenum gTextureUnits[] = { GL_TEXTURE0, GL_TEXTURE1, @@ -122,8 +140,6 @@ void OpenGLRenderer::setViewport(int width, int height) { mWidth = width; mHeight = height; - mFirstSnapshot->height = height; - mFirstSnapshot->viewport.set(0, 0, width, height); } void OpenGLRenderer::prepare() { @@ -155,14 +171,19 @@ void OpenGLRenderer::acquireContext() { } void OpenGLRenderer::releaseContext() { - glViewport(0, 0, mSnapshot->viewport.getWidth(), mSnapshot->viewport.getHeight()); + glViewport(0, 0, mWidth, mHeight); glEnable(GL_SCISSOR_TEST); setScissorFromClip(); + glDisable(GL_DITHER); + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + if (mCaches.blend) { glEnable(GL_BLEND); glBlendFunc(mCaches.lastSrcMode, mCaches.lastDstMode); + glBlendEquation(GL_FUNC_ADD); } else { glDisable(GL_BLEND); } @@ -202,17 +223,10 @@ int OpenGLRenderer::saveSnapshot(int flags) { bool OpenGLRenderer::restoreSnapshot() { bool restoreClip = mSnapshot->flags & Snapshot::kFlagClipSet; bool restoreLayer = mSnapshot->flags & Snapshot::kFlagIsLayer; - bool restoreOrtho = mSnapshot->flags & Snapshot::kFlagDirtyOrtho; sp<Snapshot> current = mSnapshot; sp<Snapshot> previous = mSnapshot->previous; - if (restoreOrtho) { - Rect& r = previous->viewport; - glViewport(r.left, r.top, r.right, r.bottom); - mOrthoMatrix.load(current->orthoMatrix); - } - mSaveCount--; mSnapshot = previous; @@ -253,11 +267,7 @@ int OpenGLRenderer::saveLayer(float left, float top, float right, float bottom, mode = SkXfermode::kSrcOver_Mode; } - if (alpha > 0 && !mSnapshot->invisible) { - createLayer(mSnapshot, left, top, right, bottom, alpha, mode, flags); - } else { - mSnapshot->invisible = true; - } + createLayer(mSnapshot, left, top, right, bottom, alpha, mode, flags); return count; } @@ -273,79 +283,123 @@ int OpenGLRenderer::saveLayerAlpha(float left, float top, float right, float bot } } +/** + * Layers are viewed by Skia are slightly different than layers in image editing + * programs (for instance.) When a layer is created, previously created layers + * and the frame buffer still receive every drawing command. For instance, if a + * layer is created and a shape intersecting the bounds of the layers and the + * framebuffer is draw, the shape will be drawn on both (unless the layer was + * created with the SkCanvas::kClipToLayer_SaveFlag flag.) + * + * A way to implement layers is to create an FBO for each layer, backed by an RGBA + * texture. Unfortunately, this is inefficient as it requires every primitive to + * be drawn n + 1 times, where n is the number of active layers. In practice this + * means, for every primitive: + * - Switch active frame buffer + * - Change viewport, clip and projection matrix + * - Issue the drawing + * + * Switching rendering target n + 1 times per drawn primitive is extremely costly. + * To avoid this, layers are implemented in a different way here. + * + * This implementation relies on the frame buffer being at least RGBA 8888. When + * a layer is created, only a texture is created, not an FBO. The content of the + * frame buffer contained within the layer's bounds is copied into this texture + * using glCopyTexImage2D(). The layer's region is then cleared(1) in the frame + * buffer and drawing continues as normal. This technique therefore treats the + * frame buffer as a scratch buffer for the layers. + * + * To compose the layers back onto the frame buffer, each layer texture + * (containing the original frame buffer data) is drawn as a simple quad over + * the frame buffer. The trick is that the quad is set as the composition + * destination in the blending equation, and the frame buffer becomes the source + * of the composition. + * + * Drawing layers with an alpha value requires an extra step before composition. + * An empty quad is drawn over the layer's region in the frame buffer. This quad + * is drawn with the rgba color (0,0,0,alpha). The alpha value offered by the + * quad is used to multiply the colors in the frame buffer. This is achieved by + * changing the GL blend functions for the GL_FUNC_ADD blend equation to + * GL_ZERO, GL_SRC_ALPHA. + * + * Because glCopyTexImage2D() can be slow, an alternative implementation might + * be use to draw a single clipped layer. The implementation described above + * is correct in every case. + * + * (1) The frame buffer is actually not cleared right away. To allow the GPU + * to potentially optimize series of calls to glCopyTexImage2D, the frame + * buffer is left untouched until the first drawing operation. Only when + * something actually gets drawn are the layers regions cleared. + */ bool OpenGLRenderer::createLayer(sp<Snapshot> snapshot, float left, float top, float right, float bottom, int alpha, SkXfermode::Mode mode,int flags) { LAYER_LOGD("Requesting layer %fx%f", right - left, bottom - top); LAYER_LOGD("Layer cache size = %d", mCaches.layerCache.getSize()); + // Window coordinates of the layer Rect bounds(left, top, right, bottom); - // TODO: Apply transformations and treat layers in screen coordinates - // mSnapshot->transform->mapRect(bounds); + mSnapshot->transform->mapRect(bounds); - GLuint previousFbo = snapshot->previous.get() ? snapshot->previous->fbo : 0; - LayerSize size(bounds.getWidth(), bounds.getHeight()); + // Layers only make sense if they are in the framebuffer's bounds + bounds.intersect(*mSnapshot->clipRect); + if (bounds.isEmpty()) return false; - Layer* layer = mCaches.layerCache.get(size, previousFbo); + LayerSize size(bounds.getWidth(), bounds.getHeight()); + Layer* layer = mCaches.layerCache.get(size); if (!layer) { return false; } - glBindFramebuffer(GL_FRAMEBUFFER, layer->fbo); - - // Clear the FBO - glDisable(GL_SCISSOR_TEST); - glClearColor(0.0f, 0.0f, 0.0f, 0.0f); - glClear(GL_COLOR_BUFFER_BIT); - glEnable(GL_SCISSOR_TEST); - layer->mode = mode; - layer->alpha = alpha / 255.0f; + layer->alpha = alpha; layer->layer.set(bounds); // Save the layer in the snapshot snapshot->flags |= Snapshot::kFlagIsLayer; snapshot->layer = layer; - snapshot->fbo = layer->fbo; - // TODO: Temporary until real layer support is implemented - snapshot->resetTransform(-bounds.left, -bounds.top, 0.0f); - // TODO: Temporary until real layer support is implemented - snapshot->resetClip(0.0f, 0.0f, bounds.getWidth(), bounds.getHeight()); - snapshot->viewport.set(0.0f, 0.0f, bounds.getWidth(), bounds.getHeight()); - snapshot->height = bounds.getHeight(); - snapshot->flags |= Snapshot::kFlagDirtyOrtho; - snapshot->orthoMatrix.load(mOrthoMatrix); - setScissorFromClip(); + // Copy the framebuffer into the layer + glBindTexture(GL_TEXTURE_2D, layer->texture); + glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bounds.left, mHeight - bounds.bottom, + bounds.getWidth(), bounds.getHeight(), 0); + + if (flags & SkCanvas::kClipToLayer_SaveFlag) { + if (mSnapshot->clipTransformed(bounds)) setScissorFromClip(); + } - // Change the ortho projection - glViewport(0, 0, bounds.getWidth(), bounds.getHeight()); - mOrthoMatrix.loadOrtho(0.0f, bounds.getWidth(), bounds.getHeight(), 0.0f, -1.0f, 1.0f); + // Enqueue the buffer coordinates to clear the corresponding region later + mLayers.push(new Rect(bounds)); return true; } +/** + * Read the documentation of createLayer() before doing anything in this method. + */ void OpenGLRenderer::composeLayer(sp<Snapshot> current, sp<Snapshot> previous) { if (!current->layer) { LOGE("Attempting to compose a layer that does not exist"); return; } - // Unbind current FBO and restore previous one - // Most of the time, previous->fbo will be 0 to bind the default buffer - glBindFramebuffer(GL_FRAMEBUFFER, previous->fbo); - // Restore the clip from the previous snapshot const Rect& clip = *previous->clipRect; - glScissor(clip.left, previous->height - clip.bottom, clip.getWidth(), clip.getHeight()); + glScissor(clip.left, mHeight - clip.bottom, clip.getWidth(), clip.getHeight()); Layer* layer = current->layer; const Rect& rect = layer->layer; - // FBOs are already drawn with a top-left origin, don't flip the texture + if (layer->alpha < 255) { + drawColorRect(rect.left, rect.top, rect.right, rect.bottom, + layer->alpha << 24, SkXfermode::kDstIn_Mode, true); + } + + // Layers are already drawn with a top-left origin, don't flip the texture resetDrawTextureTexCoords(0.0f, 1.0f, 1.0f, 0.0f); - drawTextureRect(rect.left, rect.top, rect.right, rect.bottom, - layer->texture, layer->alpha, layer->mode, layer->blend); + drawTextureMesh(rect.left, rect.top, rect.right, rect.bottom, layer->texture, + 1.0f, layer->mode, layer->blend, &mMeshVertices[0].position[0], + &mMeshVertices[0].texture[0], NULL, 0, true, true); resetDrawTextureTexCoords(0.0f, 0.0f, 1.0f, 1.0f); @@ -355,13 +409,32 @@ void OpenGLRenderer::composeLayer(sp<Snapshot> current, sp<Snapshot> previous) { if (!mCaches.layerCache.put(size, layer)) { LAYER_LOGD("Deleting layer"); - glDeleteFramebuffers(1, &layer->fbo); glDeleteTextures(1, &layer->texture); delete layer; } } +void OpenGLRenderer::clearLayerRegions() { + if (mLayers.size() == 0) return; + + for (uint32_t i = 0; i < mLayers.size(); i++) { + Rect* bounds = mLayers.itemAt(i); + + // Clear the framebuffer where the layer will draw + glScissor(bounds->left, mHeight - bounds->bottom, + bounds->getWidth(), bounds->getHeight()); + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + glClear(GL_COLOR_BUFFER_BIT); + + delete bounds; + } + mLayers.clear(); + + // Restore the clip + setScissorFromClip(); +} + /////////////////////////////////////////////////////////////////////////////// // Transforms /////////////////////////////////////////////////////////////////////////////// @@ -397,7 +470,7 @@ void OpenGLRenderer::concatMatrix(SkMatrix* matrix) { void OpenGLRenderer::setScissorFromClip() { const Rect& clip = *mSnapshot->clipRect; - glScissor(clip.left, mSnapshot->height - clip.bottom, clip.getWidth(), clip.getHeight()); + glScissor(clip.left, mHeight - clip.bottom, clip.getWidth(), clip.getHeight()); } const Rect& OpenGLRenderer::getClipBounds() { @@ -405,8 +478,6 @@ const Rect& OpenGLRenderer::getClipBounds() { } bool OpenGLRenderer::quickReject(float left, float top, float right, float bottom) { - if (mSnapshot->invisible) return true; - Rect r(left, top, right, bottom); mSnapshot->transform->mapRect(r); return !mSnapshot->clipRect->intersects(r); @@ -503,6 +574,7 @@ void OpenGLRenderer::drawPatch(SkBitmap* bitmap, Res_png_9patch* patch, Patch* mesh = mCaches.patchCache.get(patch); mesh->updateVertices(bitmap, left, top, right, bottom, &patch->xDivs[0], &patch->yDivs[0], patch->numXDivs, patch->numYDivs); + mesh->dump(); // Specify right and bottom as +1.0f from left/top to prevent scaling since the // patch mesh already defines the final size @@ -512,7 +584,6 @@ void OpenGLRenderer::drawPatch(SkBitmap* bitmap, Res_png_9patch* patch, } void OpenGLRenderer::drawColor(int color, SkXfermode::Mode mode) { - if (mSnapshot->invisible) return; const Rect& clip = *mSnapshot->clipRect; drawColorRect(clip.left, clip.top, clip.right, clip.bottom, color, mode, true); } @@ -545,18 +616,10 @@ void OpenGLRenderer::drawRect(float left, float top, float right, float bottom, void OpenGLRenderer::drawText(const char* text, int bytesCount, int count, float x, float y, SkPaint* paint) { - if (mSnapshot->invisible || text == NULL || count == 0 || - (paint->getAlpha() == 0 && paint->getXfermode() == NULL)) { + if (text == NULL || count == 0 || (paint->getAlpha() == 0 && paint->getXfermode() == NULL)) { return; } - - float scaleX = paint->getTextScaleX(); - bool applyScaleX = scaleX < 0.9999f || scaleX > 1.0001f; - if (applyScaleX) { - save(SkCanvas::kMatrix_SaveFlag); - translate(x - (x * scaleX), 0.0f); - scale(scaleX, 1.0f); - } + paint->setAntiAlias(true); float length = -1.0f; switch (paint->getTextAlign()) { @@ -606,21 +669,16 @@ void OpenGLRenderer::drawText(const char* text, int bytesCount, int count, mode, false, true); const Rect& clip = mSnapshot->getLocalClip(); + clearLayerRegions(); fontRenderer.renderText(paint, &clip, text, 0, bytesCount, count, x, y); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); glDisableVertexAttribArray(mCaches.currentProgram->getAttrib("texCoords")); drawTextDecorations(text, bytesCount, length, x, y, paint); - - if (applyScaleX) { - restore(); - } } void OpenGLRenderer::drawPath(SkPath* path, SkPaint* paint) { - if (mSnapshot->invisible) return; - GLuint textureUnit = 0; glActiveTexture(gTextureUnits[textureUnit]); @@ -647,6 +705,8 @@ void OpenGLRenderer::drawPath(SkPath* path, SkPaint* paint) { setupTextureAlpha8(texture, textureUnit, x, y, r, g, b, a, mode, true, true); + clearLayerRegions(); + // Draw the mesh glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount); glDisableVertexAttribArray(mCaches.currentProgram->getAttrib("texCoords")); @@ -778,6 +838,7 @@ void OpenGLRenderer::setupTextureAlpha8(GLuint texture, uint32_t width, uint32_t } } +// Same values used by Skia #define kStdStrikeThru_Offset (-6.0f / 21.0f) #define kStdUnderline_Offset (1.0f / 9.0f) #define kStdUnderline_Thickness (1.0f / 18.0f) @@ -831,6 +892,8 @@ void OpenGLRenderer::drawTextDecorations(const char* text, int bytesCount, float void OpenGLRenderer::drawColorRect(float left, float top, float right, float bottom, int color, SkXfermode::Mode mode, bool ignoreTransform) { + clearLayerRegions(); + // If a shader is set, preserve only the alpha if (mShader) { color |= 0x00ffffff; @@ -905,7 +968,10 @@ void OpenGLRenderer::drawTextureRect(float left, float top, float right, float b void OpenGLRenderer::drawTextureMesh(float left, float top, float right, float bottom, GLuint texture, float alpha, SkXfermode::Mode mode, bool blend, - GLvoid* vertices, GLvoid* texCoords, GLvoid* indices, GLsizei elementsCount) { + GLvoid* vertices, GLvoid* texCoords, GLvoid* indices, GLsizei elementsCount, + bool swapSrcDst, bool ignoreTransform) { + clearLayerRegions(); + ProgramDescription description; description.hasTexture = true; if (mColorFilter) { @@ -915,10 +981,15 @@ void OpenGLRenderer::drawTextureMesh(float left, float top, float right, float b mModelView.loadTranslate(left, top, 0.0f); mModelView.scale(right - left, bottom - top, 1.0f); - chooseBlending(blend || alpha < 1.0f, mode, description); + chooseBlending(blend || alpha < 1.0f, mode, description, swapSrcDst); useProgram(mCaches.programCache.get(description)); - mCaches.currentProgram->set(mOrthoMatrix, mModelView, *mSnapshot->transform); + if (!ignoreTransform) { + mCaches.currentProgram->set(mOrthoMatrix, mModelView, *mSnapshot->transform); + } else { + mat4 m; + mCaches.currentProgram->set(mOrthoMatrix, mModelView, m); + } // Texture bindTexture(texture, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, 0); @@ -948,7 +1019,7 @@ void OpenGLRenderer::drawTextureMesh(float left, float top, float right, float b } void OpenGLRenderer::chooseBlending(bool blend, SkXfermode::Mode mode, - ProgramDescription& description) { + ProgramDescription& description, bool swapSrcDst) { blend = blend || mode != SkXfermode::kSrcOver_Mode; if (blend) { if (mode < SkXfermode::kPlus_Mode) { @@ -956,8 +1027,8 @@ void OpenGLRenderer::chooseBlending(bool blend, SkXfermode::Mode mode, glEnable(GL_BLEND); } - GLenum sourceMode = gBlends[mode].src; - GLenum destMode = gBlends[mode].dst; + GLenum sourceMode = swapSrcDst ? gBlendsSwap[mode].src : gBlends[mode].src; + GLenum destMode = swapSrcDst ? gBlendsSwap[mode].dst : gBlends[mode].dst; if (sourceMode != mCaches.lastSrcMode || destMode != mCaches.lastDstMode) { glBlendFunc(sourceMode, destMode); @@ -970,6 +1041,7 @@ void OpenGLRenderer::chooseBlending(bool blend, SkXfermode::Mode mode, // the blending, turn blending off here if (mExtensions.hasFramebufferFetch()) { description.framebufferMode = mode; + description.swapSrcDst = swapSrcDst; } if (mCaches.blend) { diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h index 50f42c2..3126754 100644 --- a/libs/hwui/OpenGLRenderer.h +++ b/libs/hwui/OpenGLRenderer.h @@ -29,6 +29,7 @@ #include <utils/RefBase.h> #include <utils/ResourceTypes.h> +#include <utils/Vector.h> #include "Extensions.h" #include "Matrix.h" @@ -156,6 +157,12 @@ private: int alpha, SkXfermode::Mode mode, int flags); /** + * Clears all the regions corresponding to the current list of layers. + * This method MUST be invoked before any drawing operation. + */ + void clearLayerRegions(); + + /** * Draws a colored rectangle with the specified color. The specified coordinates * are transformed by the current snapshot's transform matrix. * @@ -166,9 +173,10 @@ private: * @param color The rectangle's ARGB color, defined as a packed 32 bits word * @param mode The Skia xfermode to use * @param ignoreTransform True if the current transform should be ignored + * @paran ignoreBlending True if the blending is set by the caller */ void drawColorRect(float left, float top, float right, float bottom, - int color, SkXfermode::Mode mode, bool ignoreTransform = false); + int color, SkXfermode::Mode mode, bool ignoreTransform = false); /** * Draws a textured rectangle with the specified texture. The specified coordinates @@ -216,10 +224,13 @@ private: * @param texCoords The texture coordinates of each vertex * @param indices The indices of the vertices, can be NULL * @param elementsCount The number of elements in the mesh, required by indices + * @param swapSrcDst Whether or not the src and dst blending operations should be swapped + * @param ignoreTransform True if the current transform should be ignored */ void drawTextureMesh(float left, float top, float right, float bottom, GLuint texture, float alpha, SkXfermode::Mode mode, bool blend, - GLvoid* vertices, GLvoid* texCoords, GLvoid* indices, GLsizei elementsCount = 0); + GLvoid* vertices, GLvoid* texCoords, GLvoid* indices, GLsizei elementsCount = 0, + bool swapSrcDst = false, bool ignoreTransform = false); /** * Prepares the renderer to draw the specified shadow. @@ -322,8 +333,13 @@ private: * Enable or disable blending as necessary. This function sets the appropriate * blend function based on the specified xfermode. */ - inline void chooseBlending(bool blend, SkXfermode::Mode mode, ProgramDescription& description); + inline void chooseBlending(bool blend, SkXfermode::Mode mode, ProgramDescription& description, + bool swapSrcDst = false); + /** + * Safely retrieves the mode from the specified xfermode. If the specified + * xfermode is null, the mode is assumed to be SkXfermode::kSrcOver_Mode. + */ inline SkXfermode::Mode getXfermode(SkXfermode* mode); /** @@ -375,6 +391,10 @@ private: // Various caches Caches& mCaches; + + // List of rectangles to clear due to calls to saveLayer() + Vector<Rect*> mLayers; + }; // class OpenGLRenderer }; // namespace uirenderer diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp index ff65c1b..becbc22 100644 --- a/libs/hwui/ProgramCache.cpp +++ b/libs/hwui/ProgramCache.cpp @@ -119,6 +119,8 @@ const char* gFS_Main_FragColor = " gl_FragColor = fragColor;\n"; const char* gFS_Main_FragColor_Blend = " gl_FragColor = blendFramebuffer(fragColor, gl_LastFragColor);\n"; +const char* gFS_Main_FragColor_Blend_Swap = + " gl_FragColor = blendFramebuffer(gl_LastFragColor, fragColor);\n"; const char* gFS_Main_ApplyColorOp[4] = { // None "", @@ -376,7 +378,8 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti if (!blendFramebuffer) { shader.append(gFS_Main_FragColor); } else { - shader.append(gFS_Main_FragColor_Blend); + shader.append(!description.swapSrcDst ? + gFS_Main_FragColor_Blend : gFS_Main_FragColor_Blend_Swap); } } // End the shader diff --git a/libs/hwui/ProgramCache.h b/libs/hwui/ProgramCache.h index 8f5304d..0a17052 100644 --- a/libs/hwui/ProgramCache.h +++ b/libs/hwui/ProgramCache.h @@ -35,7 +35,7 @@ namespace uirenderer { /////////////////////////////////////////////////////////////////////////////// // Debug -#define DEBUG_PROGRAM_CACHE 1 +#define DEBUG_PROGRAM_CACHE 0 // Debug #if DEBUG_PROGRAM_CACHE @@ -44,6 +44,9 @@ namespace uirenderer { #define PROGRAM_LOGD(...) #endif +/* + * IMPORTANT: All 32 bits are used, switch to a long. + */ #define PROGRAM_KEY_TEXTURE 0x1 #define PROGRAM_KEY_A8_TEXTURE 0x2 #define PROGRAM_KEY_BITMAP 0x4 @@ -53,6 +56,7 @@ namespace uirenderer { #define PROGRAM_KEY_COLOR_LIGHTING 0x40 #define PROGRAM_KEY_COLOR_BLEND 0x80 #define PROGRAM_KEY_BITMAP_NPOT 0x100 +#define PROGRAM_KEY_SWAP_SRC_DST 0x2000 #define PROGRAM_KEY_BITMAP_WRAPS_MASK 0x600 #define PROGRAM_KEY_BITMAP_WRAPT_MASK 0x1800 @@ -70,6 +74,9 @@ namespace uirenderer { // Types /////////////////////////////////////////////////////////////////////////////// +/* + * IMPORTANT: All 32 bits are used, switch to a long. + */ typedef uint32_t programid; /////////////////////////////////////////////////////////////////////////////// @@ -95,7 +102,7 @@ struct ProgramDescription { shadersMode(SkXfermode::kClear_Mode), isBitmapFirst(false), bitmapWrapS(GL_CLAMP_TO_EDGE), bitmapWrapT(GL_CLAMP_TO_EDGE), colorOp(kColorNone), colorMode(SkXfermode::kClear_Mode), - framebufferMode(SkXfermode::kClear_Mode) { + framebufferMode(SkXfermode::kClear_Mode), swapSrcDst(false) { } // Texturing @@ -118,6 +125,7 @@ struct ProgramDescription { // Framebuffer blending (requires Extensions.hasFramebufferFetch()) // Ignored for all values < SkXfermode::kPlus_Mode SkXfermode::Mode framebufferMode; + bool swapSrcDst; inline uint32_t getEnumForWrap(GLenum wrap) const { switch (wrap) { @@ -163,6 +171,7 @@ struct ProgramDescription { break; } key |= (framebufferMode & PROGRAM_MAX_XFERMODE) << PROGRAM_XFERMODE_FRAMEBUFFER_SHIFT; + if (swapSrcDst) key |= PROGRAM_KEY_SWAP_SRC_DST; return key; } }; // struct ProgramDescription diff --git a/libs/hwui/Snapshot.h b/libs/hwui/Snapshot.h index da48243..062c986 100644 --- a/libs/hwui/Snapshot.h +++ b/libs/hwui/Snapshot.h @@ -43,7 +43,7 @@ namespace uirenderer { */ class Snapshot: public LightRefBase<Snapshot> { public: - Snapshot(): invisible(false), flags(0), previous(NULL), layer(NULL), fbo(0) { + Snapshot(): flags(0), previous(NULL), layer(NULL) { transform = &mTransformRoot; clipRect = &mClipRectRoot; } @@ -53,13 +53,7 @@ public: * the previous snapshot. */ Snapshot(const sp<Snapshot>& s, int saveFlags): - height(s->height), - invisible(s->invisible), - flags(0), - previous(s), - layer(NULL), - fbo(s->fbo), - viewport(s->viewport) { + flags(0), previous(s), layer(NULL) { if (saveFlags & SkCanvas::kMatrix_SaveFlag) { mTransformRoot.load(*s->transform); transform = &mTransformRoot; @@ -97,24 +91,36 @@ public: */ kFlagIsLayer = 0x2, /** - * Indicates that this snapshot has changed the ortho matrix. - */ - kFlagDirtyOrtho = 0x4, - /** * Indicates that the local clip should be recomputed. */ - kFlagDirtyLocalClip = 0x8, + kFlagDirtyLocalClip = 0x4, }; /** - * Intersects the current clip with the new clip rectangle. + * Modifies the current clip with the new clip rectangle and + * the specified operation. The specified rectangle is transformed + * by this snapshot's trasnformation. */ - bool clip(float left, float top, float right, float bottom, SkRegion::Op op) { - bool clipped = false; - + bool clip(float left, float top, float right, float bottom, + SkRegion::Op op = SkRegion::kIntersect_Op) { Rect r(left, top, right, bottom); transform->mapRect(r); + return clipTransformed(r, op); + } + /** + * Modifies the current clip with the new clip rectangle and + * the specified operation. The specified rectangle is considered + * already transformed. + */ + bool clipTransformed(const Rect& r, SkRegion::Op op = SkRegion::kIntersect_Op) { + bool clipped = false; + + // NOTE: The unimplemented operations require support for regions + // Supporting regions would require using a stencil buffer instead + // of the scissor. The stencil buffer itself is not too expensive + // (memory cost excluded) but on fillrate limited devices, managing + // the stencil might have a negative impact on the framerate. switch (op) { case SkRegion::kDifference_Op: break; @@ -162,29 +168,6 @@ public: return mLocalClip; } - // TODO: Temporary - void resetTransform(float x, float y, float z) { - transform = &mTransformRoot; - transform->loadTranslate(x, y, z); - } - - // TODO: Temporary - void resetClip(float left, float top, float right, float bottom) { - clipRect = &mClipRectRoot; - clipRect->set(left, top, right, bottom); - flags |= Snapshot::kFlagClipSet | Snapshot::kFlagDirtyLocalClip; - } - - /** - * Height of the framebuffer the snapshot is rendering into. - */ - int height; - - /** - * If true, the layer won't be rendered. - */ - bool invisible; - /** * Dirty flags. */ @@ -199,17 +182,6 @@ public: * Only set when the flag kFlagIsLayer is set. */ Layer* layer; - GLuint fbo; - - /** - * Current viewport. - */ - Rect viewport; - - /** - * Contains the previous ortho matrix. - */ - mat4 orthoMatrix; /** * Local transformation. Holds the current translation, scale and |
