2010-06-16 18:44:05 -07:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2010 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.
|
|
|
|
*/
|
|
|
|
|
2010-06-22 13:11:24 -07:00
|
|
|
#define LOG_TAG "OpenGLRenderer"
|
2010-06-16 18:44:05 -07:00
|
|
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
|
2010-06-26 00:13:53 -07:00
|
|
|
#include <SkCanvas.h>
|
2010-07-21 21:33:20 -07:00
|
|
|
#include <SkTypeface.h>
|
2010-06-27 22:59:20 -07:00
|
|
|
|
|
|
|
#include <utils/Log.h>
|
2010-09-24 18:39:22 -07:00
|
|
|
#include <utils/StopWatch.h>
|
2010-06-22 13:11:24 -07:00
|
|
|
|
2011-03-17 11:06:57 -07:00
|
|
|
#include <private/hwui/DrawGlInfo.h>
|
|
|
|
|
2010-10-27 18:57:51 -07:00
|
|
|
#include <ui/Rect.h>
|
|
|
|
|
2010-06-22 13:11:24 -07:00
|
|
|
#include "OpenGLRenderer.h"
|
2013-02-04 16:16:33 -08:00
|
|
|
#include "DeferredDisplayList.h"
|
2010-11-08 12:08:41 -08:00
|
|
|
#include "DisplayListRenderer.h"
|
2013-06-12 15:31:28 -07:00
|
|
|
#include "Fence.h"
|
2012-12-10 17:56:27 -08:00
|
|
|
#include "PathTessellator.h"
|
2012-09-24 11:37:12 -07:00
|
|
|
#include "Properties.h"
|
2010-12-08 18:34:42 -08:00
|
|
|
#include "Vector.h"
|
2010-06-16 18:44:05 -07:00
|
|
|
|
|
|
|
namespace android {
|
2010-06-24 19:30:36 -07:00
|
|
|
namespace uirenderer {
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Defines
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
2010-09-16 20:49:46 -07:00
|
|
|
#define RAD_TO_DEG (180.0f / 3.14159265f)
|
|
|
|
#define MIN_ANGLE 0.001f
|
|
|
|
|
2012-07-12 18:01:00 -07:00
|
|
|
#define ALPHA_THRESHOLD 0
|
2010-10-11 17:58:29 -07:00
|
|
|
|
2012-10-16 18:44:09 -07:00
|
|
|
#define FILTER(paint) (!paint || paint->isFilterBitmap() ? GL_LINEAR : GL_NEAREST)
|
2011-11-30 20:21:23 -08:00
|
|
|
|
2010-06-24 19:30:36 -07:00
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Globals
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
2010-07-29 14:37:42 -07:00
|
|
|
/**
|
|
|
|
* Structure mapping Skia xfermodes to OpenGL blending factors.
|
|
|
|
*/
|
|
|
|
struct Blender {
|
|
|
|
SkXfermode::Mode mode;
|
|
|
|
GLenum src;
|
|
|
|
GLenum dst;
|
|
|
|
}; // struct Blender
|
|
|
|
|
2010-06-28 17:12:22 -07:00
|
|
|
// In this array, the index of each Blender equals the value of the first
|
|
|
|
// entry. For instance, gBlends[1] == gBlends[SkXfermode::kSrc_Mode]
|
|
|
|
static const Blender gBlends[] = {
|
2011-09-08 15:33:03 -07:00
|
|
|
{ SkXfermode::kClear_Mode, GL_ZERO, GL_ONE_MINUS_SRC_ALPHA },
|
|
|
|
{ SkXfermode::kSrc_Mode, GL_ONE, GL_ZERO },
|
|
|
|
{ SkXfermode::kDst_Mode, GL_ZERO, GL_ONE },
|
|
|
|
{ SkXfermode::kSrcOver_Mode, GL_ONE, GL_ONE_MINUS_SRC_ALPHA },
|
|
|
|
{ SkXfermode::kDstOver_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ONE },
|
|
|
|
{ SkXfermode::kSrcIn_Mode, GL_DST_ALPHA, GL_ZERO },
|
|
|
|
{ SkXfermode::kDstIn_Mode, GL_ZERO, GL_SRC_ALPHA },
|
|
|
|
{ SkXfermode::kSrcOut_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ZERO },
|
|
|
|
{ SkXfermode::kDstOut_Mode, GL_ZERO, GL_ONE_MINUS_SRC_ALPHA },
|
|
|
|
{ SkXfermode::kSrcATop_Mode, GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA },
|
|
|
|
{ SkXfermode::kDstATop_Mode, GL_ONE_MINUS_DST_ALPHA, GL_SRC_ALPHA },
|
|
|
|
{ SkXfermode::kXor_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA },
|
|
|
|
{ SkXfermode::kPlus_Mode, GL_ONE, GL_ONE },
|
2013-02-04 15:42:26 -05:00
|
|
|
{ SkXfermode::kModulate_Mode, GL_ZERO, GL_SRC_COLOR },
|
2011-09-08 15:33:03 -07:00
|
|
|
{ SkXfermode::kScreen_Mode, GL_ONE, GL_ONE_MINUS_SRC_COLOR }
|
2010-06-28 17:12:22 -07:00
|
|
|
};
|
2010-06-16 18:44:05 -07:00
|
|
|
|
2010-09-13 18:11:21 -07:00
|
|
|
// 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.
|
2010-09-10 19:20:06 -07:00
|
|
|
static const Blender gBlendsSwap[] = {
|
2011-09-08 15:33:03 -07:00
|
|
|
{ SkXfermode::kClear_Mode, GL_ONE_MINUS_DST_ALPHA, 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 },
|
|
|
|
{ SkXfermode::kPlus_Mode, GL_ONE, GL_ONE },
|
2013-02-04 15:42:26 -05:00
|
|
|
{ SkXfermode::kModulate_Mode, GL_DST_COLOR, GL_ZERO },
|
2011-09-08 15:33:03 -07:00
|
|
|
{ SkXfermode::kScreen_Mode, GL_ONE_MINUS_DST_COLOR, GL_ONE }
|
2010-09-10 19:20:06 -07:00
|
|
|
};
|
|
|
|
|
2010-06-23 17:47:49 -07:00
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Constructors/destructor
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
2013-02-06 16:51:04 -08:00
|
|
|
OpenGLRenderer::OpenGLRenderer():
|
|
|
|
mCaches(Caches::getInstance()), mExtensions(Extensions::getInstance()) {
|
2013-03-04 10:19:31 -08:00
|
|
|
// *set* draw modifiers to be 0
|
|
|
|
memset(&mDrawModifiers, 0, sizeof(mDrawModifiers));
|
2013-03-29 10:59:59 -07:00
|
|
|
mDrawModifiers.mOverrideLayerAlpha = 1.0f;
|
2010-06-28 17:12:22 -07:00
|
|
|
|
2010-07-27 17:39:27 -07:00
|
|
|
memcpy(mMeshVertices, gMeshVertices, sizeof(gMeshVertices));
|
|
|
|
|
2010-07-29 18:48:04 -07:00
|
|
|
mFirstSnapshot = new Snapshot;
|
2013-03-26 15:05:58 -07:00
|
|
|
mFrameStarted = false;
|
2013-05-03 14:24:16 -07:00
|
|
|
mCountOverdraw = false;
|
2012-09-24 11:37:12 -07:00
|
|
|
|
|
|
|
mScissorOptimizationDisabled = false;
|
2010-06-16 18:44:05 -07:00
|
|
|
}
|
|
|
|
|
2010-06-22 13:11:24 -07:00
|
|
|
OpenGLRenderer::~OpenGLRenderer() {
|
2010-09-22 16:10:57 -07:00
|
|
|
// The context has already been destroyed at this point, do not call
|
|
|
|
// GL APIs. All GL state should be kept in Caches.h
|
2010-06-16 18:44:05 -07:00
|
|
|
}
|
|
|
|
|
2012-09-24 11:37:12 -07:00
|
|
|
void OpenGLRenderer::initProperties() {
|
|
|
|
char property[PROPERTY_VALUE_MAX];
|
|
|
|
if (property_get(PROPERTY_DISABLE_SCISSOR_OPTIMIZATION, property, "false")) {
|
|
|
|
mScissorOptimizationDisabled = !strcasecmp(property, "true");
|
|
|
|
INIT_LOGD(" Scissor optimization %s",
|
|
|
|
mScissorOptimizationDisabled ? "disabled" : "enabled");
|
|
|
|
} else {
|
|
|
|
INIT_LOGD(" Scissor optimization enabled");
|
|
|
|
}
|
2012-01-30 17:41:55 -08:00
|
|
|
}
|
|
|
|
|
2010-06-23 17:47:49 -07:00
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Setup
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
2013-01-31 19:07:29 -08:00
|
|
|
void OpenGLRenderer::setName(const char* name) {
|
|
|
|
if (name) {
|
|
|
|
mName.setTo(name);
|
|
|
|
} else {
|
|
|
|
mName.clear();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const char* OpenGLRenderer::getName() const {
|
|
|
|
return mName.string();
|
|
|
|
}
|
|
|
|
|
2012-05-15 11:10:01 -07:00
|
|
|
bool OpenGLRenderer::isDeferred() {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2010-06-22 13:11:24 -07:00
|
|
|
void OpenGLRenderer::setViewport(int width, int height) {
|
2012-09-18 15:40:58 -07:00
|
|
|
initViewport(width, height);
|
|
|
|
|
|
|
|
glDisable(GL_DITHER);
|
|
|
|
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
|
|
|
|
|
|
|
|
glEnableVertexAttribArray(Program::kBindingPosition);
|
|
|
|
}
|
|
|
|
|
|
|
|
void OpenGLRenderer::initViewport(int width, int height) {
|
2010-07-12 14:41:06 -07:00
|
|
|
mOrthoMatrix.loadOrtho(0, width, height, 0, -1, 1);
|
2010-06-22 18:56:38 -07:00
|
|
|
|
|
|
|
mWidth = width;
|
|
|
|
mHeight = height;
|
2010-10-05 18:14:38 -07:00
|
|
|
|
|
|
|
mFirstSnapshot->height = height;
|
|
|
|
mFirstSnapshot->viewport.set(0, 0, width, height);
|
2010-06-16 18:44:05 -07:00
|
|
|
}
|
|
|
|
|
2013-03-26 15:05:58 -07:00
|
|
|
void OpenGLRenderer::setupFrameState(float left, float top,
|
2013-01-29 17:26:25 -08:00
|
|
|
float right, float bottom, bool opaque) {
|
2010-11-11 15:36:56 -08:00
|
|
|
mCaches.clearGarbage();
|
|
|
|
|
2013-03-26 15:05:58 -07:00
|
|
|
mOpaque = opaque;
|
2010-09-01 15:13:49 -07:00
|
|
|
mSnapshot = new Snapshot(mFirstSnapshot,
|
|
|
|
SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
|
2011-03-02 15:43:44 -08:00
|
|
|
mSnapshot->fbo = getTargetFbo();
|
2010-08-17 18:38:51 -07:00
|
|
|
mSaveCount = 1;
|
2010-06-23 17:47:49 -07:00
|
|
|
|
2011-01-24 16:33:45 -08:00
|
|
|
mSnapshot->setClip(left, top, right, bottom);
|
2013-03-21 14:39:04 -07:00
|
|
|
mTilingClip.set(left, top, right, bottom);
|
2013-03-26 15:05:58 -07:00
|
|
|
}
|
2012-05-22 14:07:07 -07:00
|
|
|
|
2013-03-26 15:05:58 -07:00
|
|
|
status_t OpenGLRenderer::startFrame() {
|
|
|
|
if (mFrameStarted) return DrawGlInfo::kStatusDone;
|
|
|
|
mFrameStarted = true;
|
2012-09-21 00:39:43 -07:00
|
|
|
|
2013-03-26 15:05:58 -07:00
|
|
|
mDirtyClip = true;
|
|
|
|
|
|
|
|
discardFramebuffer(mTilingClip.left, mTilingClip.top, mTilingClip.right, mTilingClip.bottom);
|
2012-09-11 17:17:07 -07:00
|
|
|
|
2013-03-26 15:05:58 -07:00
|
|
|
glViewport(0, 0, mWidth, mHeight);
|
2011-01-24 16:33:45 -08:00
|
|
|
|
2012-09-27 17:55:46 -07:00
|
|
|
// Functors break the tiling extension in pretty spectacular ways
|
|
|
|
// This ensures we don't use tiling when a functor is going to be
|
|
|
|
// invoked during the frame
|
|
|
|
mSuppressTiling = mCaches.hasRegisteredFunctors();
|
|
|
|
|
2013-03-21 14:39:04 -07:00
|
|
|
startTiling(mSnapshot, true);
|
2012-09-19 17:25:38 -07:00
|
|
|
|
2012-09-21 19:15:00 -07:00
|
|
|
debugOverdraw(true, true);
|
|
|
|
|
2013-03-26 15:05:58 -07:00
|
|
|
return clear(mTilingClip.left, mTilingClip.top,
|
|
|
|
mTilingClip.right, mTilingClip.bottom, mOpaque);
|
|
|
|
}
|
|
|
|
|
|
|
|
status_t OpenGLRenderer::prepare(bool opaque) {
|
|
|
|
return prepareDirty(0.0f, 0.0f, mWidth, mHeight, opaque);
|
|
|
|
}
|
|
|
|
|
|
|
|
status_t OpenGLRenderer::prepareDirty(float left, float top,
|
|
|
|
float right, float bottom, bool opaque) {
|
2013-05-03 14:24:16 -07:00
|
|
|
|
2013-03-26 15:05:58 -07:00
|
|
|
setupFrameState(left, top, right, bottom, opaque);
|
|
|
|
|
|
|
|
// Layer renderers will start the frame immediately
|
|
|
|
// The framebuffer renderer will first defer the display list
|
|
|
|
// for each layer and wait until the first drawing command
|
|
|
|
// to start the frame
|
|
|
|
if (mSnapshot->fbo == 0) {
|
|
|
|
syncState();
|
|
|
|
updateLayers();
|
|
|
|
} else {
|
|
|
|
return startFrame();
|
|
|
|
}
|
|
|
|
|
|
|
|
return DrawGlInfo::kStatusDone;
|
2012-10-18 15:05:02 -07:00
|
|
|
}
|
|
|
|
|
2013-01-03 13:08:57 -08:00
|
|
|
void OpenGLRenderer::discardFramebuffer(float left, float top, float right, float bottom) {
|
|
|
|
// If we know that we are going to redraw the entire framebuffer,
|
|
|
|
// perform a discard to let the driver know we don't need to preserve
|
|
|
|
// the back buffer for this frame.
|
2013-02-06 16:51:04 -08:00
|
|
|
if (mExtensions.hasDiscardFramebuffer() &&
|
2013-01-03 13:08:57 -08:00
|
|
|
left <= 0.0f && top <= 0.0f && right >= mWidth && bottom >= mHeight) {
|
2013-01-31 17:20:30 -08:00
|
|
|
const bool isFbo = getTargetFbo() == 0;
|
|
|
|
const GLenum attachments[] = {
|
|
|
|
isFbo ? (const GLenum) GL_COLOR_EXT : (const GLenum) GL_COLOR_ATTACHMENT0,
|
|
|
|
isFbo ? (const GLenum) GL_STENCIL_EXT : (const GLenum) GL_STENCIL_ATTACHMENT };
|
2013-01-03 13:08:57 -08:00
|
|
|
glDiscardFramebufferEXT(GL_FRAMEBUFFER, 1, attachments);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-10-18 15:05:02 -07:00
|
|
|
status_t OpenGLRenderer::clear(float left, float top, float right, float bottom, bool opaque) {
|
2013-05-03 14:24:16 -07:00
|
|
|
if (!opaque || mCountOverdraw) {
|
2012-07-13 15:28:31 -07:00
|
|
|
mCaches.enableScissor();
|
2012-05-22 14:07:07 -07:00
|
|
|
mCaches.setScissor(left, mSnapshot->height - bottom, right - left, bottom - top);
|
2010-10-06 19:49:23 -07:00
|
|
|
glClear(GL_COLOR_BUFFER_BIT);
|
2012-06-06 19:03:58 -07:00
|
|
|
return DrawGlInfo::kStatusDrew;
|
2012-05-22 14:07:07 -07:00
|
|
|
}
|
2012-06-06 19:03:58 -07:00
|
|
|
|
2012-10-18 15:05:02 -07:00
|
|
|
mCaches.resetScissor();
|
2012-06-06 19:03:58 -07:00
|
|
|
return DrawGlInfo::kStatusDone;
|
2012-05-22 14:07:07 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void OpenGLRenderer::syncState() {
|
|
|
|
if (mCaches.blend) {
|
|
|
|
glEnable(GL_BLEND);
|
|
|
|
} else {
|
|
|
|
glDisable(GL_BLEND);
|
2010-10-06 19:49:23 -07:00
|
|
|
}
|
2010-06-22 18:56:38 -07:00
|
|
|
}
|
|
|
|
|
2012-09-20 17:38:46 -07:00
|
|
|
void OpenGLRenderer::startTiling(const sp<Snapshot>& s, bool opaque) {
|
2012-09-27 17:55:46 -07:00
|
|
|
if (!mSuppressTiling) {
|
2013-03-21 14:39:04 -07:00
|
|
|
Rect* clip = &mTilingClip;
|
2013-01-29 17:26:25 -08:00
|
|
|
if (s->flags & Snapshot::kFlagFboTarget) {
|
2013-03-21 14:39:04 -07:00
|
|
|
clip = &(s->layer->clipRect);
|
2012-09-27 17:55:46 -07:00
|
|
|
}
|
|
|
|
|
2013-01-29 17:26:25 -08:00
|
|
|
startTiling(*clip, s->height, opaque);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void OpenGLRenderer::startTiling(const Rect& clip, int windowHeight, bool opaque) {
|
|
|
|
if (!mSuppressTiling) {
|
|
|
|
mCaches.startTiling(clip.left, windowHeight - clip.bottom,
|
2013-02-14 18:03:37 -08:00
|
|
|
clip.right - clip.left, clip.bottom - clip.top, opaque);
|
2012-09-19 17:25:38 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void OpenGLRenderer::endTiling() {
|
2012-09-27 17:55:46 -07:00
|
|
|
if (!mSuppressTiling) mCaches.endTiling();
|
2012-09-19 17:25:38 -07:00
|
|
|
}
|
|
|
|
|
2010-09-16 14:16:48 -07:00
|
|
|
void OpenGLRenderer::finish() {
|
2012-09-21 19:15:00 -07:00
|
|
|
renderOverdraw();
|
2012-09-19 17:25:38 -07:00
|
|
|
endTiling();
|
|
|
|
|
2013-03-08 17:44:20 -08:00
|
|
|
// When finish() is invoked on FBO 0 we've reached the end
|
|
|
|
// of the current frame
|
|
|
|
if (getTargetFbo() == 0) {
|
|
|
|
mCaches.pathCache.trim();
|
|
|
|
}
|
|
|
|
|
2012-09-21 00:39:43 -07:00
|
|
|
if (!suppressErrorChecks()) {
|
2010-09-16 14:16:48 -07:00
|
|
|
#if DEBUG_OPENGL
|
2012-09-21 00:39:43 -07:00
|
|
|
GLenum status = GL_NO_ERROR;
|
|
|
|
while ((status = glGetError()) != GL_NO_ERROR) {
|
|
|
|
ALOGD("GL error from OpenGLRenderer: 0x%x", status);
|
|
|
|
switch (status) {
|
|
|
|
case GL_INVALID_ENUM:
|
|
|
|
ALOGE(" GL_INVALID_ENUM");
|
|
|
|
break;
|
|
|
|
case GL_INVALID_VALUE:
|
|
|
|
ALOGE(" GL_INVALID_VALUE");
|
|
|
|
break;
|
|
|
|
case GL_INVALID_OPERATION:
|
|
|
|
ALOGE(" GL_INVALID_OPERATION");
|
|
|
|
break;
|
|
|
|
case GL_OUT_OF_MEMORY:
|
|
|
|
ALOGE(" Out of memory!");
|
|
|
|
break;
|
|
|
|
}
|
2011-01-10 21:14:18 -08:00
|
|
|
}
|
2010-09-16 14:16:48 -07:00
|
|
|
#endif
|
2012-09-21 00:39:43 -07:00
|
|
|
|
2010-11-10 11:59:15 -08:00
|
|
|
#if DEBUG_MEMORY_USAGE
|
2010-11-10 19:01:29 -08:00
|
|
|
mCaches.dumpMemoryUsage();
|
2012-09-21 00:39:43 -07:00
|
|
|
#else
|
|
|
|
if (mCaches.getDebugLevel() & kDebugMemory) {
|
|
|
|
mCaches.dumpMemoryUsage();
|
|
|
|
}
|
2010-11-10 11:59:15 -08:00
|
|
|
#endif
|
2012-09-21 00:39:43 -07:00
|
|
|
}
|
2013-03-26 15:05:58 -07:00
|
|
|
|
2013-05-03 14:24:16 -07:00
|
|
|
if (mCountOverdraw) {
|
|
|
|
countOverdraw();
|
|
|
|
}
|
|
|
|
|
2013-03-26 15:05:58 -07:00
|
|
|
mFrameStarted = false;
|
2010-09-16 14:16:48 -07:00
|
|
|
}
|
|
|
|
|
2011-01-11 14:29:25 -08:00
|
|
|
void OpenGLRenderer::interrupt() {
|
2010-08-31 11:50:35 -07:00
|
|
|
if (mCaches.currentProgram) {
|
|
|
|
if (mCaches.currentProgram->isInUse()) {
|
|
|
|
mCaches.currentProgram->remove();
|
|
|
|
mCaches.currentProgram = NULL;
|
|
|
|
}
|
|
|
|
}
|
2010-10-19 11:42:22 -07:00
|
|
|
mCaches.unbindMeshBuffer();
|
2011-12-13 13:11:32 -08:00
|
|
|
mCaches.unbindIndicesBuffer();
|
2011-12-12 20:35:21 -08:00
|
|
|
mCaches.resetVertexPointers();
|
2013-02-13 18:39:43 -08:00
|
|
|
mCaches.disableTexCoordsVertexArray();
|
2012-09-21 19:15:00 -07:00
|
|
|
debugOverdraw(false, false);
|
2010-08-31 11:50:35 -07:00
|
|
|
}
|
|
|
|
|
2011-01-11 14:29:25 -08:00
|
|
|
void OpenGLRenderer::resume() {
|
2011-11-28 11:53:21 -08:00
|
|
|
sp<Snapshot> snapshot = (mSnapshot != NULL) ? mSnapshot : mFirstSnapshot;
|
|
|
|
glViewport(0, 0, snapshot->viewport.getWidth(), snapshot->viewport.getHeight());
|
2012-09-18 15:40:58 -07:00
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, snapshot->fbo);
|
2012-09-21 19:15:00 -07:00
|
|
|
debugOverdraw(true, false);
|
2012-09-18 15:40:58 -07:00
|
|
|
|
2011-12-12 16:47:48 -08:00
|
|
|
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
|
|
|
|
|
2012-08-15 13:46:54 -07:00
|
|
|
mCaches.scissorEnabled = glIsEnabled(GL_SCISSOR_TEST);
|
2012-07-13 15:28:31 -07:00
|
|
|
mCaches.enableScissor();
|
2012-01-03 14:13:39 -08:00
|
|
|
mCaches.resetScissor();
|
2010-10-26 16:27:31 -07:00
|
|
|
dirtyClip();
|
2010-08-31 11:50:35 -07:00
|
|
|
|
2011-12-13 14:55:06 -08:00
|
|
|
mCaches.activeTexture(0);
|
2013-06-04 18:00:09 -07:00
|
|
|
mCaches.resetBoundTextures();
|
2010-10-19 11:42:22 -07:00
|
|
|
|
|
|
|
mCaches.blend = true;
|
|
|
|
glEnable(GL_BLEND);
|
|
|
|
glBlendFunc(mCaches.lastSrcMode, mCaches.lastDstMode);
|
|
|
|
glBlendEquation(GL_FUNC_ADD);
|
2010-08-31 11:50:35 -07:00
|
|
|
}
|
|
|
|
|
2012-09-18 15:40:58 -07:00
|
|
|
void OpenGLRenderer::resumeAfterLayer() {
|
|
|
|
sp<Snapshot> snapshot = (mSnapshot != NULL) ? mSnapshot : mFirstSnapshot;
|
|
|
|
glViewport(0, 0, snapshot->viewport.getWidth(), snapshot->viewport.getHeight());
|
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, snapshot->fbo);
|
2012-09-21 19:15:00 -07:00
|
|
|
debugOverdraw(true, false);
|
2012-09-18 15:40:58 -07:00
|
|
|
|
|
|
|
mCaches.resetScissor();
|
|
|
|
dirtyClip();
|
|
|
|
}
|
|
|
|
|
2012-04-23 18:22:09 -07:00
|
|
|
void OpenGLRenderer::detachFunctor(Functor* functor) {
|
2012-06-06 13:59:33 -07:00
|
|
|
mFunctors.remove(functor);
|
2012-04-23 18:22:09 -07:00
|
|
|
}
|
2012-03-27 16:33:45 -07:00
|
|
|
|
2012-04-23 18:22:09 -07:00
|
|
|
void OpenGLRenderer::attachFunctor(Functor* functor) {
|
|
|
|
mFunctors.add(functor);
|
|
|
|
}
|
2012-03-27 16:33:45 -07:00
|
|
|
|
2012-04-23 18:22:09 -07:00
|
|
|
status_t OpenGLRenderer::invokeFunctors(Rect& dirty) {
|
|
|
|
status_t result = DrawGlInfo::kStatusDone;
|
2012-04-23 20:36:17 -07:00
|
|
|
size_t count = mFunctors.size();
|
2012-03-27 16:33:45 -07:00
|
|
|
|
2012-04-23 18:22:09 -07:00
|
|
|
if (count > 0) {
|
2012-11-28 14:45:04 -08:00
|
|
|
interrupt();
|
2012-04-23 18:22:09 -07:00
|
|
|
SortedVector<Functor*> functors(mFunctors);
|
|
|
|
mFunctors.clear();
|
|
|
|
|
|
|
|
DrawGlInfo info;
|
|
|
|
info.clipLeft = 0;
|
|
|
|
info.clipTop = 0;
|
|
|
|
info.clipRight = 0;
|
|
|
|
info.clipBottom = 0;
|
|
|
|
info.isLayer = false;
|
|
|
|
info.width = 0;
|
|
|
|
info.height = 0;
|
|
|
|
memset(info.transform, 0, sizeof(float) * 16);
|
|
|
|
|
|
|
|
for (size_t i = 0; i < count; i++) {
|
|
|
|
Functor* f = functors.itemAt(i);
|
|
|
|
result |= (*f)(DrawGlInfo::kModeProcess, &info);
|
|
|
|
|
2012-04-25 15:13:52 -07:00
|
|
|
if (result & DrawGlInfo::kStatusDraw) {
|
2012-04-23 18:22:09 -07:00
|
|
|
Rect localDirty(info.dirtyLeft, info.dirtyTop, info.dirtyRight, info.dirtyBottom);
|
|
|
|
dirty.unionWith(localDirty);
|
2012-04-25 15:13:52 -07:00
|
|
|
}
|
2012-04-23 18:22:09 -07:00
|
|
|
|
2012-04-25 15:13:52 -07:00
|
|
|
if (result & DrawGlInfo::kStatusInvoke) {
|
|
|
|
mFunctors.add(f);
|
2012-03-27 16:33:45 -07:00
|
|
|
}
|
|
|
|
}
|
2012-11-28 14:45:04 -08:00
|
|
|
resume();
|
2012-03-27 16:33:45 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
status_t OpenGLRenderer::callDrawGLFunction(Functor* functor, Rect& dirty) {
|
2013-03-26 18:55:15 -07:00
|
|
|
if (mSnapshot->isIgnored()) return DrawGlInfo::kStatusDone;
|
|
|
|
|
2011-01-10 14:10:36 -08:00
|
|
|
interrupt();
|
2012-06-06 13:59:33 -07:00
|
|
|
detachFunctor(functor);
|
2012-05-22 11:54:06 -07:00
|
|
|
|
2012-07-17 17:32:48 -07:00
|
|
|
mCaches.enableScissor();
|
2011-01-25 22:53:24 -08:00
|
|
|
if (mDirtyClip) {
|
|
|
|
setScissorFromClip();
|
|
|
|
}
|
2011-03-01 14:55:21 -08:00
|
|
|
|
2011-03-16 15:30:12 -07:00
|
|
|
Rect clip(*mSnapshot->clipRect);
|
|
|
|
clip.snapToPixelBoundaries();
|
|
|
|
|
2011-03-01 14:55:21 -08:00
|
|
|
// Since we don't know what the functor will draw, let's dirty
|
|
|
|
// tne entire clip region
|
|
|
|
if (hasLayer()) {
|
|
|
|
dirtyLayerUnchecked(clip, getRegion());
|
|
|
|
}
|
|
|
|
|
2011-03-17 11:06:57 -07:00
|
|
|
DrawGlInfo info;
|
|
|
|
info.clipLeft = clip.left;
|
|
|
|
info.clipTop = clip.top;
|
|
|
|
info.clipRight = clip.right;
|
|
|
|
info.clipBottom = clip.bottom;
|
|
|
|
info.isLayer = hasLayer();
|
2012-04-11 14:32:02 -07:00
|
|
|
info.width = getSnapshot()->viewport.getWidth();
|
|
|
|
info.height = getSnapshot()->height;
|
2011-03-17 11:06:57 -07:00
|
|
|
getSnapshot()->transform->copyTo(&info.transform[0]);
|
2011-03-16 15:30:12 -07:00
|
|
|
|
2013-04-16 13:50:16 -07:00
|
|
|
status_t result = (*functor)(DrawGlInfo::kModeDraw, &info);
|
2011-03-07 18:06:46 -08:00
|
|
|
|
2012-03-27 16:33:45 -07:00
|
|
|
if (result != DrawGlInfo::kStatusDone) {
|
2011-03-17 11:06:57 -07:00
|
|
|
Rect localDirty(info.dirtyLeft, info.dirtyTop, info.dirtyRight, info.dirtyBottom);
|
2011-03-07 18:06:46 -08:00
|
|
|
dirty.unionWith(localDirty);
|
2012-03-27 16:33:45 -07:00
|
|
|
|
2012-04-05 17:52:11 -07:00
|
|
|
if (result & DrawGlInfo::kStatusInvoke) {
|
2012-04-23 18:22:09 -07:00
|
|
|
mFunctors.add(functor);
|
2012-03-27 16:33:45 -07:00
|
|
|
}
|
2011-03-07 18:06:46 -08:00
|
|
|
}
|
|
|
|
|
2011-01-10 14:10:36 -08:00
|
|
|
resume();
|
2013-04-16 13:50:16 -07:00
|
|
|
return result | DrawGlInfo::kStatusDrew;
|
2011-01-10 14:10:36 -08:00
|
|
|
}
|
|
|
|
|
2012-09-24 11:37:12 -07:00
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Debug
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
2013-03-01 14:31:04 -08:00
|
|
|
void OpenGLRenderer::eventMark(const char* name) const {
|
|
|
|
mCaches.eventMark(0, name);
|
|
|
|
}
|
|
|
|
|
2012-09-24 11:37:12 -07:00
|
|
|
void OpenGLRenderer::startMark(const char* name) const {
|
|
|
|
mCaches.startMark(0, name);
|
|
|
|
}
|
|
|
|
|
|
|
|
void OpenGLRenderer::endMark() const {
|
|
|
|
mCaches.endMark();
|
|
|
|
}
|
|
|
|
|
|
|
|
void OpenGLRenderer::debugOverdraw(bool enable, bool clear) {
|
|
|
|
if (mCaches.debugOverdraw && getTargetFbo() == 0) {
|
|
|
|
if (clear) {
|
|
|
|
mCaches.disableScissor();
|
|
|
|
mCaches.stencil.clear();
|
|
|
|
}
|
|
|
|
if (enable) {
|
|
|
|
mCaches.stencil.enableDebugWrite();
|
|
|
|
} else {
|
|
|
|
mCaches.stencil.disable();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void OpenGLRenderer::renderOverdraw() {
|
|
|
|
if (mCaches.debugOverdraw && getTargetFbo() == 0) {
|
2013-03-21 14:39:04 -07:00
|
|
|
const Rect* clip = &mTilingClip;
|
2012-09-24 11:37:12 -07:00
|
|
|
|
|
|
|
mCaches.enableScissor();
|
2013-03-21 14:39:04 -07:00
|
|
|
mCaches.setScissor(clip->left, mFirstSnapshot->height - clip->bottom,
|
2012-09-24 11:37:12 -07:00
|
|
|
clip->right - clip->left, clip->bottom - clip->top);
|
|
|
|
|
|
|
|
mCaches.stencil.enableDebugTest(2);
|
|
|
|
drawColor(0x2f0000ff, SkXfermode::kSrcOver_Mode);
|
|
|
|
mCaches.stencil.enableDebugTest(3);
|
|
|
|
drawColor(0x2f00ff00, SkXfermode::kSrcOver_Mode);
|
|
|
|
mCaches.stencil.enableDebugTest(4);
|
|
|
|
drawColor(0x3fff0000, SkXfermode::kSrcOver_Mode);
|
|
|
|
mCaches.stencil.enableDebugTest(4, true);
|
|
|
|
drawColor(0x7fff0000, SkXfermode::kSrcOver_Mode);
|
|
|
|
mCaches.stencil.disable();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-05-03 14:24:16 -07:00
|
|
|
void OpenGLRenderer::countOverdraw() {
|
|
|
|
size_t count = mWidth * mHeight;
|
|
|
|
uint32_t* buffer = new uint32_t[count];
|
|
|
|
glReadPixels(0, 0, mWidth, mHeight, GL_RGBA, GL_UNSIGNED_BYTE, &buffer[0]);
|
|
|
|
|
|
|
|
size_t total = 0;
|
|
|
|
for (size_t i = 0; i < count; i++) {
|
|
|
|
total += buffer[i] & 0xff;
|
|
|
|
}
|
|
|
|
|
|
|
|
mOverdraw = total / float(count);
|
|
|
|
|
|
|
|
delete[] buffer;
|
|
|
|
}
|
|
|
|
|
2012-09-21 00:39:43 -07:00
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Layers
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
bool OpenGLRenderer::updateLayer(Layer* layer, bool inFrame) {
|
2013-03-26 15:05:58 -07:00
|
|
|
if (layer->deferredUpdateScheduled && layer->renderer &&
|
|
|
|
layer->displayList && layer->displayList->isRenderable()) {
|
2013-06-12 15:31:28 -07:00
|
|
|
ATRACE_CALL();
|
|
|
|
|
2012-09-21 00:39:43 -07:00
|
|
|
Rect& dirty = layer->dirtyRect;
|
|
|
|
|
2012-09-21 19:15:00 -07:00
|
|
|
if (inFrame) {
|
|
|
|
endTiling();
|
|
|
|
debugOverdraw(false, false);
|
|
|
|
}
|
2012-09-21 00:39:43 -07:00
|
|
|
|
2013-03-26 15:05:58 -07:00
|
|
|
if (CC_UNLIKELY(inFrame || mCaches.drawDeferDisabled)) {
|
2013-03-29 12:37:16 -07:00
|
|
|
layer->render();
|
2013-03-26 15:05:58 -07:00
|
|
|
} else {
|
|
|
|
layer->defer();
|
|
|
|
}
|
2012-09-21 00:39:43 -07:00
|
|
|
|
|
|
|
if (inFrame) {
|
|
|
|
resumeAfterLayer();
|
|
|
|
startTiling(mSnapshot);
|
|
|
|
}
|
|
|
|
|
2012-11-29 17:52:58 -08:00
|
|
|
layer->debugDrawUpdate = mCaches.debugLayersUpdates;
|
2013-04-15 16:08:28 -07:00
|
|
|
layer->hasDrawnSinceUpdate = false;
|
2012-09-21 00:39:43 -07:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void OpenGLRenderer::updateLayers() {
|
2013-03-26 15:05:58 -07:00
|
|
|
// If draw deferring is enabled this method will simply defer
|
|
|
|
// the display list of each individual layer. The layers remain
|
|
|
|
// in the layer updates list which will be cleared by flushLayers().
|
2012-09-21 00:39:43 -07:00
|
|
|
int count = mLayerUpdates.size();
|
|
|
|
if (count > 0) {
|
2013-03-26 15:05:58 -07:00
|
|
|
if (CC_UNLIKELY(mCaches.drawDeferDisabled)) {
|
|
|
|
startMark("Layer Updates");
|
|
|
|
} else {
|
|
|
|
startMark("Defer Layer Updates");
|
|
|
|
}
|
2012-09-21 00:39:43 -07:00
|
|
|
|
2013-04-04 14:46:24 -07:00
|
|
|
// Note: it is very important to update the layers in order
|
|
|
|
for (int i = 0; i < count; i++) {
|
2012-09-21 00:39:43 -07:00
|
|
|
Layer* layer = mLayerUpdates.itemAt(i);
|
|
|
|
updateLayer(layer, false);
|
2013-03-26 15:05:58 -07:00
|
|
|
if (CC_UNLIKELY(mCaches.drawDeferDisabled)) {
|
|
|
|
mCaches.resourceCache.decrementRefcount(layer);
|
|
|
|
}
|
2012-09-21 00:39:43 -07:00
|
|
|
}
|
|
|
|
|
2013-03-26 15:05:58 -07:00
|
|
|
if (CC_UNLIKELY(mCaches.drawDeferDisabled)) {
|
|
|
|
mLayerUpdates.clear();
|
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, getTargetFbo());
|
|
|
|
}
|
|
|
|
endMark();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void OpenGLRenderer::flushLayers() {
|
|
|
|
int count = mLayerUpdates.size();
|
|
|
|
if (count > 0) {
|
|
|
|
startMark("Apply Layer Updates");
|
|
|
|
char layerName[12];
|
|
|
|
|
2013-04-04 14:46:24 -07:00
|
|
|
// Note: it is very important to update the layers in order
|
|
|
|
for (int i = 0; i < count; i++) {
|
2013-03-26 15:05:58 -07:00
|
|
|
sprintf(layerName, "Layer #%d", i);
|
2013-03-29 12:37:16 -07:00
|
|
|
startMark(layerName);
|
|
|
|
|
2013-06-12 15:31:28 -07:00
|
|
|
ATRACE_BEGIN("flushLayer");
|
2013-03-29 12:37:16 -07:00
|
|
|
Layer* layer = mLayerUpdates.itemAt(i);
|
|
|
|
layer->flush();
|
2013-06-12 15:31:28 -07:00
|
|
|
ATRACE_END();
|
|
|
|
|
2013-03-29 12:37:16 -07:00
|
|
|
mCaches.resourceCache.decrementRefcount(layer);
|
|
|
|
|
2013-03-26 15:05:58 -07:00
|
|
|
endMark();
|
|
|
|
}
|
|
|
|
|
|
|
|
mLayerUpdates.clear();
|
2012-09-21 00:39:43 -07:00
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, getTargetFbo());
|
2013-03-26 15:05:58 -07:00
|
|
|
|
2012-09-21 00:39:43 -07:00
|
|
|
endMark();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void OpenGLRenderer::pushLayerUpdate(Layer* layer) {
|
|
|
|
if (layer) {
|
2013-03-29 12:37:16 -07:00
|
|
|
// Make sure we don't introduce duplicates.
|
|
|
|
// SortedVector would do this automatically but we need to respect
|
|
|
|
// the insertion order. The linear search is not an issue since
|
|
|
|
// this list is usually very short (typically one item, at most a few)
|
|
|
|
for (int i = mLayerUpdates.size() - 1; i >= 0; i--) {
|
|
|
|
if (mLayerUpdates.itemAt(i) == layer) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2012-09-21 00:39:43 -07:00
|
|
|
mLayerUpdates.push_back(layer);
|
|
|
|
mCaches.resourceCache.incrementRefcount(layer);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void OpenGLRenderer::clearLayerUpdates() {
|
|
|
|
size_t count = mLayerUpdates.size();
|
|
|
|
if (count > 0) {
|
|
|
|
mCaches.resourceCache.lock();
|
|
|
|
for (size_t i = 0; i < count; i++) {
|
|
|
|
mCaches.resourceCache.decrementRefcountLocked(mLayerUpdates.itemAt(i));
|
|
|
|
}
|
|
|
|
mCaches.resourceCache.unlock();
|
|
|
|
mLayerUpdates.clear();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-06-12 15:31:28 -07:00
|
|
|
void OpenGLRenderer::flushLayerUpdates() {
|
|
|
|
syncState();
|
|
|
|
updateLayers();
|
|
|
|
flushLayers();
|
|
|
|
// Wait for all the layer updates to be executed
|
|
|
|
AutoFence fence;
|
|
|
|
}
|
|
|
|
|
2010-06-23 17:47:49 -07:00
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
// State management
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
2010-06-22 18:56:38 -07:00
|
|
|
int OpenGLRenderer::getSaveCount() const {
|
2010-06-25 13:46:18 -07:00
|
|
|
return mSaveCount;
|
2010-06-22 18:56:38 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
int OpenGLRenderer::save(int flags) {
|
2010-09-01 15:13:49 -07:00
|
|
|
return saveSnapshot(flags);
|
2010-06-22 18:56:38 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void OpenGLRenderer::restore() {
|
2010-08-18 11:47:12 -07:00
|
|
|
if (mSaveCount > 1) {
|
|
|
|
restoreSnapshot();
|
2010-06-25 13:46:18 -07:00
|
|
|
}
|
2010-06-22 18:56:38 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void OpenGLRenderer::restoreToCount(int saveCount) {
|
2010-08-17 18:38:51 -07:00
|
|
|
if (saveCount < 1) saveCount = 1;
|
2010-06-22 18:56:38 -07:00
|
|
|
|
2010-08-17 18:38:51 -07:00
|
|
|
while (mSaveCount > saveCount) {
|
2010-08-18 11:47:12 -07:00
|
|
|
restoreSnapshot();
|
2010-06-25 13:46:18 -07:00
|
|
|
}
|
2010-06-22 18:56:38 -07:00
|
|
|
}
|
|
|
|
|
2010-09-01 15:13:49 -07:00
|
|
|
int OpenGLRenderer::saveSnapshot(int flags) {
|
|
|
|
mSnapshot = new Snapshot(mSnapshot, flags);
|
2010-08-17 18:38:51 -07:00
|
|
|
return mSaveCount++;
|
2010-06-22 18:56:38 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
bool OpenGLRenderer::restoreSnapshot() {
|
2010-06-25 13:46:18 -07:00
|
|
|
bool restoreClip = mSnapshot->flags & Snapshot::kFlagClipSet;
|
2010-06-26 00:13:53 -07:00
|
|
|
bool restoreLayer = mSnapshot->flags & Snapshot::kFlagIsLayer;
|
2010-10-05 18:14:38 -07:00
|
|
|
bool restoreOrtho = mSnapshot->flags & Snapshot::kFlagDirtyOrtho;
|
2010-06-22 18:56:38 -07:00
|
|
|
|
2010-06-26 00:13:53 -07:00
|
|
|
sp<Snapshot> current = mSnapshot;
|
2010-06-25 13:46:18 -07:00
|
|
|
sp<Snapshot> previous = mSnapshot->previous;
|
2010-06-26 00:13:53 -07:00
|
|
|
|
2010-10-05 18:14:38 -07:00
|
|
|
if (restoreOrtho) {
|
|
|
|
Rect& r = previous->viewport;
|
|
|
|
glViewport(r.left, r.top, r.right, r.bottom);
|
|
|
|
mOrthoMatrix.load(current->orthoMatrix);
|
|
|
|
}
|
|
|
|
|
2010-08-18 17:10:07 -07:00
|
|
|
mSaveCount--;
|
|
|
|
mSnapshot = previous;
|
|
|
|
|
2010-08-18 11:47:12 -07:00
|
|
|
if (restoreClip) {
|
2010-10-26 16:27:31 -07:00
|
|
|
dirtyClip();
|
2010-08-17 18:38:51 -07:00
|
|
|
}
|
2010-08-18 11:47:12 -07:00
|
|
|
|
2010-11-03 16:19:08 -07:00
|
|
|
if (restoreLayer) {
|
2013-03-28 11:25:24 -07:00
|
|
|
endMark(); // Savelayer
|
|
|
|
startMark("ComposeLayer");
|
2010-11-03 16:19:08 -07:00
|
|
|
composeLayer(current, previous);
|
2013-03-28 11:25:24 -07:00
|
|
|
endMark();
|
2010-11-03 16:19:08 -07:00
|
|
|
}
|
|
|
|
|
2010-08-18 11:47:12 -07:00
|
|
|
return restoreClip;
|
2010-06-28 17:42:46 -07:00
|
|
|
}
|
2010-06-27 22:59:20 -07:00
|
|
|
|
2010-06-26 00:13:53 -07:00
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Layers
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
int OpenGLRenderer::saveLayer(float left, float top, float right, float bottom,
|
2013-03-08 13:12:16 -08:00
|
|
|
int alpha, SkXfermode::Mode mode, int flags) {
|
2010-10-05 18:14:38 -07:00
|
|
|
const GLuint previousFbo = mSnapshot->fbo;
|
|
|
|
const int count = saveSnapshot(flags);
|
2010-06-28 17:42:46 -07:00
|
|
|
|
2010-12-09 17:47:21 -08:00
|
|
|
if (!mSnapshot->isIgnored()) {
|
2012-08-28 17:43:28 -07:00
|
|
|
createLayer(left, top, right, bottom, alpha, mode, flags, previousFbo);
|
2010-10-11 17:58:29 -07:00
|
|
|
}
|
2010-06-28 17:42:46 -07:00
|
|
|
|
|
|
|
return count;
|
2010-06-26 00:13:53 -07:00
|
|
|
}
|
|
|
|
|
2013-03-19 15:03:48 -07:00
|
|
|
void OpenGLRenderer::calculateLayerBoundsAndClip(Rect& bounds, Rect& clip, bool fboLayer) {
|
|
|
|
const Rect untransformedBounds(bounds);
|
|
|
|
|
|
|
|
currentTransform().mapRect(bounds);
|
|
|
|
|
|
|
|
// Layers only make sense if they are in the framebuffer's bounds
|
|
|
|
if (bounds.intersect(*mSnapshot->clipRect)) {
|
|
|
|
// We cannot work with sub-pixels in this case
|
|
|
|
bounds.snapToPixelBoundaries();
|
|
|
|
|
|
|
|
// When the layer is not an FBO, we may use glCopyTexImage so we
|
|
|
|
// need to make sure the layer does not extend outside the bounds
|
|
|
|
// of the framebuffer
|
|
|
|
if (!bounds.intersect(mSnapshot->previous->viewport)) {
|
|
|
|
bounds.setEmpty();
|
|
|
|
} else if (fboLayer) {
|
|
|
|
clip.set(bounds);
|
|
|
|
mat4 inverse;
|
|
|
|
inverse.loadInverse(currentTransform());
|
|
|
|
inverse.mapRect(clip);
|
|
|
|
clip.snapToPixelBoundaries();
|
|
|
|
if (clip.intersect(untransformedBounds)) {
|
|
|
|
clip.translate(-untransformedBounds.left, -untransformedBounds.top);
|
|
|
|
bounds.set(untransformedBounds);
|
|
|
|
} else {
|
|
|
|
clip.setEmpty();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
bounds.setEmpty();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-03-26 18:55:15 -07:00
|
|
|
void OpenGLRenderer::updateSnapshotIgnoreForLayer(const Rect& bounds, const Rect& clip,
|
|
|
|
bool fboLayer, int alpha) {
|
|
|
|
if (bounds.isEmpty() || bounds.getWidth() > mCaches.maxTextureSize ||
|
|
|
|
bounds.getHeight() > mCaches.maxTextureSize ||
|
|
|
|
(fboLayer && clip.isEmpty())) {
|
|
|
|
mSnapshot->empty = fboLayer;
|
|
|
|
} else {
|
|
|
|
mSnapshot->invisible = mSnapshot->invisible || (alpha <= ALPHA_THRESHOLD && fboLayer);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-03-19 15:03:48 -07:00
|
|
|
int OpenGLRenderer::saveLayerDeferred(float left, float top, float right, float bottom,
|
|
|
|
int alpha, SkXfermode::Mode mode, int flags) {
|
|
|
|
const GLuint previousFbo = mSnapshot->fbo;
|
|
|
|
const int count = saveSnapshot(flags);
|
|
|
|
|
|
|
|
if (!mSnapshot->isIgnored() && (flags & SkCanvas::kClipToLayer_SaveFlag)) {
|
|
|
|
// initialize the snapshot as though it almost represents an FBO layer so deferred draw
|
|
|
|
// operations will be able to store and restore the current clip and transform info, and
|
|
|
|
// quick rejection will be correct (for display lists)
|
|
|
|
|
|
|
|
Rect bounds(left, top, right, bottom);
|
|
|
|
Rect clip;
|
|
|
|
calculateLayerBoundsAndClip(bounds, clip, true);
|
2013-03-26 18:55:15 -07:00
|
|
|
updateSnapshotIgnoreForLayer(bounds, clip, true, alpha);
|
2013-03-19 15:03:48 -07:00
|
|
|
|
2013-03-26 18:55:15 -07:00
|
|
|
if (!mSnapshot->isIgnored()) {
|
2013-03-19 15:03:48 -07:00
|
|
|
mSnapshot->resetTransform(-bounds.left, -bounds.top, 0.0f);
|
|
|
|
mSnapshot->resetClip(clip.left, clip.top, clip.right, clip.bottom);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-09-13 18:00:09 -07:00
|
|
|
/**
|
|
|
|
* 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.
|
2010-10-06 19:49:23 -07:00
|
|
|
* To avoid this, layers are implemented in a different way here, at least in the
|
|
|
|
* general case. FBOs are used, as an optimization, when the "clip to layer" flag
|
|
|
|
* is set. When this flag is set we can redirect all drawing operations into a
|
|
|
|
* single FBO.
|
2010-09-13 18:00:09 -07:00
|
|
|
*
|
|
|
|
* 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
|
2010-09-13 18:11:21 -07:00
|
|
|
* using glCopyTexImage2D(). The layer's region is then cleared(1) in the frame
|
2010-09-13 18:00:09 -07:00
|
|
|
* 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.
|
2010-09-13 18:11:21 -07:00
|
|
|
*
|
|
|
|
* (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.
|
2010-09-13 18:00:09 -07:00
|
|
|
*/
|
2012-08-28 17:43:28 -07:00
|
|
|
bool OpenGLRenderer::createLayer(float left, float top, float right, float bottom,
|
|
|
|
int alpha, SkXfermode::Mode mode, int flags, GLuint previousFbo) {
|
2010-10-05 18:14:38 -07:00
|
|
|
LAYER_LOGD("Requesting layer %.2fx%.2f", right - left, bottom - top);
|
2010-08-23 21:05:08 -07:00
|
|
|
LAYER_LOGD("Layer cache size = %d", mCaches.layerCache.getSize());
|
2010-07-06 11:39:32 -07:00
|
|
|
|
2010-10-05 18:14:38 -07:00
|
|
|
const bool fboLayer = flags & SkCanvas::kClipToLayer_SaveFlag;
|
|
|
|
|
2010-09-10 19:20:06 -07:00
|
|
|
// Window coordinates of the layer
|
2012-08-28 17:43:28 -07:00
|
|
|
Rect clip;
|
2010-09-01 15:13:49 -07:00
|
|
|
Rect bounds(left, top, right, bottom);
|
2013-03-19 15:03:48 -07:00
|
|
|
calculateLayerBoundsAndClip(bounds, clip, fboLayer);
|
2013-03-26 18:55:15 -07:00
|
|
|
updateSnapshotIgnoreForLayer(bounds, clip, fboLayer, alpha);
|
2010-10-11 17:58:29 -07:00
|
|
|
|
|
|
|
// Bail out if we won't draw in this snapshot
|
2013-03-26 18:55:15 -07:00
|
|
|
if (mSnapshot->isIgnored()) {
|
2010-09-16 14:16:48 -07:00
|
|
|
return false;
|
|
|
|
}
|
2010-07-06 11:39:32 -07:00
|
|
|
|
2011-12-13 14:55:06 -08:00
|
|
|
mCaches.activeTexture(0);
|
2010-10-08 15:49:53 -07:00
|
|
|
Layer* layer = mCaches.layerCache.get(bounds.getWidth(), bounds.getHeight());
|
2010-07-08 11:45:51 -07:00
|
|
|
if (!layer) {
|
|
|
|
return false;
|
2010-06-26 00:13:53 -07:00
|
|
|
}
|
|
|
|
|
2011-07-07 20:50:11 -07:00
|
|
|
layer->setAlpha(alpha, mode);
|
2010-09-01 15:13:49 -07:00
|
|
|
layer->layer.set(bounds);
|
2011-07-07 20:50:11 -07:00
|
|
|
layer->texCoords.set(0.0f, bounds.getHeight() / float(layer->getHeight()),
|
|
|
|
bounds.getWidth() / float(layer->getWidth()), 0.0f);
|
2013-02-04 16:16:33 -08:00
|
|
|
layer->setColorFilter(mDrawModifiers.mColorFilter);
|
2012-04-12 15:19:04 -07:00
|
|
|
layer->setBlend(true);
|
2012-10-18 15:05:02 -07:00
|
|
|
layer->setDirty(false);
|
2010-07-06 11:39:32 -07:00
|
|
|
|
2010-08-17 18:38:51 -07:00
|
|
|
// Save the layer in the snapshot
|
2012-08-28 17:43:28 -07:00
|
|
|
mSnapshot->flags |= Snapshot::kFlagIsLayer;
|
|
|
|
mSnapshot->layer = layer;
|
2010-08-17 11:37:00 -07:00
|
|
|
|
2013-03-28 11:25:24 -07:00
|
|
|
startMark("SaveLayer");
|
2010-10-05 18:14:38 -07:00
|
|
|
if (fboLayer) {
|
2012-08-28 17:43:28 -07:00
|
|
|
return createFboLayer(layer, bounds, clip, previousFbo);
|
2010-10-05 18:14:38 -07:00
|
|
|
} else {
|
|
|
|
// Copy the framebuffer into the layer
|
2011-07-07 20:50:11 -07:00
|
|
|
layer->bindTexture();
|
2011-01-19 14:38:29 -08:00
|
|
|
if (!bounds.isEmpty()) {
|
2011-07-07 20:50:11 -07:00
|
|
|
if (layer->isEmpty()) {
|
|
|
|
glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
|
2012-08-28 17:43:28 -07:00
|
|
|
bounds.left, mSnapshot->height - bounds.bottom,
|
2011-07-07 20:50:11 -07:00
|
|
|
layer->getWidth(), layer->getHeight(), 0);
|
|
|
|
layer->setEmpty(false);
|
2011-01-19 14:38:29 -08:00
|
|
|
} else {
|
|
|
|
glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, bounds.left,
|
2012-08-28 17:43:28 -07:00
|
|
|
mSnapshot->height - bounds.bottom, bounds.getWidth(), bounds.getHeight());
|
2011-01-19 14:38:29 -08:00
|
|
|
}
|
2011-03-14 18:05:08 -07:00
|
|
|
|
2011-06-13 19:04:27 -07:00
|
|
|
// Enqueue the buffer coordinates to clear the corresponding region later
|
|
|
|
mLayers.push(new Rect(bounds));
|
2010-10-22 17:49:18 -07:00
|
|
|
}
|
2010-10-05 18:14:38 -07:00
|
|
|
}
|
2010-07-01 11:05:42 -07:00
|
|
|
|
2010-06-28 17:42:46 -07:00
|
|
|
return true;
|
2010-06-26 00:13:53 -07:00
|
|
|
}
|
|
|
|
|
2012-08-28 17:43:28 -07:00
|
|
|
bool OpenGLRenderer::createFboLayer(Layer* layer, Rect& bounds, Rect& clip, GLuint previousFbo) {
|
2013-01-29 17:26:25 -08:00
|
|
|
layer->clipRect.set(clip);
|
2011-07-07 20:50:11 -07:00
|
|
|
layer->setFbo(mCaches.fboCache.get());
|
2010-10-27 18:57:51 -07:00
|
|
|
|
2012-08-28 17:43:28 -07:00
|
|
|
mSnapshot->region = &mSnapshot->layer->region;
|
2013-01-29 17:26:25 -08:00
|
|
|
mSnapshot->flags |= Snapshot::kFlagFboTarget | Snapshot::kFlagIsFboLayer |
|
|
|
|
Snapshot::kFlagDirtyOrtho;
|
2012-08-28 17:43:28 -07:00
|
|
|
mSnapshot->fbo = layer->getFbo();
|
|
|
|
mSnapshot->resetTransform(-bounds.left, -bounds.top, 0.0f);
|
|
|
|
mSnapshot->resetClip(clip.left, clip.top, clip.right, clip.bottom);
|
|
|
|
mSnapshot->viewport.set(0.0f, 0.0f, bounds.getWidth(), bounds.getHeight());
|
|
|
|
mSnapshot->height = bounds.getHeight();
|
|
|
|
mSnapshot->orthoMatrix.load(mOrthoMatrix);
|
2010-10-27 18:57:51 -07:00
|
|
|
|
2012-09-19 17:25:38 -07:00
|
|
|
endTiling();
|
2012-09-21 19:15:00 -07:00
|
|
|
debugOverdraw(false, false);
|
2010-10-27 18:57:51 -07:00
|
|
|
// Bind texture to FBO
|
2011-07-07 20:50:11 -07:00
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, layer->getFbo());
|
|
|
|
layer->bindTexture();
|
2010-10-27 18:57:51 -07:00
|
|
|
|
|
|
|
// Initialize the texture if needed
|
2011-07-07 20:50:11 -07:00
|
|
|
if (layer->isEmpty()) {
|
2013-04-04 12:27:54 -07:00
|
|
|
layer->allocateTexture();
|
2011-07-07 20:50:11 -07:00
|
|
|
layer->setEmpty(false);
|
2010-10-27 18:57:51 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
|
2011-07-07 20:50:11 -07:00
|
|
|
layer->getTexture(), 0);
|
2010-10-27 18:57:51 -07:00
|
|
|
|
2013-01-31 17:45:55 -08:00
|
|
|
startTiling(mSnapshot, true);
|
2010-10-27 18:57:51 -07:00
|
|
|
|
|
|
|
// Clear the FBO, expand the clear region by 1 to get nice bilinear filtering
|
2012-07-13 15:28:31 -07:00
|
|
|
mCaches.enableScissor();
|
2011-12-14 19:23:32 -08:00
|
|
|
mCaches.setScissor(clip.left - 1.0f, bounds.getHeight() - clip.bottom - 1.0f,
|
2010-10-27 18:57:51 -07:00
|
|
|
clip.getWidth() + 2.0f, clip.getHeight() + 2.0f);
|
|
|
|
glClear(GL_COLOR_BUFFER_BIT);
|
|
|
|
|
|
|
|
dirtyClip();
|
|
|
|
|
|
|
|
// 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);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2010-09-13 18:00:09 -07:00
|
|
|
/**
|
|
|
|
* Read the documentation of createLayer() before doing anything in this method.
|
|
|
|
*/
|
2010-08-17 11:37:00 -07:00
|
|
|
void OpenGLRenderer::composeLayer(sp<Snapshot> current, sp<Snapshot> previous) {
|
|
|
|
if (!current->layer) {
|
2012-01-06 19:20:56 +00:00
|
|
|
ALOGE("Attempting to compose a layer that does not exist");
|
2010-08-17 11:37:00 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-01-15 18:51:42 -08:00
|
|
|
Layer* layer = current->layer;
|
|
|
|
const Rect& rect = layer->layer;
|
2010-10-27 18:57:51 -07:00
|
|
|
const bool fboLayer = current->flags & Snapshot::kFlagIsFboLayer;
|
2010-10-05 18:14:38 -07:00
|
|
|
|
2013-06-13 14:39:01 -07:00
|
|
|
bool clipRequired = false;
|
|
|
|
quickRejectNoScissor(rect, &clipRequired); // safely ignore return, should never be rejected
|
|
|
|
mCaches.setScissorEnabled(mScissorOptimizationDisabled || clipRequired);
|
|
|
|
|
2010-10-05 18:14:38 -07:00
|
|
|
if (fboLayer) {
|
2012-09-19 17:25:38 -07:00
|
|
|
endTiling();
|
|
|
|
|
2012-04-03 19:30:26 -07:00
|
|
|
// Detach the texture from the FBO
|
|
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
|
2013-01-15 18:51:42 -08:00
|
|
|
|
|
|
|
layer->removeFbo(false);
|
|
|
|
|
2010-10-05 18:14:38 -07:00
|
|
|
// Unbind current FBO and restore previous one
|
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, previous->fbo);
|
2012-09-21 19:15:00 -07:00
|
|
|
debugOverdraw(true, false);
|
2012-09-19 17:25:38 -07:00
|
|
|
|
|
|
|
startTiling(previous);
|
2010-10-05 18:14:38 -07:00
|
|
|
}
|
|
|
|
|
2011-07-07 20:50:11 -07:00
|
|
|
if (!fboLayer && layer->getAlpha() < 255) {
|
2010-09-10 19:20:06 -07:00
|
|
|
drawColorRect(rect.left, rect.top, rect.right, rect.bottom,
|
2011-07-07 20:50:11 -07:00
|
|
|
layer->getAlpha() << 24, SkXfermode::kDstIn_Mode, true);
|
2010-10-27 18:57:51 -07:00
|
|
|
// Required below, composeLayerRect() will divide by 255
|
2011-07-07 20:50:11 -07:00
|
|
|
layer->setAlpha(255);
|
2010-09-10 19:20:06 -07:00
|
|
|
}
|
|
|
|
|
2010-10-18 14:06:08 -07:00
|
|
|
mCaches.unbindMeshBuffer();
|
2010-08-18 17:10:07 -07:00
|
|
|
|
2011-12-13 14:55:06 -08:00
|
|
|
mCaches.activeTexture(0);
|
2010-10-27 18:57:51 -07:00
|
|
|
|
|
|
|
// When the layer is stored in an FBO, we can save a bit of fillrate by
|
|
|
|
// drawing only the dirty region
|
2010-10-05 18:14:38 -07:00
|
|
|
if (fboLayer) {
|
2010-10-27 18:57:51 -07:00
|
|
|
dirtyLayer(rect.left, rect.top, rect.right, rect.bottom, *previous->transform);
|
2011-07-07 20:50:11 -07:00
|
|
|
if (layer->getColorFilter()) {
|
|
|
|
setupColorFilter(layer->getColorFilter());
|
2011-01-06 10:04:23 -08:00
|
|
|
}
|
2010-10-27 18:57:51 -07:00
|
|
|
composeLayerRegion(layer, rect);
|
2011-07-07 20:50:11 -07:00
|
|
|
if (layer->getColorFilter()) {
|
2011-01-06 10:04:23 -08:00
|
|
|
resetColorFilter();
|
|
|
|
}
|
2011-07-07 20:50:11 -07:00
|
|
|
} else if (!rect.isEmpty()) {
|
|
|
|
dirtyLayer(rect.left, rect.top, rect.right, rect.bottom);
|
|
|
|
composeLayerRect(layer, rect, true);
|
2010-10-05 18:14:38 -07:00
|
|
|
}
|
2010-08-17 11:37:00 -07:00
|
|
|
|
2010-10-26 16:27:31 -07:00
|
|
|
dirtyClip();
|
|
|
|
|
2010-10-05 18:14:38 -07:00
|
|
|
// Failing to add the layer to the cache should happen only if the layer is too large
|
2010-10-08 15:49:53 -07:00
|
|
|
if (!mCaches.layerCache.put(layer)) {
|
2010-08-17 11:37:00 -07:00
|
|
|
LAYER_LOGD("Deleting layer");
|
Fix occasional crash bug with layers
Launcher occasionally crashes with a stack trace indicating that the memory
of a Layer object is corrupt. It is possible for us to delete a Layer
structure and then, briefly, use it to draw a DisplayList again before
that DisplayList gets recreated (without the layer that got deleted).
When this happens, if the memory got corrupted, it's possible to crash.
The fix is to add Layer to the other objects which we currently refcount
(bitmaps, shaders, etc.). Then instead of deleting a Layer, we decrement the
refcount. We increment when creating it, then increment it again when it's
referenced from a DisplayList. Then we decrement the refcount instead of
deleting it, and decrement when we clear a DisplayList that refers to it.
Then when the refcount reaches 0, we delete it.
Issue #6994632 Native crash in launcher when trying to launch all apps screen
Change-Id: I0627be8d49bb2f9ba8d158a84b764bb4e7df934c
2012-09-14 15:31:25 -07:00
|
|
|
Caches::getInstance().resourceCache.decrementRefcount(layer);
|
2010-08-17 11:37:00 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
New widget: TextureView
Bug #4343984
TextureView can be used to render media content (video, OpenGL,
RenderScript) inside a View.
The key difference with SurfaceView is that TextureView does
not create a new Surface. This gives the ability to seamlessly
transform, animate, fade, etc. a TextureView, which was hard
if not impossible to do with a SurfaceView.
A TextureView also interacts perfectly with ScrollView,
ListView, etc. It allows application to embed media content
in a much more flexible way than before.
For instance, to render the camera preview at 50% opacity,
all you need to do is the following:
mTextureView.setAlpha(0.5f);
Camera c = Camera.open();
c.setPreviewTexture(mTextureView.getSurfaceTexture());
c.startPreview();
TextureView uses a SurfaceTexture to get the job done. More
APIs are required to make it easy to create OpenGL contexts
for a TextureView. It can currently be done with a bit of
JNI code.
Change-Id: Iaa7953097ab5beb8437bcbbfa03b2df5b7f80cd7
2011-04-28 18:40:04 -07:00
|
|
|
void OpenGLRenderer::drawTextureLayer(Layer* layer, const Rect& rect) {
|
2013-03-20 16:57:09 -07:00
|
|
|
float alpha = layer->getAlpha() / 255.0f * mSnapshot->alpha;
|
New widget: TextureView
Bug #4343984
TextureView can be used to render media content (video, OpenGL,
RenderScript) inside a View.
The key difference with SurfaceView is that TextureView does
not create a new Surface. This gives the ability to seamlessly
transform, animate, fade, etc. a TextureView, which was hard
if not impossible to do with a SurfaceView.
A TextureView also interacts perfectly with ScrollView,
ListView, etc. It allows application to embed media content
in a much more flexible way than before.
For instance, to render the camera preview at 50% opacity,
all you need to do is the following:
mTextureView.setAlpha(0.5f);
Camera c = Camera.open();
c.setPreviewTexture(mTextureView.getSurfaceTexture());
c.startPreview();
TextureView uses a SurfaceTexture to get the job done. More
APIs are required to make it easy to create OpenGL contexts
for a TextureView. It can currently be done with a bit of
JNI code.
Change-Id: Iaa7953097ab5beb8437bcbbfa03b2df5b7f80cd7
2011-04-28 18:40:04 -07:00
|
|
|
|
|
|
|
setupDraw();
|
2011-07-07 20:50:11 -07:00
|
|
|
if (layer->getRenderTarget() == GL_TEXTURE_2D) {
|
2011-05-02 17:24:22 -07:00
|
|
|
setupDrawWithTexture();
|
|
|
|
} else {
|
|
|
|
setupDrawWithExternalTexture();
|
|
|
|
}
|
|
|
|
setupDrawTextureTransform();
|
New widget: TextureView
Bug #4343984
TextureView can be used to render media content (video, OpenGL,
RenderScript) inside a View.
The key difference with SurfaceView is that TextureView does
not create a new Surface. This gives the ability to seamlessly
transform, animate, fade, etc. a TextureView, which was hard
if not impossible to do with a SurfaceView.
A TextureView also interacts perfectly with ScrollView,
ListView, etc. It allows application to embed media content
in a much more flexible way than before.
For instance, to render the camera preview at 50% opacity,
all you need to do is the following:
mTextureView.setAlpha(0.5f);
Camera c = Camera.open();
c.setPreviewTexture(mTextureView.getSurfaceTexture());
c.startPreview();
TextureView uses a SurfaceTexture to get the job done. More
APIs are required to make it easy to create OpenGL contexts
for a TextureView. It can currently be done with a bit of
JNI code.
Change-Id: Iaa7953097ab5beb8437bcbbfa03b2df5b7f80cd7
2011-04-28 18:40:04 -07:00
|
|
|
setupDrawColor(alpha, alpha, alpha, alpha);
|
|
|
|
setupDrawColorFilter();
|
2011-07-07 20:50:11 -07:00
|
|
|
setupDrawBlending(layer->isBlend() || alpha < 1.0f, layer->getMode());
|
New widget: TextureView
Bug #4343984
TextureView can be used to render media content (video, OpenGL,
RenderScript) inside a View.
The key difference with SurfaceView is that TextureView does
not create a new Surface. This gives the ability to seamlessly
transform, animate, fade, etc. a TextureView, which was hard
if not impossible to do with a SurfaceView.
A TextureView also interacts perfectly with ScrollView,
ListView, etc. It allows application to embed media content
in a much more flexible way than before.
For instance, to render the camera preview at 50% opacity,
all you need to do is the following:
mTextureView.setAlpha(0.5f);
Camera c = Camera.open();
c.setPreviewTexture(mTextureView.getSurfaceTexture());
c.startPreview();
TextureView uses a SurfaceTexture to get the job done. More
APIs are required to make it easy to create OpenGL contexts
for a TextureView. It can currently be done with a bit of
JNI code.
Change-Id: Iaa7953097ab5beb8437bcbbfa03b2df5b7f80cd7
2011-04-28 18:40:04 -07:00
|
|
|
setupDrawProgram();
|
|
|
|
setupDrawPureColorUniforms();
|
|
|
|
setupDrawColorFilterUniforms();
|
2011-07-07 20:50:11 -07:00
|
|
|
if (layer->getRenderTarget() == GL_TEXTURE_2D) {
|
|
|
|
setupDrawTexture(layer->getTexture());
|
2011-05-02 17:24:22 -07:00
|
|
|
} else {
|
2011-07-07 20:50:11 -07:00
|
|
|
setupDrawExternalTexture(layer->getTexture());
|
2011-05-02 17:24:22 -07:00
|
|
|
}
|
2013-03-05 10:27:35 -08:00
|
|
|
if (currentTransform().isPureTranslate() &&
|
2011-07-07 21:05:04 -07:00
|
|
|
layer->getWidth() == (uint32_t) rect.getWidth() &&
|
|
|
|
layer->getHeight() == (uint32_t) rect.getHeight()) {
|
2013-03-05 10:27:35 -08:00
|
|
|
const float x = (int) floorf(rect.left + currentTransform().getTranslateX() + 0.5f);
|
|
|
|
const float y = (int) floorf(rect.top + currentTransform().getTranslateY() + 0.5f);
|
2011-07-07 20:50:11 -07:00
|
|
|
|
2011-11-30 20:21:23 -08:00
|
|
|
layer->setFilter(GL_NEAREST);
|
2011-07-07 20:50:11 -07:00
|
|
|
setupDrawModelView(x, y, x + rect.getWidth(), y + rect.getHeight(), true);
|
|
|
|
} else {
|
2011-11-30 20:21:23 -08:00
|
|
|
layer->setFilter(GL_LINEAR);
|
2011-07-07 20:50:11 -07:00
|
|
|
setupDrawModelView(rect.left, rect.top, rect.right, rect.bottom);
|
|
|
|
}
|
|
|
|
setupDrawTextureTransformUniforms(layer->getTexTransform());
|
New widget: TextureView
Bug #4343984
TextureView can be used to render media content (video, OpenGL,
RenderScript) inside a View.
The key difference with SurfaceView is that TextureView does
not create a new Surface. This gives the ability to seamlessly
transform, animate, fade, etc. a TextureView, which was hard
if not impossible to do with a SurfaceView.
A TextureView also interacts perfectly with ScrollView,
ListView, etc. It allows application to embed media content
in a much more flexible way than before.
For instance, to render the camera preview at 50% opacity,
all you need to do is the following:
mTextureView.setAlpha(0.5f);
Camera c = Camera.open();
c.setPreviewTexture(mTextureView.getSurfaceTexture());
c.startPreview();
TextureView uses a SurfaceTexture to get the job done. More
APIs are required to make it easy to create OpenGL contexts
for a TextureView. It can currently be done with a bit of
JNI code.
Change-Id: Iaa7953097ab5beb8437bcbbfa03b2df5b7f80cd7
2011-04-28 18:40:04 -07:00
|
|
|
setupDrawMesh(&mMeshVertices[0].position[0], &mMeshVertices[0].texture[0]);
|
|
|
|
|
|
|
|
glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount);
|
|
|
|
|
|
|
|
finishDrawTexture();
|
|
|
|
}
|
|
|
|
|
2010-10-27 18:57:51 -07:00
|
|
|
void OpenGLRenderer::composeLayerRect(Layer* layer, const Rect& rect, bool swap) {
|
2011-07-07 20:50:11 -07:00
|
|
|
if (!layer->isTextureLayer()) {
|
New widget: TextureView
Bug #4343984
TextureView can be used to render media content (video, OpenGL,
RenderScript) inside a View.
The key difference with SurfaceView is that TextureView does
not create a new Surface. This gives the ability to seamlessly
transform, animate, fade, etc. a TextureView, which was hard
if not impossible to do with a SurfaceView.
A TextureView also interacts perfectly with ScrollView,
ListView, etc. It allows application to embed media content
in a much more flexible way than before.
For instance, to render the camera preview at 50% opacity,
all you need to do is the following:
mTextureView.setAlpha(0.5f);
Camera c = Camera.open();
c.setPreviewTexture(mTextureView.getSurfaceTexture());
c.startPreview();
TextureView uses a SurfaceTexture to get the job done. More
APIs are required to make it easy to create OpenGL contexts
for a TextureView. It can currently be done with a bit of
JNI code.
Change-Id: Iaa7953097ab5beb8437bcbbfa03b2df5b7f80cd7
2011-04-28 18:40:04 -07:00
|
|
|
const Rect& texCoords = layer->texCoords;
|
|
|
|
resetDrawTextureTexCoords(texCoords.left, texCoords.top,
|
|
|
|
texCoords.right, texCoords.bottom);
|
2010-10-27 18:57:51 -07:00
|
|
|
|
2011-07-07 20:50:11 -07:00
|
|
|
float x = rect.left;
|
|
|
|
float y = rect.top;
|
2013-03-05 10:27:35 -08:00
|
|
|
bool simpleTransform = currentTransform().isPureTranslate() &&
|
2011-07-07 21:05:04 -07:00
|
|
|
layer->getWidth() == (uint32_t) rect.getWidth() &&
|
2011-07-08 11:57:29 -07:00
|
|
|
layer->getHeight() == (uint32_t) rect.getHeight();
|
|
|
|
|
|
|
|
if (simpleTransform) {
|
2011-07-07 20:50:11 -07:00
|
|
|
// When we're swapping, the layer is already in screen coordinates
|
|
|
|
if (!swap) {
|
2013-03-05 10:27:35 -08:00
|
|
|
x = (int) floorf(rect.left + currentTransform().getTranslateX() + 0.5f);
|
|
|
|
y = (int) floorf(rect.top + currentTransform().getTranslateY() + 0.5f);
|
2011-07-07 20:50:11 -07:00
|
|
|
}
|
|
|
|
|
2011-11-30 20:21:23 -08:00
|
|
|
layer->setFilter(GL_NEAREST, true);
|
2011-07-07 20:50:11 -07:00
|
|
|
} else {
|
2011-11-30 20:21:23 -08:00
|
|
|
layer->setFilter(GL_LINEAR, true);
|
2011-07-07 20:50:11 -07:00
|
|
|
}
|
|
|
|
|
2013-03-29 10:59:59 -07:00
|
|
|
float alpha = getLayerAlpha(layer);
|
2013-03-20 16:57:09 -07:00
|
|
|
bool blend = layer->isBlend() || alpha < 1.0f;
|
2011-07-07 20:50:11 -07:00
|
|
|
drawTextureMesh(x, y, x + rect.getWidth(), y + rect.getHeight(),
|
2013-03-20 16:57:09 -07:00
|
|
|
layer->getTexture(), alpha, layer->getMode(), blend,
|
2011-07-07 20:50:11 -07:00
|
|
|
&mMeshVertices[0].position[0], &mMeshVertices[0].texture[0],
|
|
|
|
GL_TRIANGLE_STRIP, gMeshCount, swap, swap || simpleTransform);
|
2010-10-27 18:57:51 -07:00
|
|
|
|
New widget: TextureView
Bug #4343984
TextureView can be used to render media content (video, OpenGL,
RenderScript) inside a View.
The key difference with SurfaceView is that TextureView does
not create a new Surface. This gives the ability to seamlessly
transform, animate, fade, etc. a TextureView, which was hard
if not impossible to do with a SurfaceView.
A TextureView also interacts perfectly with ScrollView,
ListView, etc. It allows application to embed media content
in a much more flexible way than before.
For instance, to render the camera preview at 50% opacity,
all you need to do is the following:
mTextureView.setAlpha(0.5f);
Camera c = Camera.open();
c.setPreviewTexture(mTextureView.getSurfaceTexture());
c.startPreview();
TextureView uses a SurfaceTexture to get the job done. More
APIs are required to make it easy to create OpenGL contexts
for a TextureView. It can currently be done with a bit of
JNI code.
Change-Id: Iaa7953097ab5beb8437bcbbfa03b2df5b7f80cd7
2011-04-28 18:40:04 -07:00
|
|
|
resetDrawTextureTexCoords(0.0f, 0.0f, 1.0f, 1.0f);
|
|
|
|
} else {
|
|
|
|
resetDrawTextureTexCoords(0.0f, 1.0f, 1.0f, 0.0f);
|
|
|
|
drawTextureLayer(layer, rect);
|
|
|
|
resetDrawTextureTexCoords(0.0f, 0.0f, 1.0f, 1.0f);
|
|
|
|
}
|
2010-10-27 18:57:51 -07:00
|
|
|
}
|
|
|
|
|
2013-04-15 16:08:28 -07:00
|
|
|
/**
|
|
|
|
* Issues the command X, and if we're composing a save layer to the fbo or drawing a newly updated
|
|
|
|
* hardware layer with overdraw debug on, draws again to the stencil only, so that these draw
|
|
|
|
* operations are correctly counted twice for overdraw. NOTE: assumes composeLayerRegion only used
|
|
|
|
* by saveLayer's restore
|
|
|
|
*/
|
|
|
|
#define DRAW_DOUBLE_STENCIL_IF(COND, DRAW_COMMAND) { \
|
|
|
|
DRAW_COMMAND; \
|
|
|
|
if (CC_UNLIKELY(mCaches.debugOverdraw && getTargetFbo() == 0 && COND)) { \
|
|
|
|
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); \
|
|
|
|
DRAW_COMMAND; \
|
|
|
|
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); \
|
|
|
|
} \
|
|
|
|
}
|
|
|
|
|
|
|
|
#define DRAW_DOUBLE_STENCIL(DRAW_COMMAND) DRAW_DOUBLE_STENCIL_IF(true, DRAW_COMMAND)
|
|
|
|
|
2010-10-27 18:57:51 -07:00
|
|
|
void OpenGLRenderer::composeLayerRegion(Layer* layer, const Rect& rect) {
|
|
|
|
if (layer->region.isRect()) {
|
2011-04-27 14:21:41 -07:00
|
|
|
layer->setRegionAsRect();
|
|
|
|
|
2013-04-15 16:08:28 -07:00
|
|
|
DRAW_DOUBLE_STENCIL(composeLayerRect(layer, layer->regionRect));
|
2011-04-27 14:21:41 -07:00
|
|
|
|
2010-10-27 18:57:51 -07:00
|
|
|
layer->region.clear();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2011-09-07 17:55:15 -07:00
|
|
|
// TODO: See LayerRenderer.cpp::generateMesh() for important
|
|
|
|
// information about this implementation
|
2012-02-01 16:10:55 -08:00
|
|
|
if (CC_LIKELY(!layer->region.isEmpty())) {
|
2010-10-27 18:57:51 -07:00
|
|
|
size_t count;
|
2013-02-27 14:03:19 -08:00
|
|
|
const android::Rect* rects;
|
|
|
|
Region safeRegion;
|
|
|
|
if (CC_LIKELY(hasRectToRectTransform())) {
|
|
|
|
rects = layer->region.getArray(&count);
|
|
|
|
} else {
|
|
|
|
safeRegion = Region::createTJunctionFreeRegion(layer->region);
|
|
|
|
rects = safeRegion.getArray(&count);
|
|
|
|
}
|
2010-10-27 18:57:51 -07:00
|
|
|
|
2013-03-29 10:59:59 -07:00
|
|
|
const float alpha = getLayerAlpha(layer);
|
2011-07-07 20:50:11 -07:00
|
|
|
const float texX = 1.0f / float(layer->getWidth());
|
|
|
|
const float texY = 1.0f / float(layer->getHeight());
|
2011-01-16 12:54:25 -08:00
|
|
|
const float height = rect.getHeight();
|
2010-10-27 18:57:51 -07:00
|
|
|
|
2013-01-15 18:51:42 -08:00
|
|
|
setupDraw();
|
|
|
|
|
|
|
|
// We must get (and therefore bind) the region mesh buffer
|
|
|
|
// after we setup drawing in case we need to mess with the
|
|
|
|
// stencil buffer in setupDraw()
|
2010-10-27 18:57:51 -07:00
|
|
|
TextureVertex* mesh = mCaches.getRegionMesh();
|
|
|
|
GLsizei numQuads = 0;
|
|
|
|
|
2011-01-10 22:26:16 -08:00
|
|
|
setupDrawWithTexture();
|
|
|
|
setupDrawColor(alpha, alpha, alpha, alpha);
|
2011-01-13 12:13:20 -08:00
|
|
|
setupDrawColorFilter();
|
2011-07-07 20:50:11 -07:00
|
|
|
setupDrawBlending(layer->isBlend() || alpha < 1.0f, layer->getMode(), false);
|
2011-01-10 22:26:16 -08:00
|
|
|
setupDrawProgram();
|
|
|
|
setupDrawDirtyRegionsDisabled();
|
|
|
|
setupDrawPureColorUniforms();
|
2011-01-13 12:13:20 -08:00
|
|
|
setupDrawColorFilterUniforms();
|
2011-07-07 20:50:11 -07:00
|
|
|
setupDrawTexture(layer->getTexture());
|
2013-03-05 10:27:35 -08:00
|
|
|
if (currentTransform().isPureTranslate()) {
|
|
|
|
const float x = (int) floorf(rect.left + currentTransform().getTranslateX() + 0.5f);
|
|
|
|
const float y = (int) floorf(rect.top + currentTransform().getTranslateY() + 0.5f);
|
2011-07-07 20:50:11 -07:00
|
|
|
|
2011-11-30 20:21:23 -08:00
|
|
|
layer->setFilter(GL_NEAREST);
|
2011-07-07 20:50:11 -07:00
|
|
|
setupDrawModelViewTranslate(x, y, x + rect.getWidth(), y + rect.getHeight(), true);
|
|
|
|
} else {
|
2011-11-30 20:21:23 -08:00
|
|
|
layer->setFilter(GL_LINEAR);
|
2011-07-07 20:50:11 -07:00
|
|
|
setupDrawModelViewTranslate(rect.left, rect.top, rect.right, rect.bottom);
|
|
|
|
}
|
2011-12-13 13:11:32 -08:00
|
|
|
setupDrawMeshIndices(&mesh[0].position[0], &mesh[0].texture[0]);
|
2010-10-27 18:57:51 -07:00
|
|
|
|
|
|
|
for (size_t i = 0; i < count; i++) {
|
|
|
|
const android::Rect* r = &rects[i];
|
|
|
|
|
|
|
|
const float u1 = r->left * texX;
|
2011-01-16 12:54:25 -08:00
|
|
|
const float v1 = (height - r->top) * texY;
|
2010-10-27 18:57:51 -07:00
|
|
|
const float u2 = r->right * texX;
|
2011-01-16 12:54:25 -08:00
|
|
|
const float v2 = (height - r->bottom) * texY;
|
2010-10-27 18:57:51 -07:00
|
|
|
|
|
|
|
// TODO: Reject quads outside of the clip
|
|
|
|
TextureVertex::set(mesh++, r->left, r->top, u1, v1);
|
|
|
|
TextureVertex::set(mesh++, r->right, r->top, u2, v1);
|
|
|
|
TextureVertex::set(mesh++, r->left, r->bottom, u1, v2);
|
|
|
|
TextureVertex::set(mesh++, r->right, r->bottom, u2, v2);
|
|
|
|
|
|
|
|
numQuads++;
|
|
|
|
|
|
|
|
if (numQuads >= REGION_MESH_QUAD_COUNT) {
|
2013-04-15 16:08:28 -07:00
|
|
|
DRAW_DOUBLE_STENCIL(glDrawElements(GL_TRIANGLES, numQuads * 6,
|
|
|
|
GL_UNSIGNED_SHORT, NULL));
|
2010-10-27 18:57:51 -07:00
|
|
|
numQuads = 0;
|
|
|
|
mesh = mCaches.getRegionMesh();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (numQuads > 0) {
|
2013-04-15 16:08:28 -07:00
|
|
|
DRAW_DOUBLE_STENCIL(glDrawElements(GL_TRIANGLES, numQuads * 6,
|
|
|
|
GL_UNSIGNED_SHORT, NULL));
|
2010-10-27 18:57:51 -07:00
|
|
|
}
|
|
|
|
|
2011-01-10 22:26:16 -08:00
|
|
|
finishDrawTexture();
|
2010-10-27 18:57:51 -07:00
|
|
|
|
|
|
|
#if DEBUG_LAYERS_AS_REGIONS
|
2011-02-01 22:59:58 -08:00
|
|
|
drawRegionRects(layer->region);
|
2010-10-27 18:57:51 -07:00
|
|
|
#endif
|
|
|
|
|
|
|
|
layer->region.clear();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-02-01 22:59:58 -08:00
|
|
|
void OpenGLRenderer::drawRegionRects(const Region& region) {
|
|
|
|
#if DEBUG_LAYERS_AS_REGIONS
|
|
|
|
size_t count;
|
|
|
|
const android::Rect* rects = region.getArray(&count);
|
|
|
|
|
|
|
|
uint32_t colors[] = {
|
|
|
|
0x7fff0000, 0x7f00ff00,
|
|
|
|
0x7f0000ff, 0x7fff00ff,
|
|
|
|
};
|
|
|
|
|
|
|
|
int offset = 0;
|
|
|
|
int32_t top = rects[0].top;
|
|
|
|
|
|
|
|
for (size_t i = 0; i < count; i++) {
|
|
|
|
if (top != rects[i].top) {
|
|
|
|
offset ^= 0x2;
|
|
|
|
top = rects[i].top;
|
|
|
|
}
|
|
|
|
|
|
|
|
Rect r(rects[i].left, rects[i].top, rects[i].right, rects[i].bottom);
|
|
|
|
drawColorRect(r.left, r.top, r.right, r.bottom, colors[offset + (i & 0x1)],
|
|
|
|
SkXfermode::kSrcOver_Mode);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2013-01-15 18:51:42 -08:00
|
|
|
void OpenGLRenderer::drawRegionRects(const SkRegion& region, int color,
|
|
|
|
SkXfermode::Mode mode, bool dirty) {
|
|
|
|
int count = 0;
|
|
|
|
Vector<float> rects;
|
|
|
|
|
|
|
|
SkRegion::Iterator it(region);
|
|
|
|
while (!it.done()) {
|
|
|
|
const SkIRect& r = it.rect();
|
|
|
|
rects.push(r.fLeft);
|
|
|
|
rects.push(r.fTop);
|
|
|
|
rects.push(r.fRight);
|
|
|
|
rects.push(r.fBottom);
|
2012-11-26 18:30:17 -08:00
|
|
|
count += 4;
|
2013-01-15 18:51:42 -08:00
|
|
|
it.next();
|
|
|
|
}
|
|
|
|
|
2013-02-06 16:51:04 -08:00
|
|
|
drawColorRects(rects.array(), count, color, mode, true, dirty, false);
|
2013-01-15 18:51:42 -08:00
|
|
|
}
|
|
|
|
|
2010-10-27 18:57:51 -07:00
|
|
|
void OpenGLRenderer::dirtyLayer(const float left, const float top,
|
|
|
|
const float right, const float bottom, const mat4 transform) {
|
2011-01-16 12:54:25 -08:00
|
|
|
if (hasLayer()) {
|
2010-10-27 18:57:51 -07:00
|
|
|
Rect bounds(left, top, right, bottom);
|
|
|
|
transform.mapRect(bounds);
|
2011-01-16 12:54:25 -08:00
|
|
|
dirtyLayerUnchecked(bounds, getRegion());
|
2010-10-27 18:57:51 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void OpenGLRenderer::dirtyLayer(const float left, const float top,
|
|
|
|
const float right, const float bottom) {
|
2011-01-16 12:54:25 -08:00
|
|
|
if (hasLayer()) {
|
2010-10-27 18:57:51 -07:00
|
|
|
Rect bounds(left, top, right, bottom);
|
2011-01-16 12:54:25 -08:00
|
|
|
dirtyLayerUnchecked(bounds, getRegion());
|
2011-01-14 20:07:20 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void OpenGLRenderer::dirtyLayerUnchecked(Rect& bounds, Region* region) {
|
|
|
|
if (bounds.intersect(*mSnapshot->clipRect)) {
|
|
|
|
bounds.snapToPixelBoundaries();
|
|
|
|
android::Rect dirty(bounds.left, bounds.top, bounds.right, bounds.bottom);
|
|
|
|
if (!dirty.isEmpty()) {
|
|
|
|
region->orSelf(dirty);
|
2010-10-27 18:57:51 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-06-13 19:04:27 -07:00
|
|
|
void OpenGLRenderer::clearLayerRegions() {
|
|
|
|
const size_t count = mLayers.size();
|
|
|
|
if (count == 0) return;
|
|
|
|
|
|
|
|
if (!mSnapshot->isIgnored()) {
|
|
|
|
// Doing several glScissor/glClear here can negatively impact
|
|
|
|
// GPUs with a tiler architecture, instead we draw quads with
|
|
|
|
// the Clear blending mode
|
|
|
|
|
|
|
|
// The list contains bounds that have already been clipped
|
|
|
|
// against their initial clip rect, and the current clip
|
|
|
|
// is likely different so we need to disable clipping here
|
2012-07-17 17:32:48 -07:00
|
|
|
bool scissorChanged = mCaches.disableScissor();
|
2011-06-13 19:04:27 -07:00
|
|
|
|
|
|
|
Vertex mesh[count * 6];
|
|
|
|
Vertex* vertex = mesh;
|
|
|
|
|
|
|
|
for (uint32_t i = 0; i < count; i++) {
|
|
|
|
Rect* bounds = mLayers.itemAt(i);
|
|
|
|
|
|
|
|
Vertex::set(vertex++, bounds->left, bounds->bottom);
|
|
|
|
Vertex::set(vertex++, bounds->left, bounds->top);
|
|
|
|
Vertex::set(vertex++, bounds->right, bounds->top);
|
|
|
|
Vertex::set(vertex++, bounds->left, bounds->bottom);
|
|
|
|
Vertex::set(vertex++, bounds->right, bounds->top);
|
|
|
|
Vertex::set(vertex++, bounds->right, bounds->bottom);
|
|
|
|
|
|
|
|
delete bounds;
|
|
|
|
}
|
2013-02-11 18:01:20 -08:00
|
|
|
// We must clear the list of dirty rects before we
|
|
|
|
// call setupDraw() to prevent stencil setup to do
|
|
|
|
// the same thing again
|
|
|
|
mLayers.clear();
|
2011-06-13 19:04:27 -07:00
|
|
|
|
|
|
|
setupDraw(false);
|
|
|
|
setupDrawColor(0.0f, 0.0f, 0.0f, 1.0f);
|
|
|
|
setupDrawBlending(true, SkXfermode::kClear_Mode);
|
|
|
|
setupDrawProgram();
|
|
|
|
setupDrawPureColorUniforms();
|
|
|
|
setupDrawModelViewTranslate(0.0f, 0.0f, 0.0f, 0.0f, true);
|
2011-12-12 18:14:06 -08:00
|
|
|
setupDrawVertices(&mesh[0].position[0]);
|
2011-06-13 19:04:27 -07:00
|
|
|
|
|
|
|
glDrawArrays(GL_TRIANGLES, 0, count * 6);
|
2012-07-17 17:32:48 -07:00
|
|
|
|
|
|
|
if (scissorChanged) mCaches.enableScissor();
|
2011-06-13 19:04:27 -07:00
|
|
|
} else {
|
|
|
|
for (uint32_t i = 0; i < count; i++) {
|
|
|
|
delete mLayers.itemAt(i);
|
|
|
|
}
|
2013-02-11 18:01:20 -08:00
|
|
|
mLayers.clear();
|
2011-06-13 19:04:27 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-02-04 16:16:33 -08:00
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
// State Deferral
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
2013-03-08 13:12:16 -08:00
|
|
|
bool OpenGLRenderer::storeDisplayState(DeferredDisplayState& state, int stateDeferFlags) {
|
2013-02-04 16:16:33 -08:00
|
|
|
const Rect& currentClip = *(mSnapshot->clipRect);
|
|
|
|
const mat4& currentMatrix = *(mSnapshot->transform);
|
|
|
|
|
2013-03-08 13:12:16 -08:00
|
|
|
if (stateDeferFlags & kStateDeferFlag_Draw) {
|
|
|
|
// state has bounds initialized in local coordinates
|
|
|
|
if (!state.mBounds.isEmpty()) {
|
|
|
|
currentMatrix.mapRect(state.mBounds);
|
2013-05-31 11:38:03 -07:00
|
|
|
Rect clippedBounds(state.mBounds);
|
|
|
|
if(!clippedBounds.intersect(currentClip)) {
|
2013-03-08 13:12:16 -08:00
|
|
|
// quick rejected
|
|
|
|
return true;
|
|
|
|
}
|
2013-05-31 11:38:03 -07:00
|
|
|
|
|
|
|
state.mClipSideFlags = kClipSide_Unclipped;
|
|
|
|
if (!currentClip.contains(state.mBounds)) {
|
|
|
|
int& flags = state.mClipSideFlags;
|
|
|
|
// op partially clipped, so record which sides are clipped for clip-aware merging
|
|
|
|
if (currentClip.left > state.mBounds.left) flags |= kClipSide_Left;
|
|
|
|
if (currentClip.top > state.mBounds.top) flags |= kClipSide_Top;
|
|
|
|
if (currentClip.right < state.mBounds.right) flags |= kClipSide_Right;
|
|
|
|
if (currentClip.bottom < state.mBounds.bottom) flags |= kClipSide_Bottom;
|
|
|
|
}
|
|
|
|
state.mBounds.set(clippedBounds);
|
2013-03-08 13:12:16 -08:00
|
|
|
} else {
|
2013-05-21 15:29:59 -07:00
|
|
|
// If we don't have bounds, let's assume we're clipped
|
|
|
|
// to prevent merging
|
2013-05-31 11:38:03 -07:00
|
|
|
state.mClipSideFlags = kClipSide_Full;
|
2013-03-08 13:12:16 -08:00
|
|
|
state.mBounds.set(currentClip);
|
2013-02-04 16:16:33 -08:00
|
|
|
}
|
2013-03-08 13:12:16 -08:00
|
|
|
}
|
|
|
|
|
2013-03-04 10:19:31 -08:00
|
|
|
state.mClipValid = (stateDeferFlags & kStateDeferFlag_Clip);
|
|
|
|
if (state.mClipValid) {
|
2013-03-08 13:12:16 -08:00
|
|
|
state.mClip.set(currentClip);
|
2013-02-04 16:16:33 -08:00
|
|
|
}
|
|
|
|
|
2013-03-28 11:25:24 -07:00
|
|
|
// Transform, drawModifiers, and alpha always deferred, since they are used by state operations
|
|
|
|
// (Note: saveLayer/restore use colorFilter and alpha, so we just save restore everything)
|
2013-02-04 16:16:33 -08:00
|
|
|
state.mMatrix.load(currentMatrix);
|
2013-03-28 11:25:24 -07:00
|
|
|
state.mDrawModifiers = mDrawModifiers;
|
|
|
|
state.mAlpha = mSnapshot->alpha;
|
2013-02-04 16:16:33 -08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2013-03-04 10:19:31 -08:00
|
|
|
void OpenGLRenderer::restoreDisplayState(const DeferredDisplayState& state, bool skipClipRestore) {
|
2013-03-05 10:27:35 -08:00
|
|
|
currentTransform().load(state.mMatrix);
|
2013-03-28 11:25:24 -07:00
|
|
|
mDrawModifiers = state.mDrawModifiers;
|
|
|
|
mSnapshot->alpha = state.mAlpha;
|
2013-03-08 13:12:16 -08:00
|
|
|
|
2013-03-04 10:19:31 -08:00
|
|
|
if (state.mClipValid && !skipClipRestore) {
|
2013-05-31 11:38:03 -07:00
|
|
|
mSnapshot->setClip(state.mClip.left, state.mClip.top,
|
|
|
|
state.mClip.right, state.mClip.bottom);
|
2013-03-08 13:12:16 -08:00
|
|
|
dirtyClip();
|
|
|
|
}
|
2013-02-04 16:16:33 -08:00
|
|
|
}
|
|
|
|
|
2013-05-31 11:38:03 -07:00
|
|
|
/**
|
|
|
|
* Merged multidraw (such as in drawText and drawBitmaps rely on the fact that no clipping is done
|
|
|
|
* in the draw path. Instead, clipping is done ahead of time - either as a single clip rect (when at
|
|
|
|
* least one op is clipped), or disabled entirely (because no merged op is clipped)
|
|
|
|
*
|
|
|
|
* This method should be called when restoreDisplayState() won't be restoring the clip
|
|
|
|
*/
|
|
|
|
void OpenGLRenderer::setupMergedMultiDraw(const Rect* clipRect) {
|
|
|
|
if (clipRect != NULL) {
|
|
|
|
mSnapshot->setClip(clipRect->left, clipRect->top, clipRect->right, clipRect->bottom);
|
|
|
|
} else {
|
|
|
|
mSnapshot->setClip(0, 0, mWidth, mHeight);
|
|
|
|
}
|
2013-03-04 10:19:31 -08:00
|
|
|
dirtyClip();
|
2013-05-31 11:38:03 -07:00
|
|
|
mCaches.setScissorEnabled(clipRect != NULL || mScissorOptimizationDisabled);
|
2013-03-04 10:19:31 -08:00
|
|
|
}
|
|
|
|
|
2010-06-23 17:47:49 -07:00
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Transforms
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
void OpenGLRenderer::translate(float dx, float dy) {
|
2013-06-11 16:19:24 -07:00
|
|
|
currentTransform().translate(dx, dy);
|
2010-06-23 17:47:49 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void OpenGLRenderer::rotate(float degrees) {
|
2013-03-05 10:27:35 -08:00
|
|
|
currentTransform().rotate(degrees, 0.0f, 0.0f, 1.0f);
|
2010-06-23 17:47:49 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void OpenGLRenderer::scale(float sx, float sy) {
|
2013-03-05 10:27:35 -08:00
|
|
|
currentTransform().scale(sx, sy, 1.0f);
|
2010-06-23 17:47:49 -07:00
|
|
|
}
|
|
|
|
|
2011-01-18 11:19:19 -08:00
|
|
|
void OpenGLRenderer::skew(float sx, float sy) {
|
2013-03-05 10:27:35 -08:00
|
|
|
currentTransform().skew(sx, sy);
|
2011-01-18 11:19:19 -08:00
|
|
|
}
|
|
|
|
|
2010-06-23 17:47:49 -07:00
|
|
|
void OpenGLRenderer::setMatrix(SkMatrix* matrix) {
|
2011-10-28 14:32:20 -07:00
|
|
|
if (matrix) {
|
2013-03-05 10:27:35 -08:00
|
|
|
currentTransform().load(*matrix);
|
2011-10-28 14:32:20 -07:00
|
|
|
} else {
|
2013-03-05 10:27:35 -08:00
|
|
|
currentTransform().loadIdentity();
|
2011-10-28 14:32:20 -07:00
|
|
|
}
|
2010-06-23 17:47:49 -07:00
|
|
|
}
|
|
|
|
|
2013-02-21 11:30:22 -08:00
|
|
|
bool OpenGLRenderer::hasRectToRectTransform() {
|
2013-03-05 10:27:35 -08:00
|
|
|
return CC_LIKELY(currentTransform().rectToRect());
|
2013-02-21 11:30:22 -08:00
|
|
|
}
|
|
|
|
|
2010-06-23 17:47:49 -07:00
|
|
|
void OpenGLRenderer::getMatrix(SkMatrix* matrix) {
|
2013-03-05 10:27:35 -08:00
|
|
|
currentTransform().copyTo(*matrix);
|
2010-06-23 17:47:49 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void OpenGLRenderer::concatMatrix(SkMatrix* matrix) {
|
2010-10-15 13:57:28 -07:00
|
|
|
SkMatrix transform;
|
2013-03-05 10:27:35 -08:00
|
|
|
currentTransform().copyTo(transform);
|
2010-10-15 13:57:28 -07:00
|
|
|
transform.preConcat(*matrix);
|
2013-03-05 10:27:35 -08:00
|
|
|
currentTransform().load(transform);
|
2010-06-23 17:47:49 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Clipping
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
2010-06-22 18:56:38 -07:00
|
|
|
void OpenGLRenderer::setScissorFromClip() {
|
2010-10-15 13:57:28 -07:00
|
|
|
Rect clip(*mSnapshot->clipRect);
|
|
|
|
clip.snapToPixelBoundaries();
|
2011-12-14 19:23:32 -08:00
|
|
|
|
2012-07-17 17:32:48 -07:00
|
|
|
if (mCaches.setScissor(clip.left, mSnapshot->height - clip.bottom,
|
|
|
|
clip.getWidth(), clip.getHeight())) {
|
|
|
|
mDirtyClip = false;
|
|
|
|
}
|
2010-06-24 19:30:36 -07:00
|
|
|
}
|
|
|
|
|
2013-01-15 18:51:42 -08:00
|
|
|
void OpenGLRenderer::ensureStencilBuffer() {
|
|
|
|
// Thanks to the mismatch between EGL and OpenGL ES FBO we
|
|
|
|
// cannot attach a stencil buffer to fbo0 dynamically. Let's
|
|
|
|
// just hope we have one when hasLayer() returns false.
|
|
|
|
if (hasLayer()) {
|
|
|
|
attachStencilBufferToLayer(mSnapshot->layer);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void OpenGLRenderer::attachStencilBufferToLayer(Layer* layer) {
|
|
|
|
// The layer's FBO is already bound when we reach this stage
|
|
|
|
if (!layer->getStencilRenderBuffer()) {
|
2013-01-29 17:26:25 -08:00
|
|
|
// GL_QCOM_tiled_rendering doesn't like it if a renderbuffer
|
|
|
|
// is attached after we initiated tiling. We must turn it off,
|
|
|
|
// attach the new render buffer then turn tiling back on
|
|
|
|
endTiling();
|
|
|
|
|
2013-02-12 16:08:55 -08:00
|
|
|
RenderBuffer* buffer = mCaches.renderBufferCache.get(
|
2013-02-06 16:51:04 -08:00
|
|
|
Stencil::getSmallestStencilFormat(), layer->getWidth(), layer->getHeight());
|
2013-01-15 18:51:42 -08:00
|
|
|
layer->setStencilRenderBuffer(buffer);
|
2013-01-29 17:26:25 -08:00
|
|
|
|
2013-01-31 17:45:55 -08:00
|
|
|
startTiling(layer->clipRect, layer->layer.getHeight());
|
2013-01-15 18:51:42 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void OpenGLRenderer::setStencilFromClip() {
|
|
|
|
if (!mCaches.debugOverdraw) {
|
|
|
|
if (!mSnapshot->clipRegion->isEmpty()) {
|
|
|
|
// NOTE: The order here is important, we must set dirtyClip to false
|
|
|
|
// before any draw call to avoid calling back into this method
|
|
|
|
mDirtyClip = false;
|
|
|
|
|
|
|
|
ensureStencilBuffer();
|
|
|
|
|
|
|
|
mCaches.stencil.enableWrite();
|
|
|
|
|
|
|
|
// Clear the stencil but first make sure we restrict drawing
|
|
|
|
// to the region's bounds
|
|
|
|
bool resetScissor = mCaches.enableScissor();
|
|
|
|
if (resetScissor) {
|
|
|
|
// The scissor was not set so we now need to update it
|
|
|
|
setScissorFromClip();
|
|
|
|
}
|
|
|
|
mCaches.stencil.clear();
|
|
|
|
if (resetScissor) mCaches.disableScissor();
|
|
|
|
|
|
|
|
// NOTE: We could use the region contour path to generate a smaller mesh
|
|
|
|
// Since we are using the stencil we could use the red book path
|
|
|
|
// drawing technique. It might increase bandwidth usage though.
|
|
|
|
|
|
|
|
// The last parameter is important: we are not drawing in the color buffer
|
|
|
|
// so we don't want to dirty the current layer, if any
|
|
|
|
drawRegionRects(*mSnapshot->clipRegion, 0xff000000, SkXfermode::kSrc_Mode, false);
|
|
|
|
|
|
|
|
mCaches.stencil.enableTest();
|
2013-02-25 14:15:37 -08:00
|
|
|
|
|
|
|
// Draw the region used to generate the stencil if the appropriate debug
|
|
|
|
// mode is enabled
|
|
|
|
if (mCaches.debugStencilClip == Caches::kStencilShowRegion) {
|
|
|
|
drawRegionRects(*mSnapshot->clipRegion, 0x7f0000ff, SkXfermode::kSrcOver_Mode);
|
|
|
|
}
|
2013-01-15 18:51:42 -08:00
|
|
|
} else {
|
|
|
|
mCaches.stencil.disable();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-06-24 19:30:36 -07:00
|
|
|
const Rect& OpenGLRenderer::getClipBounds() {
|
2010-07-16 14:12:24 -07:00
|
|
|
return mSnapshot->getLocalClip();
|
2010-06-22 18:56:38 -07:00
|
|
|
}
|
|
|
|
|
2013-06-13 14:39:01 -07:00
|
|
|
bool OpenGLRenderer::quickRejectNoScissor(float left, float top, float right, float bottom,
|
|
|
|
bool* clipRequired) {
|
|
|
|
if (mSnapshot->isIgnored() || bottom <= top || right <= left) {
|
2012-07-17 17:32:48 -07:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
Rect r(left, top, right, bottom);
|
2013-03-05 10:27:35 -08:00
|
|
|
currentTransform().mapRect(r);
|
2012-07-17 17:32:48 -07:00
|
|
|
r.snapToPixelBoundaries();
|
|
|
|
|
|
|
|
Rect clipRect(*mSnapshot->clipRect);
|
|
|
|
clipRect.snapToPixelBoundaries();
|
|
|
|
|
2013-06-13 14:39:01 -07:00
|
|
|
if (!clipRect.intersects(r)) return true;
|
2012-09-18 15:40:58 -07:00
|
|
|
|
2013-06-13 14:39:01 -07:00
|
|
|
if (clipRequired) *clipRequired = !clipRect.contains(r);
|
|
|
|
return false;
|
2012-09-18 15:40:58 -07:00
|
|
|
}
|
|
|
|
|
2013-01-04 19:05:13 -08:00
|
|
|
bool OpenGLRenderer::quickRejectPreStroke(float left, float top, float right, float bottom,
|
|
|
|
SkPaint* paint) {
|
2012-09-25 12:00:29 -07:00
|
|
|
if (paint->getStyle() != SkPaint::kFill_Style) {
|
|
|
|
float outset = paint->getStrokeWidth() * 0.5f;
|
|
|
|
return quickReject(left - outset, top - outset, right + outset, bottom + outset);
|
|
|
|
} else {
|
|
|
|
return quickReject(left, top, right, bottom);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-06-25 13:41:57 -07:00
|
|
|
bool OpenGLRenderer::quickReject(float left, float top, float right, float bottom) {
|
2013-06-13 14:39:01 -07:00
|
|
|
bool clipRequired = false;
|
|
|
|
if (quickRejectNoScissor(left, top, right, bottom, &clipRequired)) {
|
2010-10-11 17:58:29 -07:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2013-06-13 14:39:01 -07:00
|
|
|
if (!isDeferred()) {
|
|
|
|
mCaches.setScissorEnabled(mScissorOptimizationDisabled || clipRequired);
|
2012-07-13 15:28:31 -07:00
|
|
|
}
|
2013-06-13 14:39:01 -07:00
|
|
|
return false;
|
2010-06-25 13:41:57 -07:00
|
|
|
}
|
|
|
|
|
2013-01-15 18:51:42 -08:00
|
|
|
void OpenGLRenderer::debugClip() {
|
2012-12-03 12:34:51 -08:00
|
|
|
#if DEBUG_CLIP_REGIONS
|
2013-01-15 18:51:42 -08:00
|
|
|
if (!isDeferred() && !mSnapshot->clipRegion->isEmpty()) {
|
|
|
|
drawRegionRects(*mSnapshot->clipRegion, 0x7f00ff00, SkXfermode::kSrcOver_Mode);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
2012-12-03 12:34:51 -08:00
|
|
|
|
2013-01-15 18:51:42 -08:00
|
|
|
bool OpenGLRenderer::clipRect(float left, float top, float right, float bottom, SkRegion::Op op) {
|
2013-03-05 10:27:35 -08:00
|
|
|
if (CC_LIKELY(currentTransform().rectToRect())) {
|
2013-01-15 18:51:42 -08:00
|
|
|
bool clipped = mSnapshot->clip(left, top, right, bottom, op);
|
|
|
|
if (clipped) {
|
|
|
|
dirtyClip();
|
2012-12-03 12:34:51 -08:00
|
|
|
}
|
2013-01-15 18:51:42 -08:00
|
|
|
return !mSnapshot->clipRect->isEmpty();
|
2010-06-25 13:46:18 -07:00
|
|
|
}
|
2013-01-15 18:51:42 -08:00
|
|
|
|
|
|
|
SkPath path;
|
|
|
|
path.addRect(left, top, right, bottom);
|
|
|
|
|
|
|
|
return clipPath(&path, op);
|
2010-06-16 18:44:05 -07:00
|
|
|
}
|
|
|
|
|
2012-12-03 12:34:51 -08:00
|
|
|
bool OpenGLRenderer::clipPath(SkPath* path, SkRegion::Op op) {
|
2013-01-15 18:51:42 -08:00
|
|
|
SkMatrix transform;
|
2013-03-05 10:27:35 -08:00
|
|
|
currentTransform().copyTo(transform);
|
2013-01-15 18:51:42 -08:00
|
|
|
|
|
|
|
SkPath transformed;
|
|
|
|
path->transform(transform, &transformed);
|
|
|
|
|
|
|
|
SkRegion clip;
|
|
|
|
if (!mSnapshot->clipRegion->isEmpty()) {
|
|
|
|
clip.setRegion(*mSnapshot->clipRegion);
|
|
|
|
} else {
|
|
|
|
Rect* bounds = mSnapshot->clipRect;
|
|
|
|
clip.setRect(bounds->left, bounds->top, bounds->right, bounds->bottom);
|
|
|
|
}
|
|
|
|
|
|
|
|
SkRegion region;
|
|
|
|
region.setPath(transformed, clip);
|
|
|
|
|
|
|
|
bool clipped = mSnapshot->clipRegionTransformed(region, op);
|
|
|
|
if (clipped) {
|
|
|
|
dirtyClip();
|
|
|
|
}
|
|
|
|
return !mSnapshot->clipRect->isEmpty();
|
2012-12-03 12:34:51 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
bool OpenGLRenderer::clipRegion(SkRegion* region, SkRegion::Op op) {
|
2013-01-15 18:51:42 -08:00
|
|
|
bool clipped = mSnapshot->clipRegionTransformed(*region, op);
|
|
|
|
if (clipped) {
|
|
|
|
dirtyClip();
|
|
|
|
}
|
|
|
|
return !mSnapshot->clipRect->isEmpty();
|
2012-12-03 12:34:51 -08:00
|
|
|
}
|
|
|
|
|
2012-04-12 15:19:04 -07:00
|
|
|
Rect* OpenGLRenderer::getClipRect() {
|
|
|
|
return mSnapshot->clipRect;
|
|
|
|
}
|
|
|
|
|
2010-12-13 18:24:33 -08:00
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Drawing commands
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
2011-06-13 19:04:27 -07:00
|
|
|
void OpenGLRenderer::setupDraw(bool clear) {
|
2012-07-17 17:32:48 -07:00
|
|
|
// TODO: It would be best if we could do this before quickReject()
|
|
|
|
// changes the scissor test state
|
2011-06-13 19:04:27 -07:00
|
|
|
if (clear) clearLayerRegions();
|
2013-01-15 18:51:42 -08:00
|
|
|
// Make sure setScissor & setStencil happen at the beginning of
|
|
|
|
// this method
|
2013-02-21 11:30:22 -08:00
|
|
|
if (mDirtyClip) {
|
|
|
|
if (mCaches.scissorEnabled) {
|
|
|
|
setScissorFromClip();
|
|
|
|
}
|
2013-01-15 18:51:42 -08:00
|
|
|
setStencilFromClip();
|
2010-12-13 18:24:33 -08:00
|
|
|
}
|
2013-02-25 14:15:37 -08:00
|
|
|
|
2010-12-13 18:24:33 -08:00
|
|
|
mDescription.reset();
|
2013-02-25 14:15:37 -08:00
|
|
|
|
2010-12-13 18:24:33 -08:00
|
|
|
mSetShaderColor = false;
|
|
|
|
mColorSet = false;
|
|
|
|
mColorA = mColorR = mColorG = mColorB = 0.0f;
|
|
|
|
mTextureUnit = 0;
|
|
|
|
mTrackDirtyRegions = true;
|
2013-02-25 14:15:37 -08:00
|
|
|
|
|
|
|
// Enable debug highlight when what we're about to draw is tested against
|
|
|
|
// the stencil buffer and if stencil highlight debugging is on
|
|
|
|
mDescription.hasDebugHighlight = !mCaches.debugOverdraw &&
|
|
|
|
mCaches.debugStencilClip == Caches::kStencilShowHighlight &&
|
|
|
|
mCaches.stencil.isTestEnabled();
|
2013-05-03 14:24:16 -07:00
|
|
|
|
|
|
|
mDescription.emulateStencil = mCountOverdraw;
|
2010-12-13 18:24:33 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
void OpenGLRenderer::setupDrawWithTexture(bool isAlpha8) {
|
|
|
|
mDescription.hasTexture = true;
|
|
|
|
mDescription.hasAlpha8Texture = isAlpha8;
|
|
|
|
}
|
|
|
|
|
2013-02-13 18:39:43 -08:00
|
|
|
void OpenGLRenderer::setupDrawWithTextureAndColor(bool isAlpha8) {
|
|
|
|
mDescription.hasTexture = true;
|
|
|
|
mDescription.hasColors = true;
|
|
|
|
mDescription.hasAlpha8Texture = isAlpha8;
|
|
|
|
}
|
|
|
|
|
New widget: TextureView
Bug #4343984
TextureView can be used to render media content (video, OpenGL,
RenderScript) inside a View.
The key difference with SurfaceView is that TextureView does
not create a new Surface. This gives the ability to seamlessly
transform, animate, fade, etc. a TextureView, which was hard
if not impossible to do with a SurfaceView.
A TextureView also interacts perfectly with ScrollView,
ListView, etc. It allows application to embed media content
in a much more flexible way than before.
For instance, to render the camera preview at 50% opacity,
all you need to do is the following:
mTextureView.setAlpha(0.5f);
Camera c = Camera.open();
c.setPreviewTexture(mTextureView.getSurfaceTexture());
c.startPreview();
TextureView uses a SurfaceTexture to get the job done. More
APIs are required to make it easy to create OpenGL contexts
for a TextureView. It can currently be done with a bit of
JNI code.
Change-Id: Iaa7953097ab5beb8437bcbbfa03b2df5b7f80cd7
2011-04-28 18:40:04 -07:00
|
|
|
void OpenGLRenderer::setupDrawWithExternalTexture() {
|
|
|
|
mDescription.hasExternalTexture = true;
|
|
|
|
}
|
|
|
|
|
2011-12-13 13:11:32 -08:00
|
|
|
void OpenGLRenderer::setupDrawNoTexture() {
|
2013-02-13 18:39:43 -08:00
|
|
|
mCaches.disableTexCoordsVertexArray();
|
2011-12-13 13:11:32 -08:00
|
|
|
}
|
|
|
|
|
2012-09-17 17:25:49 -07:00
|
|
|
void OpenGLRenderer::setupDrawAA() {
|
2011-05-02 15:00:16 -07:00
|
|
|
mDescription.isAA = true;
|
2011-04-13 17:58:08 -07:00
|
|
|
}
|
|
|
|
|
2010-12-14 20:13:35 -08:00
|
|
|
void OpenGLRenderer::setupDrawColor(int color, int alpha) {
|
|
|
|
mColorA = alpha / 255.0f;
|
2013-01-04 12:26:18 -08:00
|
|
|
mColorR = mColorA * ((color >> 16) & 0xFF) / 255.0f;
|
|
|
|
mColorG = mColorA * ((color >> 8) & 0xFF) / 255.0f;
|
|
|
|
mColorB = mColorA * ((color ) & 0xFF) / 255.0f;
|
2010-12-13 18:24:33 -08:00
|
|
|
mColorSet = true;
|
|
|
|
mSetShaderColor = mDescription.setColor(mColorR, mColorG, mColorB, mColorA);
|
|
|
|
}
|
|
|
|
|
2010-12-14 15:55:39 -08:00
|
|
|
void OpenGLRenderer::setupDrawAlpha8Color(int color, int alpha) {
|
|
|
|
mColorA = alpha / 255.0f;
|
2013-01-04 12:26:18 -08:00
|
|
|
mColorR = mColorA * ((color >> 16) & 0xFF) / 255.0f;
|
|
|
|
mColorG = mColorA * ((color >> 8) & 0xFF) / 255.0f;
|
|
|
|
mColorB = mColorA * ((color ) & 0xFF) / 255.0f;
|
2010-12-14 15:55:39 -08:00
|
|
|
mColorSet = true;
|
|
|
|
mSetShaderColor = mDescription.setAlpha8Color(mColorR, mColorG, mColorB, mColorA);
|
|
|
|
}
|
|
|
|
|
2012-07-16 17:04:24 -07:00
|
|
|
void OpenGLRenderer::setupDrawTextGamma(const SkPaint* paint) {
|
|
|
|
mCaches.fontRenderer->describe(mDescription, paint);
|
|
|
|
}
|
|
|
|
|
2010-12-13 18:24:33 -08:00
|
|
|
void OpenGLRenderer::setupDrawColor(float r, float g, float b, float a) {
|
|
|
|
mColorA = a;
|
|
|
|
mColorR = r;
|
|
|
|
mColorG = g;
|
|
|
|
mColorB = b;
|
|
|
|
mColorSet = true;
|
|
|
|
mSetShaderColor = mDescription.setColor(r, g, b, a);
|
|
|
|
}
|
|
|
|
|
|
|
|
void OpenGLRenderer::setupDrawShader() {
|
2013-02-04 16:16:33 -08:00
|
|
|
if (mDrawModifiers.mShader) {
|
|
|
|
mDrawModifiers.mShader->describe(mDescription, mExtensions);
|
2010-12-13 18:24:33 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void OpenGLRenderer::setupDrawColorFilter() {
|
2013-02-04 16:16:33 -08:00
|
|
|
if (mDrawModifiers.mColorFilter) {
|
|
|
|
mDrawModifiers.mColorFilter->describe(mDescription, mExtensions);
|
2010-12-13 18:24:33 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-05-27 11:43:46 -07:00
|
|
|
void OpenGLRenderer::accountForClear(SkXfermode::Mode mode) {
|
|
|
|
if (mColorSet && mode == SkXfermode::kClear_Mode) {
|
|
|
|
mColorA = 1.0f;
|
|
|
|
mColorR = mColorG = mColorB = 0.0f;
|
2011-06-13 19:04:27 -07:00
|
|
|
mSetShaderColor = mDescription.modulate = true;
|
2011-05-27 11:43:46 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-12-13 18:24:33 -08:00
|
|
|
void OpenGLRenderer::setupDrawBlending(SkXfermode::Mode mode, bool swapSrcDst) {
|
2011-05-27 11:43:46 -07:00
|
|
|
// When the blending mode is kClear_Mode, we need to use a modulate color
|
|
|
|
// argb=1,0,0,0
|
|
|
|
accountForClear(mode);
|
2013-02-04 16:16:33 -08:00
|
|
|
bool blend = (mColorSet && mColorA < 1.0f) ||
|
|
|
|
(mDrawModifiers.mShader && mDrawModifiers.mShader->blend());
|
|
|
|
chooseBlending(blend, mode, mDescription, swapSrcDst);
|
2010-12-13 18:24:33 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
void OpenGLRenderer::setupDrawBlending(bool blend, SkXfermode::Mode mode, bool swapSrcDst) {
|
2011-05-27 11:43:46 -07:00
|
|
|
// When the blending mode is kClear_Mode, we need to use a modulate color
|
|
|
|
// argb=1,0,0,0
|
|
|
|
accountForClear(mode);
|
2013-02-04 16:16:33 -08:00
|
|
|
blend |= (mColorSet && mColorA < 1.0f) ||
|
|
|
|
(mDrawModifiers.mShader && mDrawModifiers.mShader->blend()) ||
|
|
|
|
(mDrawModifiers.mColorFilter && mDrawModifiers.mColorFilter->blend());
|
|
|
|
chooseBlending(blend, mode, mDescription, swapSrcDst);
|
2010-12-13 18:24:33 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
void OpenGLRenderer::setupDrawProgram() {
|
|
|
|
useProgram(mCaches.programCache.get(mDescription));
|
|
|
|
}
|
|
|
|
|
|
|
|
void OpenGLRenderer::setupDrawDirtyRegionsDisabled() {
|
|
|
|
mTrackDirtyRegions = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void OpenGLRenderer::setupDrawModelViewTranslate(float left, float top, float right, float bottom,
|
|
|
|
bool ignoreTransform) {
|
|
|
|
mModelView.loadTranslate(left, top, 0.0f);
|
|
|
|
if (!ignoreTransform) {
|
2013-03-05 10:27:35 -08:00
|
|
|
mCaches.currentProgram->set(mOrthoMatrix, mModelView, currentTransform());
|
|
|
|
if (mTrackDirtyRegions) dirtyLayer(left, top, right, bottom, currentTransform());
|
2010-12-13 18:24:33 -08:00
|
|
|
} else {
|
2013-02-26 19:10:14 -08:00
|
|
|
mCaches.currentProgram->set(mOrthoMatrix, mModelView, mat4::identity());
|
2010-12-13 18:24:33 -08:00
|
|
|
if (mTrackDirtyRegions) dirtyLayer(left, top, right, bottom);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-04-26 07:28:09 -07:00
|
|
|
void OpenGLRenderer::setupDrawModelViewIdentity(bool offset) {
|
2013-03-05 10:27:35 -08:00
|
|
|
mCaches.currentProgram->set(mOrthoMatrix, mat4::identity(), currentTransform(), offset);
|
2010-12-14 20:13:35 -08:00
|
|
|
}
|
|
|
|
|
2010-12-13 18:24:33 -08:00
|
|
|
void OpenGLRenderer::setupDrawModelView(float left, float top, float right, float bottom,
|
|
|
|
bool ignoreTransform, bool ignoreModelView) {
|
|
|
|
if (!ignoreModelView) {
|
|
|
|
mModelView.loadTranslate(left, top, 0.0f);
|
|
|
|
mModelView.scale(right - left, bottom - top, 1.0f);
|
|
|
|
} else {
|
|
|
|
mModelView.loadIdentity();
|
|
|
|
}
|
2010-12-14 15:55:39 -08:00
|
|
|
bool dirty = right - left > 0.0f && bottom - top > 0.0f;
|
|
|
|
if (!ignoreTransform) {
|
2013-03-05 10:27:35 -08:00
|
|
|
mCaches.currentProgram->set(mOrthoMatrix, mModelView, currentTransform());
|
2010-12-14 15:55:39 -08:00
|
|
|
if (mTrackDirtyRegions && dirty) {
|
2013-03-05 10:27:35 -08:00
|
|
|
dirtyLayer(left, top, right, bottom, currentTransform());
|
2010-12-14 15:55:39 -08:00
|
|
|
}
|
|
|
|
} else {
|
2013-02-26 19:10:14 -08:00
|
|
|
mCaches.currentProgram->set(mOrthoMatrix, mModelView, mat4::identity());
|
2010-12-14 15:55:39 -08:00
|
|
|
if (mTrackDirtyRegions && dirty) dirtyLayer(left, top, right, bottom);
|
|
|
|
}
|
2010-12-13 18:24:33 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
void OpenGLRenderer::setupDrawColorUniforms() {
|
2013-02-04 16:16:33 -08:00
|
|
|
if ((mColorSet && !mDrawModifiers.mShader) || (mDrawModifiers.mShader && mSetShaderColor)) {
|
2010-12-13 18:24:33 -08:00
|
|
|
mCaches.currentProgram->setColor(mColorR, mColorG, mColorB, mColorA);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-12-14 15:55:39 -08:00
|
|
|
void OpenGLRenderer::setupDrawPureColorUniforms() {
|
2010-12-14 10:59:41 -08:00
|
|
|
if (mSetShaderColor) {
|
2010-12-14 15:55:39 -08:00
|
|
|
mCaches.currentProgram->setColor(mColorR, mColorG, mColorB, mColorA);
|
2010-12-14 10:59:41 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-12-13 18:24:33 -08:00
|
|
|
void OpenGLRenderer::setupDrawShaderUniforms(bool ignoreTransform) {
|
2013-02-04 16:16:33 -08:00
|
|
|
if (mDrawModifiers.mShader) {
|
2010-12-13 18:24:33 -08:00
|
|
|
if (ignoreTransform) {
|
2013-03-05 10:27:35 -08:00
|
|
|
mModelView.loadInverse(currentTransform());
|
2010-12-13 18:24:33 -08:00
|
|
|
}
|
2013-02-04 16:16:33 -08:00
|
|
|
mDrawModifiers.mShader->setupProgram(mCaches.currentProgram,
|
|
|
|
mModelView, *mSnapshot, &mTextureUnit);
|
2010-12-13 18:24:33 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-12-14 20:13:35 -08:00
|
|
|
void OpenGLRenderer::setupDrawShaderIdentityUniforms() {
|
2013-02-04 16:16:33 -08:00
|
|
|
if (mDrawModifiers.mShader) {
|
|
|
|
mDrawModifiers.mShader->setupProgram(mCaches.currentProgram,
|
2013-02-26 19:10:14 -08:00
|
|
|
mat4::identity(), *mSnapshot, &mTextureUnit);
|
2010-12-14 20:13:35 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-12-13 18:24:33 -08:00
|
|
|
void OpenGLRenderer::setupDrawColorFilterUniforms() {
|
2013-02-04 16:16:33 -08:00
|
|
|
if (mDrawModifiers.mColorFilter) {
|
|
|
|
mDrawModifiers.mColorFilter->setupProgram(mCaches.currentProgram);
|
2010-12-13 18:24:33 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-07-16 17:04:24 -07:00
|
|
|
void OpenGLRenderer::setupDrawTextGammaUniforms() {
|
|
|
|
mCaches.fontRenderer->setupProgram(mDescription, mCaches.currentProgram);
|
|
|
|
}
|
|
|
|
|
2010-12-13 18:24:33 -08:00
|
|
|
void OpenGLRenderer::setupDrawSimpleMesh() {
|
2011-12-12 20:35:21 -08:00
|
|
|
bool force = mCaches.bindMeshBuffer();
|
2012-09-25 12:00:29 -07:00
|
|
|
mCaches.bindPositionVertexPointer(force, 0);
|
2011-12-13 13:11:32 -08:00
|
|
|
mCaches.unbindIndicesBuffer();
|
2010-12-13 18:24:33 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
void OpenGLRenderer::setupDrawTexture(GLuint texture) {
|
2013-03-20 16:31:12 -07:00
|
|
|
if (texture) bindTexture(texture);
|
2011-12-13 22:00:19 -08:00
|
|
|
mTextureUnit++;
|
2011-12-13 13:11:32 -08:00
|
|
|
mCaches.enableTexCoordsVertexArray();
|
2010-12-13 18:24:33 -08:00
|
|
|
}
|
|
|
|
|
New widget: TextureView
Bug #4343984
TextureView can be used to render media content (video, OpenGL,
RenderScript) inside a View.
The key difference with SurfaceView is that TextureView does
not create a new Surface. This gives the ability to seamlessly
transform, animate, fade, etc. a TextureView, which was hard
if not impossible to do with a SurfaceView.
A TextureView also interacts perfectly with ScrollView,
ListView, etc. It allows application to embed media content
in a much more flexible way than before.
For instance, to render the camera preview at 50% opacity,
all you need to do is the following:
mTextureView.setAlpha(0.5f);
Camera c = Camera.open();
c.setPreviewTexture(mTextureView.getSurfaceTexture());
c.startPreview();
TextureView uses a SurfaceTexture to get the job done. More
APIs are required to make it easy to create OpenGL contexts
for a TextureView. It can currently be done with a bit of
JNI code.
Change-Id: Iaa7953097ab5beb8437bcbbfa03b2df5b7f80cd7
2011-04-28 18:40:04 -07:00
|
|
|
void OpenGLRenderer::setupDrawExternalTexture(GLuint texture) {
|
|
|
|
bindExternalTexture(texture);
|
2011-12-13 22:00:19 -08:00
|
|
|
mTextureUnit++;
|
2011-12-13 13:11:32 -08:00
|
|
|
mCaches.enableTexCoordsVertexArray();
|
New widget: TextureView
Bug #4343984
TextureView can be used to render media content (video, OpenGL,
RenderScript) inside a View.
The key difference with SurfaceView is that TextureView does
not create a new Surface. This gives the ability to seamlessly
transform, animate, fade, etc. a TextureView, which was hard
if not impossible to do with a SurfaceView.
A TextureView also interacts perfectly with ScrollView,
ListView, etc. It allows application to embed media content
in a much more flexible way than before.
For instance, to render the camera preview at 50% opacity,
all you need to do is the following:
mTextureView.setAlpha(0.5f);
Camera c = Camera.open();
c.setPreviewTexture(mTextureView.getSurfaceTexture());
c.startPreview();
TextureView uses a SurfaceTexture to get the job done. More
APIs are required to make it easy to create OpenGL contexts
for a TextureView. It can currently be done with a bit of
JNI code.
Change-Id: Iaa7953097ab5beb8437bcbbfa03b2df5b7f80cd7
2011-04-28 18:40:04 -07:00
|
|
|
}
|
|
|
|
|
2011-05-02 17:24:22 -07:00
|
|
|
void OpenGLRenderer::setupDrawTextureTransform() {
|
|
|
|
mDescription.hasTextureTransform = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void OpenGLRenderer::setupDrawTextureTransformUniforms(mat4& transform) {
|
New widget: TextureView
Bug #4343984
TextureView can be used to render media content (video, OpenGL,
RenderScript) inside a View.
The key difference with SurfaceView is that TextureView does
not create a new Surface. This gives the ability to seamlessly
transform, animate, fade, etc. a TextureView, which was hard
if not impossible to do with a SurfaceView.
A TextureView also interacts perfectly with ScrollView,
ListView, etc. It allows application to embed media content
in a much more flexible way than before.
For instance, to render the camera preview at 50% opacity,
all you need to do is the following:
mTextureView.setAlpha(0.5f);
Camera c = Camera.open();
c.setPreviewTexture(mTextureView.getSurfaceTexture());
c.startPreview();
TextureView uses a SurfaceTexture to get the job done. More
APIs are required to make it easy to create OpenGL contexts
for a TextureView. It can currently be done with a bit of
JNI code.
Change-Id: Iaa7953097ab5beb8437bcbbfa03b2df5b7f80cd7
2011-04-28 18:40:04 -07:00
|
|
|
glUniformMatrix4fv(mCaches.currentProgram->getUniform("mainTextureTransform"), 1,
|
|
|
|
GL_FALSE, &transform.data[0]);
|
|
|
|
}
|
|
|
|
|
2010-12-13 18:24:33 -08:00
|
|
|
void OpenGLRenderer::setupDrawMesh(GLvoid* vertices, GLvoid* texCoords, GLuint vbo) {
|
2011-12-12 20:35:21 -08:00
|
|
|
bool force = false;
|
Pack preloaded framework assets in a texture atlas
When the Android runtime starts, the system preloads a series of assets
in the Zygote process. These assets are shared across all processes.
Unfortunately, each one of these assets is later uploaded in its own
OpenGL texture, once per process. This wastes memory and generates
unnecessary OpenGL state changes.
This CL introduces an asset server that provides an atlas to all processes.
Note: bitmaps used by skia shaders are *not* sampled from the atlas.
It's an uncommon use case and would require extra texture transforms
in the GL shaders.
WHAT IS THE ASSETS ATLAS
The "assets atlas" is a single, shareable graphic buffer that contains
all the system's preloaded bitmap drawables (this includes 9-patches.)
The atlas is made of two distinct objects: the graphic buffer that
contains the actual pixels and the map which indicates where each
preloaded bitmap can be found in the atlas (essentially a pair of
x and y coordinates.)
HOW IS THE ASSETS ATLAS GENERATED
Because we need to support a wide variety of devices and because it
is easy to change the list of preloaded drawables, the atlas is
generated at runtime, during the startup phase of the system process.
There are several steps that lead to the atlas generation:
1. If the device is booting for the first time, or if the device was
updated, we need to find the best atlas configuration. To do so,
the atlas service tries a number of width, height and algorithm
variations that allows us to pack as many assets as possible while
using as little memory as possible. Once a best configuration is found,
it gets written to disk in /data/system/framework_atlas
2. Given a best configuration (algorithm variant, dimensions and
number of bitmaps that can be packed in the atlas), the atlas service
packs all the preloaded bitmaps into a single graphic buffer object.
3. The packing is done using Skia in a temporary native bitmap. The
Skia bitmap is then copied into the graphic buffer using OpenGL ES
to benefit from texture swizzling.
HOW PROCESSES USE THE ATLAS
Whenever a process' hardware renderer initializes its EGL context,
it queries the atlas service for the graphic buffer and the map.
It is important to remember that both the context and the map will
be valid for the lifetime of the hardware renderer (if the system
process goes down, all apps get killed as well.)
Every time the hardware renderer needs to render a bitmap, it first
checks whether the bitmap can be found in the assets atlas. When
the bitmap is part of the atlas, texture coordinates are remapped
appropriately before rendering.
Change-Id: I8eaecf53e7f6a33d90da3d0047c5ceec89ea3af0
2013-04-17 18:54:38 -07:00
|
|
|
if (!vertices || vbo) {
|
2011-12-12 20:35:21 -08:00
|
|
|
force = mCaches.bindMeshBuffer(vbo == 0 ? mCaches.meshBuffer : vbo);
|
2010-12-13 18:24:33 -08:00
|
|
|
} else {
|
2011-12-12 20:35:21 -08:00
|
|
|
force = mCaches.unbindMeshBuffer();
|
2010-12-13 18:24:33 -08:00
|
|
|
}
|
2011-12-12 19:03:35 -08:00
|
|
|
|
2012-09-25 12:00:29 -07:00
|
|
|
mCaches.bindPositionVertexPointer(force, vertices);
|
2011-12-13 13:11:32 -08:00
|
|
|
if (mCaches.currentProgram->texCoords >= 0) {
|
2012-09-25 12:00:29 -07:00
|
|
|
mCaches.bindTexCoordsVertexPointer(force, texCoords);
|
2011-12-13 13:11:32 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
mCaches.unbindIndicesBuffer();
|
|
|
|
}
|
|
|
|
|
2013-02-13 18:39:43 -08:00
|
|
|
void OpenGLRenderer::setupDrawMesh(GLvoid* vertices, GLvoid* texCoords, GLvoid* colors) {
|
|
|
|
bool force = mCaches.unbindMeshBuffer();
|
|
|
|
GLsizei stride = sizeof(ColorTextureVertex);
|
|
|
|
|
|
|
|
mCaches.bindPositionVertexPointer(force, vertices, stride);
|
|
|
|
if (mCaches.currentProgram->texCoords >= 0) {
|
|
|
|
mCaches.bindTexCoordsVertexPointer(force, texCoords, stride);
|
|
|
|
}
|
|
|
|
int slot = mCaches.currentProgram->getAttrib("colors");
|
|
|
|
if (slot >= 0) {
|
|
|
|
glEnableVertexAttribArray(slot);
|
|
|
|
glVertexAttribPointer(slot, 4, GL_FLOAT, GL_FALSE, stride, colors);
|
|
|
|
}
|
|
|
|
|
|
|
|
mCaches.unbindIndicesBuffer();
|
|
|
|
}
|
|
|
|
|
Pack preloaded framework assets in a texture atlas
When the Android runtime starts, the system preloads a series of assets
in the Zygote process. These assets are shared across all processes.
Unfortunately, each one of these assets is later uploaded in its own
OpenGL texture, once per process. This wastes memory and generates
unnecessary OpenGL state changes.
This CL introduces an asset server that provides an atlas to all processes.
Note: bitmaps used by skia shaders are *not* sampled from the atlas.
It's an uncommon use case and would require extra texture transforms
in the GL shaders.
WHAT IS THE ASSETS ATLAS
The "assets atlas" is a single, shareable graphic buffer that contains
all the system's preloaded bitmap drawables (this includes 9-patches.)
The atlas is made of two distinct objects: the graphic buffer that
contains the actual pixels and the map which indicates where each
preloaded bitmap can be found in the atlas (essentially a pair of
x and y coordinates.)
HOW IS THE ASSETS ATLAS GENERATED
Because we need to support a wide variety of devices and because it
is easy to change the list of preloaded drawables, the atlas is
generated at runtime, during the startup phase of the system process.
There are several steps that lead to the atlas generation:
1. If the device is booting for the first time, or if the device was
updated, we need to find the best atlas configuration. To do so,
the atlas service tries a number of width, height and algorithm
variations that allows us to pack as many assets as possible while
using as little memory as possible. Once a best configuration is found,
it gets written to disk in /data/system/framework_atlas
2. Given a best configuration (algorithm variant, dimensions and
number of bitmaps that can be packed in the atlas), the atlas service
packs all the preloaded bitmaps into a single graphic buffer object.
3. The packing is done using Skia in a temporary native bitmap. The
Skia bitmap is then copied into the graphic buffer using OpenGL ES
to benefit from texture swizzling.
HOW PROCESSES USE THE ATLAS
Whenever a process' hardware renderer initializes its EGL context,
it queries the atlas service for the graphic buffer and the map.
It is important to remember that both the context and the map will
be valid for the lifetime of the hardware renderer (if the system
process goes down, all apps get killed as well.)
Every time the hardware renderer needs to render a bitmap, it first
checks whether the bitmap can be found in the assets atlas. When
the bitmap is part of the atlas, texture coordinates are remapped
appropriately before rendering.
Change-Id: I8eaecf53e7f6a33d90da3d0047c5ceec89ea3af0
2013-04-17 18:54:38 -07:00
|
|
|
void OpenGLRenderer::setupDrawMeshIndices(GLvoid* vertices, GLvoid* texCoords, GLuint vbo) {
|
|
|
|
bool force = false;
|
|
|
|
// If vbo is != 0 we want to treat the vertices parameter as an offset inside
|
|
|
|
// a VBO. However, if vertices is set to NULL and vbo == 0 then we want to
|
|
|
|
// use the default VBO found in Caches
|
|
|
|
if (!vertices || vbo) {
|
|
|
|
force = mCaches.bindMeshBuffer(vbo == 0 ? mCaches.meshBuffer : vbo);
|
|
|
|
} else {
|
|
|
|
force = mCaches.unbindMeshBuffer();
|
|
|
|
}
|
|
|
|
mCaches.bindIndicesBuffer();
|
|
|
|
|
2012-09-25 12:00:29 -07:00
|
|
|
mCaches.bindPositionVertexPointer(force, vertices);
|
2011-12-13 13:11:32 -08:00
|
|
|
if (mCaches.currentProgram->texCoords >= 0) {
|
2012-09-25 12:00:29 -07:00
|
|
|
mCaches.bindTexCoordsVertexPointer(force, texCoords);
|
2010-12-14 20:13:35 -08:00
|
|
|
}
|
2010-12-13 18:24:33 -08:00
|
|
|
}
|
|
|
|
|
2011-04-13 17:58:08 -07:00
|
|
|
void OpenGLRenderer::setupDrawVertices(GLvoid* vertices) {
|
2011-12-12 20:35:21 -08:00
|
|
|
bool force = mCaches.unbindMeshBuffer();
|
2012-09-25 12:00:29 -07:00
|
|
|
mCaches.bindPositionVertexPointer(force, vertices, gVertexStride);
|
2011-12-13 13:11:32 -08:00
|
|
|
mCaches.unbindIndicesBuffer();
|
2011-04-13 17:58:08 -07:00
|
|
|
}
|
|
|
|
|
2010-12-13 18:24:33 -08:00
|
|
|
void OpenGLRenderer::finishDrawTexture() {
|
|
|
|
}
|
|
|
|
|
2010-06-23 17:47:49 -07:00
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Drawing
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
2013-03-08 13:12:16 -08:00
|
|
|
status_t OpenGLRenderer::drawDisplayList(DisplayList* displayList, Rect& dirty,
|
|
|
|
int32_t replayFlags) {
|
2013-04-10 07:43:29 -07:00
|
|
|
status_t status;
|
2010-11-08 12:08:41 -08:00
|
|
|
// All the usual checks and setup operations (quickReject, setupDraw, etc.)
|
|
|
|
// will be performed by the display list itself
|
2011-08-25 14:01:48 -07:00
|
|
|
if (displayList && displayList->isRenderable()) {
|
2013-03-19 15:03:48 -07:00
|
|
|
if (CC_UNLIKELY(mCaches.drawDeferDisabled)) {
|
2013-04-10 07:43:29 -07:00
|
|
|
status = startFrame();
|
2013-03-08 13:12:16 -08:00
|
|
|
ReplayStateStruct replayStruct(*this, dirty, replayFlags);
|
|
|
|
displayList->replay(replayStruct, 0);
|
2013-04-10 07:43:29 -07:00
|
|
|
return status | replayStruct.mDrawGlStatus;
|
2013-02-04 16:16:33 -08:00
|
|
|
}
|
|
|
|
|
2013-05-31 11:38:03 -07:00
|
|
|
bool avoidOverdraw = !mCaches.debugOverdraw && !mCountOverdraw; // shh, don't tell devs!
|
|
|
|
DeferredDisplayList deferredList(*(mSnapshot->clipRect), avoidOverdraw);
|
2013-03-08 13:12:16 -08:00
|
|
|
DeferStateStruct deferStruct(deferredList, *this, replayFlags);
|
|
|
|
displayList->defer(deferStruct, 0);
|
2013-03-26 15:05:58 -07:00
|
|
|
|
|
|
|
flushLayers();
|
2013-04-10 07:43:29 -07:00
|
|
|
status = startFrame();
|
2013-03-26 15:05:58 -07:00
|
|
|
|
2013-04-10 07:43:29 -07:00
|
|
|
return status | deferredList.flush(*this, dirty);
|
2010-11-08 12:08:41 -08:00
|
|
|
}
|
2011-03-14 18:05:08 -07:00
|
|
|
|
2012-03-26 16:45:05 -07:00
|
|
|
return DrawGlInfo::kStatusDone;
|
2010-11-08 12:08:41 -08:00
|
|
|
}
|
|
|
|
|
2013-02-04 16:16:33 -08:00
|
|
|
void OpenGLRenderer::outputDisplayList(DisplayList* displayList) {
|
2011-04-22 16:18:45 -07:00
|
|
|
if (displayList) {
|
2013-02-22 11:48:16 -08:00
|
|
|
displayList->output(1);
|
2011-04-22 16:18:45 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-03-18 16:50:13 -07:00
|
|
|
void OpenGLRenderer::drawAlphaBitmap(Texture* texture, float left, float top, SkPaint* paint) {
|
|
|
|
int alpha;
|
|
|
|
SkXfermode::Mode mode;
|
|
|
|
getAlphaAndMode(paint, &alpha, &mode);
|
|
|
|
|
2013-01-04 12:26:18 -08:00
|
|
|
int color = paint != NULL ? paint->getColor() : 0;
|
|
|
|
|
2011-03-18 16:50:13 -07:00
|
|
|
float x = left;
|
|
|
|
float y = top;
|
|
|
|
|
2013-01-04 12:26:18 -08:00
|
|
|
texture->setWrap(GL_CLAMP_TO_EDGE, true);
|
|
|
|
|
2011-03-18 16:50:13 -07:00
|
|
|
bool ignoreTransform = false;
|
2013-03-05 10:27:35 -08:00
|
|
|
if (currentTransform().isPureTranslate()) {
|
|
|
|
x = (int) floorf(left + currentTransform().getTranslateX() + 0.5f);
|
|
|
|
y = (int) floorf(top + currentTransform().getTranslateY() + 0.5f);
|
2011-03-18 16:50:13 -07:00
|
|
|
ignoreTransform = true;
|
|
|
|
|
2013-01-04 12:26:18 -08:00
|
|
|
texture->setFilter(GL_NEAREST, true);
|
|
|
|
} else {
|
|
|
|
texture->setFilter(FILTER(paint), true);
|
2011-03-23 17:15:38 -07:00
|
|
|
}
|
2011-07-25 16:36:01 -07:00
|
|
|
|
Pack preloaded framework assets in a texture atlas
When the Android runtime starts, the system preloads a series of assets
in the Zygote process. These assets are shared across all processes.
Unfortunately, each one of these assets is later uploaded in its own
OpenGL texture, once per process. This wastes memory and generates
unnecessary OpenGL state changes.
This CL introduces an asset server that provides an atlas to all processes.
Note: bitmaps used by skia shaders are *not* sampled from the atlas.
It's an uncommon use case and would require extra texture transforms
in the GL shaders.
WHAT IS THE ASSETS ATLAS
The "assets atlas" is a single, shareable graphic buffer that contains
all the system's preloaded bitmap drawables (this includes 9-patches.)
The atlas is made of two distinct objects: the graphic buffer that
contains the actual pixels and the map which indicates where each
preloaded bitmap can be found in the atlas (essentially a pair of
x and y coordinates.)
HOW IS THE ASSETS ATLAS GENERATED
Because we need to support a wide variety of devices and because it
is easy to change the list of preloaded drawables, the atlas is
generated at runtime, during the startup phase of the system process.
There are several steps that lead to the atlas generation:
1. If the device is booting for the first time, or if the device was
updated, we need to find the best atlas configuration. To do so,
the atlas service tries a number of width, height and algorithm
variations that allows us to pack as many assets as possible while
using as little memory as possible. Once a best configuration is found,
it gets written to disk in /data/system/framework_atlas
2. Given a best configuration (algorithm variant, dimensions and
number of bitmaps that can be packed in the atlas), the atlas service
packs all the preloaded bitmaps into a single graphic buffer object.
3. The packing is done using Skia in a temporary native bitmap. The
Skia bitmap is then copied into the graphic buffer using OpenGL ES
to benefit from texture swizzling.
HOW PROCESSES USE THE ATLAS
Whenever a process' hardware renderer initializes its EGL context,
it queries the atlas service for the graphic buffer and the map.
It is important to remember that both the context and the map will
be valid for the lifetime of the hardware renderer (if the system
process goes down, all apps get killed as well.)
Every time the hardware renderer needs to render a bitmap, it first
checks whether the bitmap can be found in the assets atlas. When
the bitmap is part of the atlas, texture coordinates are remapped
appropriately before rendering.
Change-Id: I8eaecf53e7f6a33d90da3d0047c5ceec89ea3af0
2013-04-17 18:54:38 -07:00
|
|
|
// No need to check for a UV mapper on the texture object, only ARGB_8888
|
|
|
|
// bitmaps get packed in the atlas
|
2013-01-04 12:26:18 -08:00
|
|
|
drawAlpha8TextureMesh(x, y, x + texture->width, y + texture->height, texture->id,
|
Pack preloaded framework assets in a texture atlas
When the Android runtime starts, the system preloads a series of assets
in the Zygote process. These assets are shared across all processes.
Unfortunately, each one of these assets is later uploaded in its own
OpenGL texture, once per process. This wastes memory and generates
unnecessary OpenGL state changes.
This CL introduces an asset server that provides an atlas to all processes.
Note: bitmaps used by skia shaders are *not* sampled from the atlas.
It's an uncommon use case and would require extra texture transforms
in the GL shaders.
WHAT IS THE ASSETS ATLAS
The "assets atlas" is a single, shareable graphic buffer that contains
all the system's preloaded bitmap drawables (this includes 9-patches.)
The atlas is made of two distinct objects: the graphic buffer that
contains the actual pixels and the map which indicates where each
preloaded bitmap can be found in the atlas (essentially a pair of
x and y coordinates.)
HOW IS THE ASSETS ATLAS GENERATED
Because we need to support a wide variety of devices and because it
is easy to change the list of preloaded drawables, the atlas is
generated at runtime, during the startup phase of the system process.
There are several steps that lead to the atlas generation:
1. If the device is booting for the first time, or if the device was
updated, we need to find the best atlas configuration. To do so,
the atlas service tries a number of width, height and algorithm
variations that allows us to pack as many assets as possible while
using as little memory as possible. Once a best configuration is found,
it gets written to disk in /data/system/framework_atlas
2. Given a best configuration (algorithm variant, dimensions and
number of bitmaps that can be packed in the atlas), the atlas service
packs all the preloaded bitmaps into a single graphic buffer object.
3. The packing is done using Skia in a temporary native bitmap. The
Skia bitmap is then copied into the graphic buffer using OpenGL ES
to benefit from texture swizzling.
HOW PROCESSES USE THE ATLAS
Whenever a process' hardware renderer initializes its EGL context,
it queries the atlas service for the graphic buffer and the map.
It is important to remember that both the context and the map will
be valid for the lifetime of the hardware renderer (if the system
process goes down, all apps get killed as well.)
Every time the hardware renderer needs to render a bitmap, it first
checks whether the bitmap can be found in the assets atlas. When
the bitmap is part of the atlas, texture coordinates are remapped
appropriately before rendering.
Change-Id: I8eaecf53e7f6a33d90da3d0047c5ceec89ea3af0
2013-04-17 18:54:38 -07:00
|
|
|
paint != NULL, color, alpha, mode, (GLvoid*) NULL, (GLvoid*) gMeshTextureOffset,
|
|
|
|
GL_TRIANGLE_STRIP, gMeshCount, ignoreTransform);
|
2011-03-18 16:50:13 -07:00
|
|
|
}
|
|
|
|
|
2013-03-04 10:19:31 -08:00
|
|
|
status_t OpenGLRenderer::drawBitmaps(SkBitmap* bitmap, int bitmapCount, TextureVertex* vertices,
|
2013-05-21 15:29:59 -07:00
|
|
|
bool transformed, const Rect& bounds, SkPaint* paint) {
|
2013-03-04 10:19:31 -08:00
|
|
|
mCaches.activeTexture(0);
|
Pack preloaded framework assets in a texture atlas
When the Android runtime starts, the system preloads a series of assets
in the Zygote process. These assets are shared across all processes.
Unfortunately, each one of these assets is later uploaded in its own
OpenGL texture, once per process. This wastes memory and generates
unnecessary OpenGL state changes.
This CL introduces an asset server that provides an atlas to all processes.
Note: bitmaps used by skia shaders are *not* sampled from the atlas.
It's an uncommon use case and would require extra texture transforms
in the GL shaders.
WHAT IS THE ASSETS ATLAS
The "assets atlas" is a single, shareable graphic buffer that contains
all the system's preloaded bitmap drawables (this includes 9-patches.)
The atlas is made of two distinct objects: the graphic buffer that
contains the actual pixels and the map which indicates where each
preloaded bitmap can be found in the atlas (essentially a pair of
x and y coordinates.)
HOW IS THE ASSETS ATLAS GENERATED
Because we need to support a wide variety of devices and because it
is easy to change the list of preloaded drawables, the atlas is
generated at runtime, during the startup phase of the system process.
There are several steps that lead to the atlas generation:
1. If the device is booting for the first time, or if the device was
updated, we need to find the best atlas configuration. To do so,
the atlas service tries a number of width, height and algorithm
variations that allows us to pack as many assets as possible while
using as little memory as possible. Once a best configuration is found,
it gets written to disk in /data/system/framework_atlas
2. Given a best configuration (algorithm variant, dimensions and
number of bitmaps that can be packed in the atlas), the atlas service
packs all the preloaded bitmaps into a single graphic buffer object.
3. The packing is done using Skia in a temporary native bitmap. The
Skia bitmap is then copied into the graphic buffer using OpenGL ES
to benefit from texture swizzling.
HOW PROCESSES USE THE ATLAS
Whenever a process' hardware renderer initializes its EGL context,
it queries the atlas service for the graphic buffer and the map.
It is important to remember that both the context and the map will
be valid for the lifetime of the hardware renderer (if the system
process goes down, all apps get killed as well.)
Every time the hardware renderer needs to render a bitmap, it first
checks whether the bitmap can be found in the assets atlas. When
the bitmap is part of the atlas, texture coordinates are remapped
appropriately before rendering.
Change-Id: I8eaecf53e7f6a33d90da3d0047c5ceec89ea3af0
2013-04-17 18:54:38 -07:00
|
|
|
Texture* texture = getTexture(bitmap);
|
2013-03-04 10:19:31 -08:00
|
|
|
if (!texture) return DrawGlInfo::kStatusDone;
|
Pack preloaded framework assets in a texture atlas
When the Android runtime starts, the system preloads a series of assets
in the Zygote process. These assets are shared across all processes.
Unfortunately, each one of these assets is later uploaded in its own
OpenGL texture, once per process. This wastes memory and generates
unnecessary OpenGL state changes.
This CL introduces an asset server that provides an atlas to all processes.
Note: bitmaps used by skia shaders are *not* sampled from the atlas.
It's an uncommon use case and would require extra texture transforms
in the GL shaders.
WHAT IS THE ASSETS ATLAS
The "assets atlas" is a single, shareable graphic buffer that contains
all the system's preloaded bitmap drawables (this includes 9-patches.)
The atlas is made of two distinct objects: the graphic buffer that
contains the actual pixels and the map which indicates where each
preloaded bitmap can be found in the atlas (essentially a pair of
x and y coordinates.)
HOW IS THE ASSETS ATLAS GENERATED
Because we need to support a wide variety of devices and because it
is easy to change the list of preloaded drawables, the atlas is
generated at runtime, during the startup phase of the system process.
There are several steps that lead to the atlas generation:
1. If the device is booting for the first time, or if the device was
updated, we need to find the best atlas configuration. To do so,
the atlas service tries a number of width, height and algorithm
variations that allows us to pack as many assets as possible while
using as little memory as possible. Once a best configuration is found,
it gets written to disk in /data/system/framework_atlas
2. Given a best configuration (algorithm variant, dimensions and
number of bitmaps that can be packed in the atlas), the atlas service
packs all the preloaded bitmaps into a single graphic buffer object.
3. The packing is done using Skia in a temporary native bitmap. The
Skia bitmap is then copied into the graphic buffer using OpenGL ES
to benefit from texture swizzling.
HOW PROCESSES USE THE ATLAS
Whenever a process' hardware renderer initializes its EGL context,
it queries the atlas service for the graphic buffer and the map.
It is important to remember that both the context and the map will
be valid for the lifetime of the hardware renderer (if the system
process goes down, all apps get killed as well.)
Every time the hardware renderer needs to render a bitmap, it first
checks whether the bitmap can be found in the assets atlas. When
the bitmap is part of the atlas, texture coordinates are remapped
appropriately before rendering.
Change-Id: I8eaecf53e7f6a33d90da3d0047c5ceec89ea3af0
2013-04-17 18:54:38 -07:00
|
|
|
|
2013-03-04 10:19:31 -08:00
|
|
|
const AutoTexture autoCleanup(texture);
|
|
|
|
|
|
|
|
int alpha;
|
|
|
|
SkXfermode::Mode mode;
|
|
|
|
getAlphaAndMode(paint, &alpha, &mode);
|
|
|
|
|
|
|
|
texture->setWrap(GL_CLAMP_TO_EDGE, true);
|
2013-05-21 15:29:59 -07:00
|
|
|
texture->setFilter(transformed ? FILTER(paint) : GL_NEAREST, true);
|
2013-03-04 10:19:31 -08:00
|
|
|
|
|
|
|
const float x = (int) floorf(bounds.left + 0.5f);
|
|
|
|
const float y = (int) floorf(bounds.top + 0.5f);
|
|
|
|
if (CC_UNLIKELY(bitmap->getConfig() == SkBitmap::kA8_Config)) {
|
|
|
|
int color = paint != NULL ? paint->getColor() : 0;
|
|
|
|
drawAlpha8TextureMesh(x, y, x + bounds.getWidth(), y + bounds.getHeight(),
|
|
|
|
texture->id, paint != NULL, color, alpha, mode,
|
|
|
|
&vertices[0].position[0], &vertices[0].texture[0],
|
|
|
|
GL_TRIANGLES, bitmapCount * 6, true, true);
|
|
|
|
} else {
|
|
|
|
drawTextureMesh(x, y, x + bounds.getWidth(), y + bounds.getHeight(),
|
|
|
|
texture->id, alpha / 255.0f, mode, texture->blend,
|
|
|
|
&vertices[0].position[0], &vertices[0].texture[0],
|
|
|
|
GL_TRIANGLES, bitmapCount * 6, false, true, 0, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
return DrawGlInfo::kStatusDrew;
|
|
|
|
}
|
|
|
|
|
2012-05-31 15:21:51 -07:00
|
|
|
status_t OpenGLRenderer::drawBitmap(SkBitmap* bitmap, float left, float top, SkPaint* paint) {
|
2010-07-12 20:20:03 -07:00
|
|
|
const float right = left + bitmap->width();
|
|
|
|
const float bottom = top + bitmap->height();
|
|
|
|
|
|
|
|
if (quickReject(left, top, right, bottom)) {
|
2012-05-31 15:21:51 -07:00
|
|
|
return DrawGlInfo::kStatusDone;
|
2010-07-12 20:20:03 -07:00
|
|
|
}
|
|
|
|
|
2011-12-13 14:55:06 -08:00
|
|
|
mCaches.activeTexture(0);
|
Pack preloaded framework assets in a texture atlas
When the Android runtime starts, the system preloads a series of assets
in the Zygote process. These assets are shared across all processes.
Unfortunately, each one of these assets is later uploaded in its own
OpenGL texture, once per process. This wastes memory and generates
unnecessary OpenGL state changes.
This CL introduces an asset server that provides an atlas to all processes.
Note: bitmaps used by skia shaders are *not* sampled from the atlas.
It's an uncommon use case and would require extra texture transforms
in the GL shaders.
WHAT IS THE ASSETS ATLAS
The "assets atlas" is a single, shareable graphic buffer that contains
all the system's preloaded bitmap drawables (this includes 9-patches.)
The atlas is made of two distinct objects: the graphic buffer that
contains the actual pixels and the map which indicates where each
preloaded bitmap can be found in the atlas (essentially a pair of
x and y coordinates.)
HOW IS THE ASSETS ATLAS GENERATED
Because we need to support a wide variety of devices and because it
is easy to change the list of preloaded drawables, the atlas is
generated at runtime, during the startup phase of the system process.
There are several steps that lead to the atlas generation:
1. If the device is booting for the first time, or if the device was
updated, we need to find the best atlas configuration. To do so,
the atlas service tries a number of width, height and algorithm
variations that allows us to pack as many assets as possible while
using as little memory as possible. Once a best configuration is found,
it gets written to disk in /data/system/framework_atlas
2. Given a best configuration (algorithm variant, dimensions and
number of bitmaps that can be packed in the atlas), the atlas service
packs all the preloaded bitmaps into a single graphic buffer object.
3. The packing is done using Skia in a temporary native bitmap. The
Skia bitmap is then copied into the graphic buffer using OpenGL ES
to benefit from texture swizzling.
HOW PROCESSES USE THE ATLAS
Whenever a process' hardware renderer initializes its EGL context,
it queries the atlas service for the graphic buffer and the map.
It is important to remember that both the context and the map will
be valid for the lifetime of the hardware renderer (if the system
process goes down, all apps get killed as well.)
Every time the hardware renderer needs to render a bitmap, it first
checks whether the bitmap can be found in the assets atlas. When
the bitmap is part of the atlas, texture coordinates are remapped
appropriately before rendering.
Change-Id: I8eaecf53e7f6a33d90da3d0047c5ceec89ea3af0
2013-04-17 18:54:38 -07:00
|
|
|
Texture* texture = getTexture(bitmap);
|
2012-05-31 15:21:51 -07:00
|
|
|
if (!texture) return DrawGlInfo::kStatusDone;
|
2010-08-06 11:18:34 -07:00
|
|
|
const AutoTexture autoCleanup(texture);
|
|
|
|
|
2012-02-01 16:10:55 -08:00
|
|
|
if (CC_UNLIKELY(bitmap->getConfig() == SkBitmap::kA8_Config)) {
|
2011-03-18 16:50:13 -07:00
|
|
|
drawAlphaBitmap(texture, left, top, paint);
|
|
|
|
} else {
|
|
|
|
drawTextureRect(left, top, right, bottom, texture, paint);
|
|
|
|
}
|
2012-05-31 15:21:51 -07:00
|
|
|
|
|
|
|
return DrawGlInfo::kStatusDrew;
|
2010-06-30 19:21:21 -07:00
|
|
|
}
|
|
|
|
|
2012-05-31 15:21:51 -07:00
|
|
|
status_t OpenGLRenderer::drawBitmap(SkBitmap* bitmap, SkMatrix* matrix, SkPaint* paint) {
|
2010-07-01 11:05:42 -07:00
|
|
|
Rect r(0.0f, 0.0f, bitmap->width(), bitmap->height());
|
|
|
|
const mat4 transform(*matrix);
|
|
|
|
transform.mapRect(r);
|
|
|
|
|
2010-07-12 20:20:03 -07:00
|
|
|
if (quickReject(r.left, r.top, r.right, r.bottom)) {
|
2012-05-31 15:21:51 -07:00
|
|
|
return DrawGlInfo::kStatusDone;
|
2010-07-12 20:20:03 -07:00
|
|
|
}
|
|
|
|
|
2011-12-13 14:55:06 -08:00
|
|
|
mCaches.activeTexture(0);
|
Pack preloaded framework assets in a texture atlas
When the Android runtime starts, the system preloads a series of assets
in the Zygote process. These assets are shared across all processes.
Unfortunately, each one of these assets is later uploaded in its own
OpenGL texture, once per process. This wastes memory and generates
unnecessary OpenGL state changes.
This CL introduces an asset server that provides an atlas to all processes.
Note: bitmaps used by skia shaders are *not* sampled from the atlas.
It's an uncommon use case and would require extra texture transforms
in the GL shaders.
WHAT IS THE ASSETS ATLAS
The "assets atlas" is a single, shareable graphic buffer that contains
all the system's preloaded bitmap drawables (this includes 9-patches.)
The atlas is made of two distinct objects: the graphic buffer that
contains the actual pixels and the map which indicates where each
preloaded bitmap can be found in the atlas (essentially a pair of
x and y coordinates.)
HOW IS THE ASSETS ATLAS GENERATED
Because we need to support a wide variety of devices and because it
is easy to change the list of preloaded drawables, the atlas is
generated at runtime, during the startup phase of the system process.
There are several steps that lead to the atlas generation:
1. If the device is booting for the first time, or if the device was
updated, we need to find the best atlas configuration. To do so,
the atlas service tries a number of width, height and algorithm
variations that allows us to pack as many assets as possible while
using as little memory as possible. Once a best configuration is found,
it gets written to disk in /data/system/framework_atlas
2. Given a best configuration (algorithm variant, dimensions and
number of bitmaps that can be packed in the atlas), the atlas service
packs all the preloaded bitmaps into a single graphic buffer object.
3. The packing is done using Skia in a temporary native bitmap. The
Skia bitmap is then copied into the graphic buffer using OpenGL ES
to benefit from texture swizzling.
HOW PROCESSES USE THE ATLAS
Whenever a process' hardware renderer initializes its EGL context,
it queries the atlas service for the graphic buffer and the map.
It is important to remember that both the context and the map will
be valid for the lifetime of the hardware renderer (if the system
process goes down, all apps get killed as well.)
Every time the hardware renderer needs to render a bitmap, it first
checks whether the bitmap can be found in the assets atlas. When
the bitmap is part of the atlas, texture coordinates are remapped
appropriately before rendering.
Change-Id: I8eaecf53e7f6a33d90da3d0047c5ceec89ea3af0
2013-04-17 18:54:38 -07:00
|
|
|
Texture* texture = getTexture(bitmap);
|
2012-05-31 15:21:51 -07:00
|
|
|
if (!texture) return DrawGlInfo::kStatusDone;
|
2010-08-06 11:18:34 -07:00
|
|
|
const AutoTexture autoCleanup(texture);
|
|
|
|
|
2010-10-27 18:57:51 -07:00
|
|
|
// This could be done in a cheaper way, all we need is pass the matrix
|
|
|
|
// to the vertex shader. The save/restore is a bit overkill.
|
|
|
|
save(SkCanvas::kMatrix_SaveFlag);
|
|
|
|
concatMatrix(matrix);
|
2013-01-04 12:26:18 -08:00
|
|
|
if (CC_UNLIKELY(bitmap->getConfig() == SkBitmap::kA8_Config)) {
|
|
|
|
drawAlphaBitmap(texture, 0.0f, 0.0f, paint);
|
|
|
|
} else {
|
|
|
|
drawTextureRect(0.0f, 0.0f, bitmap->width(), bitmap->height(), texture, paint);
|
|
|
|
}
|
2010-10-27 18:57:51 -07:00
|
|
|
restore();
|
2012-05-31 15:21:51 -07:00
|
|
|
|
|
|
|
return DrawGlInfo::kStatusDrew;
|
2010-07-01 11:05:42 -07:00
|
|
|
}
|
|
|
|
|
2012-05-31 15:21:51 -07:00
|
|
|
status_t OpenGLRenderer::drawBitmapData(SkBitmap* bitmap, float left, float top, SkPaint* paint) {
|
2012-05-14 19:44:40 -07:00
|
|
|
const float right = left + bitmap->width();
|
|
|
|
const float bottom = top + bitmap->height();
|
|
|
|
|
|
|
|
if (quickReject(left, top, right, bottom)) {
|
2012-05-31 15:21:51 -07:00
|
|
|
return DrawGlInfo::kStatusDone;
|
2012-05-14 19:44:40 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
mCaches.activeTexture(0);
|
|
|
|
Texture* texture = mCaches.textureCache.getTransient(bitmap);
|
|
|
|
const AutoTexture autoCleanup(texture);
|
|
|
|
|
2013-01-04 12:26:18 -08:00
|
|
|
if (CC_UNLIKELY(bitmap->getConfig() == SkBitmap::kA8_Config)) {
|
|
|
|
drawAlphaBitmap(texture, left, top, paint);
|
|
|
|
} else {
|
|
|
|
drawTextureRect(left, top, right, bottom, texture, paint);
|
|
|
|
}
|
2012-05-31 15:21:51 -07:00
|
|
|
|
|
|
|
return DrawGlInfo::kStatusDrew;
|
2012-05-14 19:44:40 -07:00
|
|
|
}
|
|
|
|
|
2012-05-31 15:21:51 -07:00
|
|
|
status_t OpenGLRenderer::drawBitmapMesh(SkBitmap* bitmap, int meshWidth, int meshHeight,
|
2011-01-20 19:09:30 -08:00
|
|
|
float* vertices, int* colors, SkPaint* paint) {
|
|
|
|
if (!vertices || mSnapshot->isIgnored()) {
|
2012-05-31 15:21:51 -07:00
|
|
|
return DrawGlInfo::kStatusDone;
|
2011-01-20 19:09:30 -08:00
|
|
|
}
|
|
|
|
|
2013-06-13 14:39:01 -07:00
|
|
|
// TODO: use quickReject on bounds from vertices
|
|
|
|
mCaches.enableScissor();
|
|
|
|
|
2011-02-10 15:52:54 -08:00
|
|
|
float left = FLT_MAX;
|
|
|
|
float top = FLT_MAX;
|
|
|
|
float right = FLT_MIN;
|
|
|
|
float bottom = FLT_MIN;
|
|
|
|
|
2012-10-16 11:08:44 -07:00
|
|
|
const uint32_t count = meshWidth * meshHeight * 6;
|
2011-02-10 15:52:54 -08:00
|
|
|
|
2013-02-13 18:39:43 -08:00
|
|
|
ColorTextureVertex mesh[count];
|
|
|
|
ColorTextureVertex* vertex = mesh;
|
|
|
|
|
|
|
|
bool cleanupColors = false;
|
|
|
|
if (!colors) {
|
|
|
|
uint32_t colorsCount = (meshWidth + 1) * (meshHeight + 1);
|
|
|
|
colors = new int[colorsCount];
|
|
|
|
memset(colors, 0xff, colorsCount * sizeof(int));
|
|
|
|
cleanupColors = true;
|
|
|
|
}
|
2012-10-16 11:08:44 -07:00
|
|
|
|
Pack preloaded framework assets in a texture atlas
When the Android runtime starts, the system preloads a series of assets
in the Zygote process. These assets are shared across all processes.
Unfortunately, each one of these assets is later uploaded in its own
OpenGL texture, once per process. This wastes memory and generates
unnecessary OpenGL state changes.
This CL introduces an asset server that provides an atlas to all processes.
Note: bitmaps used by skia shaders are *not* sampled from the atlas.
It's an uncommon use case and would require extra texture transforms
in the GL shaders.
WHAT IS THE ASSETS ATLAS
The "assets atlas" is a single, shareable graphic buffer that contains
all the system's preloaded bitmap drawables (this includes 9-patches.)
The atlas is made of two distinct objects: the graphic buffer that
contains the actual pixels and the map which indicates where each
preloaded bitmap can be found in the atlas (essentially a pair of
x and y coordinates.)
HOW IS THE ASSETS ATLAS GENERATED
Because we need to support a wide variety of devices and because it
is easy to change the list of preloaded drawables, the atlas is
generated at runtime, during the startup phase of the system process.
There are several steps that lead to the atlas generation:
1. If the device is booting for the first time, or if the device was
updated, we need to find the best atlas configuration. To do so,
the atlas service tries a number of width, height and algorithm
variations that allows us to pack as many assets as possible while
using as little memory as possible. Once a best configuration is found,
it gets written to disk in /data/system/framework_atlas
2. Given a best configuration (algorithm variant, dimensions and
number of bitmaps that can be packed in the atlas), the atlas service
packs all the preloaded bitmaps into a single graphic buffer object.
3. The packing is done using Skia in a temporary native bitmap. The
Skia bitmap is then copied into the graphic buffer using OpenGL ES
to benefit from texture swizzling.
HOW PROCESSES USE THE ATLAS
Whenever a process' hardware renderer initializes its EGL context,
it queries the atlas service for the graphic buffer and the map.
It is important to remember that both the context and the map will
be valid for the lifetime of the hardware renderer (if the system
process goes down, all apps get killed as well.)
Every time the hardware renderer needs to render a bitmap, it first
checks whether the bitmap can be found in the assets atlas. When
the bitmap is part of the atlas, texture coordinates are remapped
appropriately before rendering.
Change-Id: I8eaecf53e7f6a33d90da3d0047c5ceec89ea3af0
2013-04-17 18:54:38 -07:00
|
|
|
mCaches.activeTexture(0);
|
|
|
|
Texture* texture = mCaches.assetAtlas.getEntryTexture(bitmap);
|
|
|
|
const UvMapper& mapper(getMapper(texture));
|
|
|
|
|
2011-01-20 19:09:30 -08:00
|
|
|
for (int32_t y = 0; y < meshHeight; y++) {
|
|
|
|
for (int32_t x = 0; x < meshWidth; x++) {
|
|
|
|
uint32_t i = (y * (meshWidth + 1) + x) * 2;
|
|
|
|
|
|
|
|
float u1 = float(x) / meshWidth;
|
|
|
|
float u2 = float(x + 1) / meshWidth;
|
|
|
|
float v1 = float(y) / meshHeight;
|
|
|
|
float v2 = float(y + 1) / meshHeight;
|
|
|
|
|
Pack preloaded framework assets in a texture atlas
When the Android runtime starts, the system preloads a series of assets
in the Zygote process. These assets are shared across all processes.
Unfortunately, each one of these assets is later uploaded in its own
OpenGL texture, once per process. This wastes memory and generates
unnecessary OpenGL state changes.
This CL introduces an asset server that provides an atlas to all processes.
Note: bitmaps used by skia shaders are *not* sampled from the atlas.
It's an uncommon use case and would require extra texture transforms
in the GL shaders.
WHAT IS THE ASSETS ATLAS
The "assets atlas" is a single, shareable graphic buffer that contains
all the system's preloaded bitmap drawables (this includes 9-patches.)
The atlas is made of two distinct objects: the graphic buffer that
contains the actual pixels and the map which indicates where each
preloaded bitmap can be found in the atlas (essentially a pair of
x and y coordinates.)
HOW IS THE ASSETS ATLAS GENERATED
Because we need to support a wide variety of devices and because it
is easy to change the list of preloaded drawables, the atlas is
generated at runtime, during the startup phase of the system process.
There are several steps that lead to the atlas generation:
1. If the device is booting for the first time, or if the device was
updated, we need to find the best atlas configuration. To do so,
the atlas service tries a number of width, height and algorithm
variations that allows us to pack as many assets as possible while
using as little memory as possible. Once a best configuration is found,
it gets written to disk in /data/system/framework_atlas
2. Given a best configuration (algorithm variant, dimensions and
number of bitmaps that can be packed in the atlas), the atlas service
packs all the preloaded bitmaps into a single graphic buffer object.
3. The packing is done using Skia in a temporary native bitmap. The
Skia bitmap is then copied into the graphic buffer using OpenGL ES
to benefit from texture swizzling.
HOW PROCESSES USE THE ATLAS
Whenever a process' hardware renderer initializes its EGL context,
it queries the atlas service for the graphic buffer and the map.
It is important to remember that both the context and the map will
be valid for the lifetime of the hardware renderer (if the system
process goes down, all apps get killed as well.)
Every time the hardware renderer needs to render a bitmap, it first
checks whether the bitmap can be found in the assets atlas. When
the bitmap is part of the atlas, texture coordinates are remapped
appropriately before rendering.
Change-Id: I8eaecf53e7f6a33d90da3d0047c5ceec89ea3af0
2013-04-17 18:54:38 -07:00
|
|
|
mapper.map(u1, v1, u2, v2);
|
|
|
|
|
2011-01-20 19:09:30 -08:00
|
|
|
int ax = i + (meshWidth + 1) * 2;
|
|
|
|
int ay = ax + 1;
|
|
|
|
int bx = i;
|
|
|
|
int by = bx + 1;
|
|
|
|
int cx = i + 2;
|
|
|
|
int cy = cx + 1;
|
|
|
|
int dx = i + (meshWidth + 1) * 2 + 2;
|
|
|
|
int dy = dx + 1;
|
|
|
|
|
2013-02-13 18:39:43 -08:00
|
|
|
ColorTextureVertex::set(vertex++, vertices[dx], vertices[dy], u2, v2, colors[dx / 2]);
|
|
|
|
ColorTextureVertex::set(vertex++, vertices[ax], vertices[ay], u1, v2, colors[ax / 2]);
|
|
|
|
ColorTextureVertex::set(vertex++, vertices[bx], vertices[by], u1, v1, colors[bx / 2]);
|
2011-01-20 19:09:30 -08:00
|
|
|
|
2013-02-13 18:39:43 -08:00
|
|
|
ColorTextureVertex::set(vertex++, vertices[dx], vertices[dy], u2, v2, colors[dx / 2]);
|
|
|
|
ColorTextureVertex::set(vertex++, vertices[bx], vertices[by], u1, v1, colors[bx / 2]);
|
|
|
|
ColorTextureVertex::set(vertex++, vertices[cx], vertices[cy], u2, v1, colors[cx / 2]);
|
2011-02-10 15:52:54 -08:00
|
|
|
|
2012-10-16 11:08:44 -07:00
|
|
|
left = fminf(left, fminf(vertices[ax], fminf(vertices[bx], vertices[cx])));
|
|
|
|
top = fminf(top, fminf(vertices[ay], fminf(vertices[by], vertices[cy])));
|
|
|
|
right = fmaxf(right, fmaxf(vertices[ax], fmaxf(vertices[bx], vertices[cx])));
|
|
|
|
bottom = fmaxf(bottom, fmaxf(vertices[ay], fmaxf(vertices[by], vertices[cy])));
|
2011-01-20 19:09:30 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-10-16 11:08:44 -07:00
|
|
|
if (quickReject(left, top, right, bottom)) {
|
2013-02-13 18:39:43 -08:00
|
|
|
if (cleanupColors) delete[] colors;
|
2012-10-16 11:08:44 -07:00
|
|
|
return DrawGlInfo::kStatusDone;
|
|
|
|
}
|
|
|
|
|
2013-02-13 18:39:43 -08:00
|
|
|
if (!texture) {
|
Pack preloaded framework assets in a texture atlas
When the Android runtime starts, the system preloads a series of assets
in the Zygote process. These assets are shared across all processes.
Unfortunately, each one of these assets is later uploaded in its own
OpenGL texture, once per process. This wastes memory and generates
unnecessary OpenGL state changes.
This CL introduces an asset server that provides an atlas to all processes.
Note: bitmaps used by skia shaders are *not* sampled from the atlas.
It's an uncommon use case and would require extra texture transforms
in the GL shaders.
WHAT IS THE ASSETS ATLAS
The "assets atlas" is a single, shareable graphic buffer that contains
all the system's preloaded bitmap drawables (this includes 9-patches.)
The atlas is made of two distinct objects: the graphic buffer that
contains the actual pixels and the map which indicates where each
preloaded bitmap can be found in the atlas (essentially a pair of
x and y coordinates.)
HOW IS THE ASSETS ATLAS GENERATED
Because we need to support a wide variety of devices and because it
is easy to change the list of preloaded drawables, the atlas is
generated at runtime, during the startup phase of the system process.
There are several steps that lead to the atlas generation:
1. If the device is booting for the first time, or if the device was
updated, we need to find the best atlas configuration. To do so,
the atlas service tries a number of width, height and algorithm
variations that allows us to pack as many assets as possible while
using as little memory as possible. Once a best configuration is found,
it gets written to disk in /data/system/framework_atlas
2. Given a best configuration (algorithm variant, dimensions and
number of bitmaps that can be packed in the atlas), the atlas service
packs all the preloaded bitmaps into a single graphic buffer object.
3. The packing is done using Skia in a temporary native bitmap. The
Skia bitmap is then copied into the graphic buffer using OpenGL ES
to benefit from texture swizzling.
HOW PROCESSES USE THE ATLAS
Whenever a process' hardware renderer initializes its EGL context,
it queries the atlas service for the graphic buffer and the map.
It is important to remember that both the context and the map will
be valid for the lifetime of the hardware renderer (if the system
process goes down, all apps get killed as well.)
Every time the hardware renderer needs to render a bitmap, it first
checks whether the bitmap can be found in the assets atlas. When
the bitmap is part of the atlas, texture coordinates are remapped
appropriately before rendering.
Change-Id: I8eaecf53e7f6a33d90da3d0047c5ceec89ea3af0
2013-04-17 18:54:38 -07:00
|
|
|
texture = mCaches.textureCache.get(bitmap);
|
|
|
|
if (!texture) {
|
|
|
|
if (cleanupColors) delete[] colors;
|
|
|
|
return DrawGlInfo::kStatusDone;
|
|
|
|
}
|
2013-02-13 18:39:43 -08:00
|
|
|
}
|
2012-10-16 11:08:44 -07:00
|
|
|
const AutoTexture autoCleanup(texture);
|
|
|
|
|
|
|
|
texture->setWrap(GL_CLAMP_TO_EDGE, true);
|
|
|
|
texture->setFilter(FILTER(paint), true);
|
|
|
|
|
|
|
|
int alpha;
|
|
|
|
SkXfermode::Mode mode;
|
|
|
|
getAlphaAndMode(paint, &alpha, &mode);
|
|
|
|
|
2013-02-13 18:39:43 -08:00
|
|
|
float a = alpha / 255.0f;
|
|
|
|
|
2012-10-16 11:08:44 -07:00
|
|
|
if (hasLayer()) {
|
2013-03-05 10:27:35 -08:00
|
|
|
dirtyLayer(left, top, right, bottom, currentTransform());
|
2011-02-10 15:52:54 -08:00
|
|
|
}
|
|
|
|
|
2013-02-13 18:39:43 -08:00
|
|
|
setupDraw();
|
|
|
|
setupDrawWithTextureAndColor();
|
|
|
|
setupDrawColor(a, a, a, a);
|
|
|
|
setupDrawColorFilter();
|
|
|
|
setupDrawBlending(true, mode, false);
|
|
|
|
setupDrawProgram();
|
|
|
|
setupDrawDirtyRegionsDisabled();
|
|
|
|
setupDrawModelView(0.0f, 0.0f, 1.0f, 1.0f, false);
|
|
|
|
setupDrawTexture(texture->id);
|
|
|
|
setupDrawPureColorUniforms();
|
|
|
|
setupDrawColorFilterUniforms();
|
|
|
|
setupDrawMesh(&mesh[0].position[0], &mesh[0].texture[0], &mesh[0].color[0]);
|
|
|
|
|
|
|
|
glDrawArrays(GL_TRIANGLES, 0, count);
|
|
|
|
|
|
|
|
finishDrawTexture();
|
|
|
|
|
|
|
|
int slot = mCaches.currentProgram->getAttrib("colors");
|
|
|
|
if (slot >= 0) {
|
|
|
|
glDisableVertexAttribArray(slot);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cleanupColors) delete[] colors;
|
2012-05-31 15:21:51 -07:00
|
|
|
|
|
|
|
return DrawGlInfo::kStatusDrew;
|
2011-01-20 19:09:30 -08:00
|
|
|
}
|
|
|
|
|
2012-05-31 15:21:51 -07:00
|
|
|
status_t OpenGLRenderer::drawBitmap(SkBitmap* bitmap,
|
2010-06-30 19:21:21 -07:00
|
|
|
float srcLeft, float srcTop, float srcRight, float srcBottom,
|
|
|
|
float dstLeft, float dstTop, float dstRight, float dstBottom,
|
2010-10-08 08:37:55 -07:00
|
|
|
SkPaint* paint) {
|
2010-07-12 20:20:03 -07:00
|
|
|
if (quickReject(dstLeft, dstTop, dstRight, dstBottom)) {
|
2012-05-31 15:21:51 -07:00
|
|
|
return DrawGlInfo::kStatusDone;
|
2010-07-12 20:20:03 -07:00
|
|
|
}
|
|
|
|
|
2011-12-13 14:55:06 -08:00
|
|
|
mCaches.activeTexture(0);
|
Pack preloaded framework assets in a texture atlas
When the Android runtime starts, the system preloads a series of assets
in the Zygote process. These assets are shared across all processes.
Unfortunately, each one of these assets is later uploaded in its own
OpenGL texture, once per process. This wastes memory and generates
unnecessary OpenGL state changes.
This CL introduces an asset server that provides an atlas to all processes.
Note: bitmaps used by skia shaders are *not* sampled from the atlas.
It's an uncommon use case and would require extra texture transforms
in the GL shaders.
WHAT IS THE ASSETS ATLAS
The "assets atlas" is a single, shareable graphic buffer that contains
all the system's preloaded bitmap drawables (this includes 9-patches.)
The atlas is made of two distinct objects: the graphic buffer that
contains the actual pixels and the map which indicates where each
preloaded bitmap can be found in the atlas (essentially a pair of
x and y coordinates.)
HOW IS THE ASSETS ATLAS GENERATED
Because we need to support a wide variety of devices and because it
is easy to change the list of preloaded drawables, the atlas is
generated at runtime, during the startup phase of the system process.
There are several steps that lead to the atlas generation:
1. If the device is booting for the first time, or if the device was
updated, we need to find the best atlas configuration. To do so,
the atlas service tries a number of width, height and algorithm
variations that allows us to pack as many assets as possible while
using as little memory as possible. Once a best configuration is found,
it gets written to disk in /data/system/framework_atlas
2. Given a best configuration (algorithm variant, dimensions and
number of bitmaps that can be packed in the atlas), the atlas service
packs all the preloaded bitmaps into a single graphic buffer object.
3. The packing is done using Skia in a temporary native bitmap. The
Skia bitmap is then copied into the graphic buffer using OpenGL ES
to benefit from texture swizzling.
HOW PROCESSES USE THE ATLAS
Whenever a process' hardware renderer initializes its EGL context,
it queries the atlas service for the graphic buffer and the map.
It is important to remember that both the context and the map will
be valid for the lifetime of the hardware renderer (if the system
process goes down, all apps get killed as well.)
Every time the hardware renderer needs to render a bitmap, it first
checks whether the bitmap can be found in the assets atlas. When
the bitmap is part of the atlas, texture coordinates are remapped
appropriately before rendering.
Change-Id: I8eaecf53e7f6a33d90da3d0047c5ceec89ea3af0
2013-04-17 18:54:38 -07:00
|
|
|
Texture* texture = getTexture(bitmap);
|
2012-05-31 15:21:51 -07:00
|
|
|
if (!texture) return DrawGlInfo::kStatusDone;
|
2010-08-06 11:18:34 -07:00
|
|
|
const AutoTexture autoCleanup(texture);
|
2010-06-30 19:21:21 -07:00
|
|
|
|
|
|
|
const float width = texture->width;
|
|
|
|
const float height = texture->height;
|
2010-06-30 17:56:19 -07:00
|
|
|
|
Pack preloaded framework assets in a texture atlas
When the Android runtime starts, the system preloads a series of assets
in the Zygote process. These assets are shared across all processes.
Unfortunately, each one of these assets is later uploaded in its own
OpenGL texture, once per process. This wastes memory and generates
unnecessary OpenGL state changes.
This CL introduces an asset server that provides an atlas to all processes.
Note: bitmaps used by skia shaders are *not* sampled from the atlas.
It's an uncommon use case and would require extra texture transforms
in the GL shaders.
WHAT IS THE ASSETS ATLAS
The "assets atlas" is a single, shareable graphic buffer that contains
all the system's preloaded bitmap drawables (this includes 9-patches.)
The atlas is made of two distinct objects: the graphic buffer that
contains the actual pixels and the map which indicates where each
preloaded bitmap can be found in the atlas (essentially a pair of
x and y coordinates.)
HOW IS THE ASSETS ATLAS GENERATED
Because we need to support a wide variety of devices and because it
is easy to change the list of preloaded drawables, the atlas is
generated at runtime, during the startup phase of the system process.
There are several steps that lead to the atlas generation:
1. If the device is booting for the first time, or if the device was
updated, we need to find the best atlas configuration. To do so,
the atlas service tries a number of width, height and algorithm
variations that allows us to pack as many assets as possible while
using as little memory as possible. Once a best configuration is found,
it gets written to disk in /data/system/framework_atlas
2. Given a best configuration (algorithm variant, dimensions and
number of bitmaps that can be packed in the atlas), the atlas service
packs all the preloaded bitmaps into a single graphic buffer object.
3. The packing is done using Skia in a temporary native bitmap. The
Skia bitmap is then copied into the graphic buffer using OpenGL ES
to benefit from texture swizzling.
HOW PROCESSES USE THE ATLAS
Whenever a process' hardware renderer initializes its EGL context,
it queries the atlas service for the graphic buffer and the map.
It is important to remember that both the context and the map will
be valid for the lifetime of the hardware renderer (if the system
process goes down, all apps get killed as well.)
Every time the hardware renderer needs to render a bitmap, it first
checks whether the bitmap can be found in the assets atlas. When
the bitmap is part of the atlas, texture coordinates are remapped
appropriately before rendering.
Change-Id: I8eaecf53e7f6a33d90da3d0047c5ceec89ea3af0
2013-04-17 18:54:38 -07:00
|
|
|
float u1 = fmax(0.0f, srcLeft / width);
|
|
|
|
float v1 = fmax(0.0f, srcTop / height);
|
|
|
|
float u2 = fmin(1.0f, srcRight / width);
|
|
|
|
float v2 = fmin(1.0f, srcBottom / height);
|
|
|
|
|
|
|
|
getMapper(texture).map(u1, v1, u2, v2);
|
2010-06-30 17:56:19 -07:00
|
|
|
|
2010-10-18 14:06:08 -07:00
|
|
|
mCaches.unbindMeshBuffer();
|
2010-06-30 19:21:21 -07:00
|
|
|
resetDrawTextureTexCoords(u1, v1, u2, v2);
|
|
|
|
|
2010-10-18 14:06:08 -07:00
|
|
|
int alpha;
|
|
|
|
SkXfermode::Mode mode;
|
|
|
|
getAlphaAndMode(paint, &alpha, &mode);
|
|
|
|
|
2011-11-30 20:21:23 -08:00
|
|
|
texture->setWrap(GL_CLAMP_TO_EDGE, true);
|
|
|
|
|
2013-01-04 12:26:18 -08:00
|
|
|
float scaleX = (dstRight - dstLeft) / (srcRight - srcLeft);
|
|
|
|
float scaleY = (dstBottom - dstTop) / (srcBottom - srcTop);
|
2010-12-06 18:07:02 -08:00
|
|
|
|
2013-01-04 12:26:18 -08:00
|
|
|
bool scaled = scaleX != 1.0f || scaleY != 1.0f;
|
|
|
|
// Apply a scale transform on the canvas only when a shader is in use
|
|
|
|
// Skia handles the ratio between the dst and src rects as a scale factor
|
|
|
|
// when a shader is set
|
2013-02-04 16:16:33 -08:00
|
|
|
bool useScaleTransform = mDrawModifiers.mShader && scaled;
|
2013-01-04 12:26:18 -08:00
|
|
|
bool ignoreTransform = false;
|
2011-07-28 15:39:12 -07:00
|
|
|
|
2013-03-05 10:27:35 -08:00
|
|
|
if (CC_LIKELY(currentTransform().isPureTranslate() && !useScaleTransform)) {
|
|
|
|
float x = (int) floorf(dstLeft + currentTransform().getTranslateX() + 0.5f);
|
|
|
|
float y = (int) floorf(dstTop + currentTransform().getTranslateY() + 0.5f);
|
2013-01-04 12:26:18 -08:00
|
|
|
|
|
|
|
dstRight = x + (dstRight - dstLeft);
|
|
|
|
dstBottom = y + (dstBottom - dstTop);
|
|
|
|
|
|
|
|
dstLeft = x;
|
|
|
|
dstTop = y;
|
|
|
|
|
|
|
|
texture->setFilter(scaled ? FILTER(paint) : GL_NEAREST, true);
|
|
|
|
ignoreTransform = true;
|
2010-12-06 18:07:02 -08:00
|
|
|
} else {
|
2011-11-30 20:21:23 -08:00
|
|
|
texture->setFilter(FILTER(paint), true);
|
2013-01-04 12:26:18 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (CC_UNLIKELY(useScaleTransform)) {
|
|
|
|
save(SkCanvas::kMatrix_SaveFlag);
|
|
|
|
translate(dstLeft, dstTop);
|
|
|
|
scale(scaleX, scaleY);
|
|
|
|
|
|
|
|
dstLeft = 0.0f;
|
|
|
|
dstTop = 0.0f;
|
|
|
|
|
|
|
|
dstRight = srcRight - srcLeft;
|
|
|
|
dstBottom = srcBottom - srcTop;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (CC_UNLIKELY(bitmap->getConfig() == SkBitmap::kA8_Config)) {
|
|
|
|
int color = paint ? paint->getColor() : 0;
|
|
|
|
drawAlpha8TextureMesh(dstLeft, dstTop, dstRight, dstBottom,
|
|
|
|
texture->id, paint != NULL, color, alpha, mode,
|
|
|
|
&mMeshVertices[0].position[0], &mMeshVertices[0].texture[0],
|
|
|
|
GL_TRIANGLE_STRIP, gMeshCount, ignoreTransform);
|
|
|
|
} else {
|
|
|
|
drawTextureMesh(dstLeft, dstTop, dstRight, dstBottom,
|
|
|
|
texture->id, alpha / 255.0f, mode, texture->blend,
|
|
|
|
&mMeshVertices[0].position[0], &mMeshVertices[0].texture[0],
|
|
|
|
GL_TRIANGLE_STRIP, gMeshCount, false, ignoreTransform);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (CC_UNLIKELY(useScaleTransform)) {
|
|
|
|
restore();
|
2010-12-06 18:07:02 -08:00
|
|
|
}
|
2010-06-30 19:21:21 -07:00
|
|
|
|
|
|
|
resetDrawTextureTexCoords(0.0f, 0.0f, 1.0f, 1.0f);
|
2012-05-31 15:21:51 -07:00
|
|
|
|
|
|
|
return DrawGlInfo::kStatusDrew;
|
2010-06-29 21:05:21 -07:00
|
|
|
}
|
|
|
|
|
Pack preloaded framework assets in a texture atlas
When the Android runtime starts, the system preloads a series of assets
in the Zygote process. These assets are shared across all processes.
Unfortunately, each one of these assets is later uploaded in its own
OpenGL texture, once per process. This wastes memory and generates
unnecessary OpenGL state changes.
This CL introduces an asset server that provides an atlas to all processes.
Note: bitmaps used by skia shaders are *not* sampled from the atlas.
It's an uncommon use case and would require extra texture transforms
in the GL shaders.
WHAT IS THE ASSETS ATLAS
The "assets atlas" is a single, shareable graphic buffer that contains
all the system's preloaded bitmap drawables (this includes 9-patches.)
The atlas is made of two distinct objects: the graphic buffer that
contains the actual pixels and the map which indicates where each
preloaded bitmap can be found in the atlas (essentially a pair of
x and y coordinates.)
HOW IS THE ASSETS ATLAS GENERATED
Because we need to support a wide variety of devices and because it
is easy to change the list of preloaded drawables, the atlas is
generated at runtime, during the startup phase of the system process.
There are several steps that lead to the atlas generation:
1. If the device is booting for the first time, or if the device was
updated, we need to find the best atlas configuration. To do so,
the atlas service tries a number of width, height and algorithm
variations that allows us to pack as many assets as possible while
using as little memory as possible. Once a best configuration is found,
it gets written to disk in /data/system/framework_atlas
2. Given a best configuration (algorithm variant, dimensions and
number of bitmaps that can be packed in the atlas), the atlas service
packs all the preloaded bitmaps into a single graphic buffer object.
3. The packing is done using Skia in a temporary native bitmap. The
Skia bitmap is then copied into the graphic buffer using OpenGL ES
to benefit from texture swizzling.
HOW PROCESSES USE THE ATLAS
Whenever a process' hardware renderer initializes its EGL context,
it queries the atlas service for the graphic buffer and the map.
It is important to remember that both the context and the map will
be valid for the lifetime of the hardware renderer (if the system
process goes down, all apps get killed as well.)
Every time the hardware renderer needs to render a bitmap, it first
checks whether the bitmap can be found in the assets atlas. When
the bitmap is part of the atlas, texture coordinates are remapped
appropriately before rendering.
Change-Id: I8eaecf53e7f6a33d90da3d0047c5ceec89ea3af0
2013-04-17 18:54:38 -07:00
|
|
|
status_t OpenGLRenderer::drawPatch(SkBitmap* bitmap, Res_png_9patch* patch,
|
2010-10-08 08:37:55 -07:00
|
|
|
float left, float top, float right, float bottom, SkPaint* paint) {
|
2012-07-16 12:41:17 -07:00
|
|
|
int alpha;
|
|
|
|
SkXfermode::Mode mode;
|
2013-03-29 10:59:59 -07:00
|
|
|
getAlphaAndMode(paint, &alpha, &mode);
|
2012-07-16 12:41:17 -07:00
|
|
|
|
2013-06-11 16:19:24 -07:00
|
|
|
if (quickReject(left, top, right, bottom)) {
|
|
|
|
return DrawGlInfo::kStatusDone;
|
|
|
|
}
|
|
|
|
|
|
|
|
AssetAtlas::Entry* entry = mCaches.assetAtlas.getEntry(bitmap);
|
|
|
|
const Patch* mesh = mCaches.patchCache.get(entry, bitmap->width(), bitmap->height(),
|
|
|
|
right - left, bottom - top, patch);
|
|
|
|
|
|
|
|
return drawPatch(bitmap, mesh, entry, left, top, right, bottom, alpha, mode);
|
2012-07-16 12:41:17 -07:00
|
|
|
}
|
|
|
|
|
2013-06-11 16:19:24 -07:00
|
|
|
status_t OpenGLRenderer::drawPatch(SkBitmap* bitmap, const Patch* mesh,
|
Pack preloaded framework assets in a texture atlas
When the Android runtime starts, the system preloads a series of assets
in the Zygote process. These assets are shared across all processes.
Unfortunately, each one of these assets is later uploaded in its own
OpenGL texture, once per process. This wastes memory and generates
unnecessary OpenGL state changes.
This CL introduces an asset server that provides an atlas to all processes.
Note: bitmaps used by skia shaders are *not* sampled from the atlas.
It's an uncommon use case and would require extra texture transforms
in the GL shaders.
WHAT IS THE ASSETS ATLAS
The "assets atlas" is a single, shareable graphic buffer that contains
all the system's preloaded bitmap drawables (this includes 9-patches.)
The atlas is made of two distinct objects: the graphic buffer that
contains the actual pixels and the map which indicates where each
preloaded bitmap can be found in the atlas (essentially a pair of
x and y coordinates.)
HOW IS THE ASSETS ATLAS GENERATED
Because we need to support a wide variety of devices and because it
is easy to change the list of preloaded drawables, the atlas is
generated at runtime, during the startup phase of the system process.
There are several steps that lead to the atlas generation:
1. If the device is booting for the first time, or if the device was
updated, we need to find the best atlas configuration. To do so,
the atlas service tries a number of width, height and algorithm
variations that allows us to pack as many assets as possible while
using as little memory as possible. Once a best configuration is found,
it gets written to disk in /data/system/framework_atlas
2. Given a best configuration (algorithm variant, dimensions and
number of bitmaps that can be packed in the atlas), the atlas service
packs all the preloaded bitmaps into a single graphic buffer object.
3. The packing is done using Skia in a temporary native bitmap. The
Skia bitmap is then copied into the graphic buffer using OpenGL ES
to benefit from texture swizzling.
HOW PROCESSES USE THE ATLAS
Whenever a process' hardware renderer initializes its EGL context,
it queries the atlas service for the graphic buffer and the map.
It is important to remember that both the context and the map will
be valid for the lifetime of the hardware renderer (if the system
process goes down, all apps get killed as well.)
Every time the hardware renderer needs to render a bitmap, it first
checks whether the bitmap can be found in the assets atlas. When
the bitmap is part of the atlas, texture coordinates are remapped
appropriately before rendering.
Change-Id: I8eaecf53e7f6a33d90da3d0047c5ceec89ea3af0
2013-04-17 18:54:38 -07:00
|
|
|
AssetAtlas::Entry* entry, float left, float top, float right, float bottom,
|
|
|
|
int alpha, SkXfermode::Mode mode) {
|
2013-06-11 16:19:24 -07:00
|
|
|
|
2010-07-12 20:20:03 -07:00
|
|
|
if (quickReject(left, top, right, bottom)) {
|
2012-05-31 15:21:51 -07:00
|
|
|
return DrawGlInfo::kStatusDone;
|
2010-07-12 20:20:03 -07:00
|
|
|
}
|
|
|
|
|
2012-02-01 16:10:55 -08:00
|
|
|
if (CC_LIKELY(mesh && mesh->verticesCount > 0)) {
|
2013-02-28 12:15:35 -08:00
|
|
|
mCaches.activeTexture(0);
|
2013-05-24 16:19:19 -07:00
|
|
|
Texture* texture = entry ? entry->texture : mCaches.textureCache.get(bitmap);
|
2013-02-28 12:15:35 -08:00
|
|
|
if (!texture) return DrawGlInfo::kStatusDone;
|
|
|
|
const AutoTexture autoCleanup(texture);
|
Pack preloaded framework assets in a texture atlas
When the Android runtime starts, the system preloads a series of assets
in the Zygote process. These assets are shared across all processes.
Unfortunately, each one of these assets is later uploaded in its own
OpenGL texture, once per process. This wastes memory and generates
unnecessary OpenGL state changes.
This CL introduces an asset server that provides an atlas to all processes.
Note: bitmaps used by skia shaders are *not* sampled from the atlas.
It's an uncommon use case and would require extra texture transforms
in the GL shaders.
WHAT IS THE ASSETS ATLAS
The "assets atlas" is a single, shareable graphic buffer that contains
all the system's preloaded bitmap drawables (this includes 9-patches.)
The atlas is made of two distinct objects: the graphic buffer that
contains the actual pixels and the map which indicates where each
preloaded bitmap can be found in the atlas (essentially a pair of
x and y coordinates.)
HOW IS THE ASSETS ATLAS GENERATED
Because we need to support a wide variety of devices and because it
is easy to change the list of preloaded drawables, the atlas is
generated at runtime, during the startup phase of the system process.
There are several steps that lead to the atlas generation:
1. If the device is booting for the first time, or if the device was
updated, we need to find the best atlas configuration. To do so,
the atlas service tries a number of width, height and algorithm
variations that allows us to pack as many assets as possible while
using as little memory as possible. Once a best configuration is found,
it gets written to disk in /data/system/framework_atlas
2. Given a best configuration (algorithm variant, dimensions and
number of bitmaps that can be packed in the atlas), the atlas service
packs all the preloaded bitmaps into a single graphic buffer object.
3. The packing is done using Skia in a temporary native bitmap. The
Skia bitmap is then copied into the graphic buffer using OpenGL ES
to benefit from texture swizzling.
HOW PROCESSES USE THE ATLAS
Whenever a process' hardware renderer initializes its EGL context,
it queries the atlas service for the graphic buffer and the map.
It is important to remember that both the context and the map will
be valid for the lifetime of the hardware renderer (if the system
process goes down, all apps get killed as well.)
Every time the hardware renderer needs to render a bitmap, it first
checks whether the bitmap can be found in the assets atlas. When
the bitmap is part of the atlas, texture coordinates are remapped
appropriately before rendering.
Change-Id: I8eaecf53e7f6a33d90da3d0047c5ceec89ea3af0
2013-04-17 18:54:38 -07:00
|
|
|
|
2013-02-28 12:15:35 -08:00
|
|
|
texture->setWrap(GL_CLAMP_TO_EDGE, true);
|
|
|
|
texture->setFilter(GL_LINEAR, true);
|
|
|
|
|
2013-03-05 10:27:35 -08:00
|
|
|
const bool pureTranslate = currentTransform().isPureTranslate();
|
2010-10-27 18:57:51 -07:00
|
|
|
// Mark the current layer dirty where we are going to draw the patch
|
2011-01-24 20:40:18 -08:00
|
|
|
if (hasLayer() && mesh->hasEmptyQuads) {
|
2013-03-05 10:27:35 -08:00
|
|
|
const float offsetX = left + currentTransform().getTranslateX();
|
|
|
|
const float offsetY = top + currentTransform().getTranslateY();
|
2010-10-27 18:57:51 -07:00
|
|
|
const size_t count = mesh->quads.size();
|
|
|
|
for (size_t i = 0; i < count; i++) {
|
2010-12-07 13:30:10 -08:00
|
|
|
const Rect& bounds = mesh->quads.itemAt(i);
|
2012-02-01 16:10:55 -08:00
|
|
|
if (CC_LIKELY(pureTranslate)) {
|
2011-02-04 14:00:42 -08:00
|
|
|
const float x = (int) floorf(bounds.left + offsetX + 0.5f);
|
|
|
|
const float y = (int) floorf(bounds.top + offsetY + 0.5f);
|
|
|
|
dirtyLayer(x, y, x + bounds.getWidth(), y + bounds.getHeight());
|
2010-12-06 18:07:02 -08:00
|
|
|
} else {
|
2011-02-04 14:00:42 -08:00
|
|
|
dirtyLayer(left + bounds.left, top + bounds.top,
|
2013-03-05 10:27:35 -08:00
|
|
|
left + bounds.right, top + bounds.bottom, currentTransform());
|
2010-12-06 18:07:02 -08:00
|
|
|
}
|
2010-10-27 18:57:51 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Pack preloaded framework assets in a texture atlas
When the Android runtime starts, the system preloads a series of assets
in the Zygote process. These assets are shared across all processes.
Unfortunately, each one of these assets is later uploaded in its own
OpenGL texture, once per process. This wastes memory and generates
unnecessary OpenGL state changes.
This CL introduces an asset server that provides an atlas to all processes.
Note: bitmaps used by skia shaders are *not* sampled from the atlas.
It's an uncommon use case and would require extra texture transforms
in the GL shaders.
WHAT IS THE ASSETS ATLAS
The "assets atlas" is a single, shareable graphic buffer that contains
all the system's preloaded bitmap drawables (this includes 9-patches.)
The atlas is made of two distinct objects: the graphic buffer that
contains the actual pixels and the map which indicates where each
preloaded bitmap can be found in the atlas (essentially a pair of
x and y coordinates.)
HOW IS THE ASSETS ATLAS GENERATED
Because we need to support a wide variety of devices and because it
is easy to change the list of preloaded drawables, the atlas is
generated at runtime, during the startup phase of the system process.
There are several steps that lead to the atlas generation:
1. If the device is booting for the first time, or if the device was
updated, we need to find the best atlas configuration. To do so,
the atlas service tries a number of width, height and algorithm
variations that allows us to pack as many assets as possible while
using as little memory as possible. Once a best configuration is found,
it gets written to disk in /data/system/framework_atlas
2. Given a best configuration (algorithm variant, dimensions and
number of bitmaps that can be packed in the atlas), the atlas service
packs all the preloaded bitmaps into a single graphic buffer object.
3. The packing is done using Skia in a temporary native bitmap. The
Skia bitmap is then copied into the graphic buffer using OpenGL ES
to benefit from texture swizzling.
HOW PROCESSES USE THE ATLAS
Whenever a process' hardware renderer initializes its EGL context,
it queries the atlas service for the graphic buffer and the map.
It is important to remember that both the context and the map will
be valid for the lifetime of the hardware renderer (if the system
process goes down, all apps get killed as well.)
Every time the hardware renderer needs to render a bitmap, it first
checks whether the bitmap can be found in the assets atlas. When
the bitmap is part of the atlas, texture coordinates are remapped
appropriately before rendering.
Change-Id: I8eaecf53e7f6a33d90da3d0047c5ceec89ea3af0
2013-04-17 18:54:38 -07:00
|
|
|
alpha *= mSnapshot->alpha;
|
|
|
|
|
2012-02-01 16:10:55 -08:00
|
|
|
if (CC_LIKELY(pureTranslate)) {
|
2013-03-05 10:27:35 -08:00
|
|
|
const float x = (int) floorf(left + currentTransform().getTranslateX() + 0.5f);
|
|
|
|
const float y = (int) floorf(top + currentTransform().getTranslateY() + 0.5f);
|
2010-12-06 18:07:02 -08:00
|
|
|
|
Pack preloaded framework assets in a texture atlas
When the Android runtime starts, the system preloads a series of assets
in the Zygote process. These assets are shared across all processes.
Unfortunately, each one of these assets is later uploaded in its own
OpenGL texture, once per process. This wastes memory and generates
unnecessary OpenGL state changes.
This CL introduces an asset server that provides an atlas to all processes.
Note: bitmaps used by skia shaders are *not* sampled from the atlas.
It's an uncommon use case and would require extra texture transforms
in the GL shaders.
WHAT IS THE ASSETS ATLAS
The "assets atlas" is a single, shareable graphic buffer that contains
all the system's preloaded bitmap drawables (this includes 9-patches.)
The atlas is made of two distinct objects: the graphic buffer that
contains the actual pixels and the map which indicates where each
preloaded bitmap can be found in the atlas (essentially a pair of
x and y coordinates.)
HOW IS THE ASSETS ATLAS GENERATED
Because we need to support a wide variety of devices and because it
is easy to change the list of preloaded drawables, the atlas is
generated at runtime, during the startup phase of the system process.
There are several steps that lead to the atlas generation:
1. If the device is booting for the first time, or if the device was
updated, we need to find the best atlas configuration. To do so,
the atlas service tries a number of width, height and algorithm
variations that allows us to pack as many assets as possible while
using as little memory as possible. Once a best configuration is found,
it gets written to disk in /data/system/framework_atlas
2. Given a best configuration (algorithm variant, dimensions and
number of bitmaps that can be packed in the atlas), the atlas service
packs all the preloaded bitmaps into a single graphic buffer object.
3. The packing is done using Skia in a temporary native bitmap. The
Skia bitmap is then copied into the graphic buffer using OpenGL ES
to benefit from texture swizzling.
HOW PROCESSES USE THE ATLAS
Whenever a process' hardware renderer initializes its EGL context,
it queries the atlas service for the graphic buffer and the map.
It is important to remember that both the context and the map will
be valid for the lifetime of the hardware renderer (if the system
process goes down, all apps get killed as well.)
Every time the hardware renderer needs to render a bitmap, it first
checks whether the bitmap can be found in the assets atlas. When
the bitmap is part of the atlas, texture coordinates are remapped
appropriately before rendering.
Change-Id: I8eaecf53e7f6a33d90da3d0047c5ceec89ea3af0
2013-04-17 18:54:38 -07:00
|
|
|
right = x + right - left;
|
|
|
|
bottom = y + bottom - top;
|
|
|
|
drawIndexedTextureMesh(x, y, right, bottom, texture->id, alpha / 255.0f,
|
|
|
|
mode, texture->blend, (GLvoid*) mesh->offset, (GLvoid*) mesh->textureOffset,
|
|
|
|
GL_TRIANGLES, mesh->indexCount, false, true,
|
|
|
|
mCaches.patchCache.getMeshBuffer(), true, !mesh->hasEmptyQuads);
|
2010-12-06 18:07:02 -08:00
|
|
|
} else {
|
Pack preloaded framework assets in a texture atlas
When the Android runtime starts, the system preloads a series of assets
in the Zygote process. These assets are shared across all processes.
Unfortunately, each one of these assets is later uploaded in its own
OpenGL texture, once per process. This wastes memory and generates
unnecessary OpenGL state changes.
This CL introduces an asset server that provides an atlas to all processes.
Note: bitmaps used by skia shaders are *not* sampled from the atlas.
It's an uncommon use case and would require extra texture transforms
in the GL shaders.
WHAT IS THE ASSETS ATLAS
The "assets atlas" is a single, shareable graphic buffer that contains
all the system's preloaded bitmap drawables (this includes 9-patches.)
The atlas is made of two distinct objects: the graphic buffer that
contains the actual pixels and the map which indicates where each
preloaded bitmap can be found in the atlas (essentially a pair of
x and y coordinates.)
HOW IS THE ASSETS ATLAS GENERATED
Because we need to support a wide variety of devices and because it
is easy to change the list of preloaded drawables, the atlas is
generated at runtime, during the startup phase of the system process.
There are several steps that lead to the atlas generation:
1. If the device is booting for the first time, or if the device was
updated, we need to find the best atlas configuration. To do so,
the atlas service tries a number of width, height and algorithm
variations that allows us to pack as many assets as possible while
using as little memory as possible. Once a best configuration is found,
it gets written to disk in /data/system/framework_atlas
2. Given a best configuration (algorithm variant, dimensions and
number of bitmaps that can be packed in the atlas), the atlas service
packs all the preloaded bitmaps into a single graphic buffer object.
3. The packing is done using Skia in a temporary native bitmap. The
Skia bitmap is then copied into the graphic buffer using OpenGL ES
to benefit from texture swizzling.
HOW PROCESSES USE THE ATLAS
Whenever a process' hardware renderer initializes its EGL context,
it queries the atlas service for the graphic buffer and the map.
It is important to remember that both the context and the map will
be valid for the lifetime of the hardware renderer (if the system
process goes down, all apps get killed as well.)
Every time the hardware renderer needs to render a bitmap, it first
checks whether the bitmap can be found in the assets atlas. When
the bitmap is part of the atlas, texture coordinates are remapped
appropriately before rendering.
Change-Id: I8eaecf53e7f6a33d90da3d0047c5ceec89ea3af0
2013-04-17 18:54:38 -07:00
|
|
|
drawIndexedTextureMesh(left, top, right, bottom, texture->id, alpha / 255.0f,
|
|
|
|
mode, texture->blend, (GLvoid*) mesh->offset, (GLvoid*) mesh->textureOffset,
|
|
|
|
GL_TRIANGLES, mesh->indexCount, false, false,
|
|
|
|
mCaches.patchCache.getMeshBuffer(), true, !mesh->hasEmptyQuads);
|
2010-12-06 18:07:02 -08:00
|
|
|
}
|
2010-10-15 17:55:25 -07:00
|
|
|
}
|
2012-05-31 15:21:51 -07:00
|
|
|
|
|
|
|
return DrawGlInfo::kStatusDrew;
|
2010-07-08 19:17:03 -07:00
|
|
|
}
|
|
|
|
|
2012-12-10 17:56:27 -08:00
|
|
|
status_t OpenGLRenderer::drawVertexBuffer(const VertexBuffer& vertexBuffer, SkPaint* paint,
|
|
|
|
bool useOffset) {
|
2013-05-08 18:35:44 -07:00
|
|
|
if (!vertexBuffer.getVertexCount()) {
|
2012-10-01 13:50:37 -07:00
|
|
|
// no vertices to draw
|
2012-12-10 17:56:27 -08:00
|
|
|
return DrawGlInfo::kStatusDone;
|
2012-10-01 13:50:37 -07:00
|
|
|
}
|
|
|
|
|
2012-12-10 17:56:27 -08:00
|
|
|
int color = paint->getColor();
|
|
|
|
SkXfermode::Mode mode = getXfermode(paint->getXfermode());
|
|
|
|
bool isAA = paint->isAntiAlias();
|
|
|
|
|
2012-09-17 17:25:49 -07:00
|
|
|
setupDraw();
|
|
|
|
setupDrawNoTexture();
|
|
|
|
if (isAA) setupDrawAA();
|
|
|
|
setupDrawColor(color, ((color >> 24) & 0xFF) * mSnapshot->alpha);
|
|
|
|
setupDrawColorFilter();
|
|
|
|
setupDrawShader();
|
|
|
|
setupDrawBlending(isAA, mode);
|
|
|
|
setupDrawProgram();
|
2012-12-10 17:56:27 -08:00
|
|
|
setupDrawModelViewIdentity(useOffset);
|
2012-09-17 17:25:49 -07:00
|
|
|
setupDrawColorUniforms();
|
|
|
|
setupDrawColorFilterUniforms();
|
|
|
|
setupDrawShaderIdentityUniforms();
|
2011-05-12 09:06:00 -07:00
|
|
|
|
2012-09-17 17:25:49 -07:00
|
|
|
void* vertices = vertexBuffer.getBuffer();
|
|
|
|
bool force = mCaches.unbindMeshBuffer();
|
2012-09-25 12:00:29 -07:00
|
|
|
mCaches.bindPositionVertexPointer(true, vertices, isAA ? gAlphaVertexStride : gVertexStride);
|
2012-09-17 17:25:49 -07:00
|
|
|
mCaches.resetTexCoordsVertexPointer();
|
|
|
|
mCaches.unbindIndicesBuffer();
|
2012-08-31 18:24:33 -07:00
|
|
|
|
2012-09-17 17:25:49 -07:00
|
|
|
int alphaSlot = -1;
|
|
|
|
if (isAA) {
|
|
|
|
void* alphaCoords = ((GLbyte*) vertices) + gVertexAlphaOffset;
|
|
|
|
alphaSlot = mCaches.currentProgram->getAttrib("vtxAlpha");
|
2012-08-31 18:24:33 -07:00
|
|
|
|
2012-09-17 17:25:49 -07:00
|
|
|
// TODO: avoid enable/disable in back to back uses of the alpha attribute
|
2012-08-31 18:24:33 -07:00
|
|
|
glEnableVertexAttribArray(alphaSlot);
|
|
|
|
glVertexAttribPointer(alphaSlot, 1, GL_FLOAT, GL_FALSE, gAlphaVertexStride, alphaCoords);
|
2012-09-17 17:25:49 -07:00
|
|
|
}
|
2012-08-31 18:24:33 -07:00
|
|
|
|
2013-05-08 18:35:44 -07:00
|
|
|
glDrawArrays(GL_TRIANGLE_STRIP, 0, vertexBuffer.getVertexCount());
|
2012-04-04 11:38:54 -07:00
|
|
|
|
2012-09-17 17:25:49 -07:00
|
|
|
if (isAA) {
|
2012-08-31 18:24:33 -07:00
|
|
|
glDisableVertexAttribArray(alphaSlot);
|
2012-07-18 17:15:41 -07:00
|
|
|
}
|
2012-12-10 17:56:27 -08:00
|
|
|
|
|
|
|
return DrawGlInfo::kStatusDrew;
|
2011-05-12 09:06:00 -07:00
|
|
|
}
|
|
|
|
|
2011-05-06 12:06:34 -07:00
|
|
|
/**
|
2012-12-10 17:56:27 -08:00
|
|
|
* Renders a convex path via tessellation. For AA paths, this function uses a similar approach to
|
|
|
|
* that of AA lines in the drawLines() function. We expand the convex path by a half pixel in
|
|
|
|
* screen space in all directions. However, instead of using a fragment shader to compute the
|
|
|
|
* translucency of the color from its position, we simply use a varying parameter to define how far
|
|
|
|
* a given pixel is from the edge. For non-AA paths, the expansion and alpha varying are not used.
|
|
|
|
*
|
|
|
|
* Doesn't yet support joins, caps, or path effects.
|
2011-05-06 12:06:34 -07:00
|
|
|
*/
|
2012-12-10 17:56:27 -08:00
|
|
|
status_t OpenGLRenderer::drawConvexPath(const SkPath& path, SkPaint* paint) {
|
|
|
|
VertexBuffer vertexBuffer;
|
|
|
|
// TODO: try clipping large paths to viewport
|
|
|
|
PathTessellator::tessellatePath(path, paint, mSnapshot->transform, vertexBuffer);
|
2012-04-04 11:38:54 -07:00
|
|
|
|
2013-03-15 19:06:39 -07:00
|
|
|
if (hasLayer()) {
|
|
|
|
SkRect bounds = path.getBounds();
|
|
|
|
PathTessellator::expandBoundsForStroke(bounds, paint, false);
|
|
|
|
dirtyLayer(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, currentTransform());
|
|
|
|
}
|
2012-04-04 11:38:54 -07:00
|
|
|
|
2012-12-10 17:56:27 -08:00
|
|
|
return drawVertexBuffer(vertexBuffer, paint);
|
|
|
|
}
|
2012-04-04 11:38:54 -07:00
|
|
|
|
2012-12-10 17:56:27 -08:00
|
|
|
/**
|
|
|
|
* We create tristrips for the lines much like shape stroke tessellation, using a per-vertex alpha
|
|
|
|
* and additional geometry for defining an alpha slope perimeter.
|
|
|
|
*
|
|
|
|
* Using GL_LINES can be difficult because the rasterization rules for those lines produces some
|
|
|
|
* unexpected results, and may vary between hardware devices. Previously we used a varying-base
|
|
|
|
* in-shader alpha region, but found it to be taxing on some GPUs.
|
|
|
|
*
|
|
|
|
* TODO: try using a fixed input buffer for non-capped lines as in text rendering. this may reduce
|
|
|
|
* memory transfer by removing need for degenerate vertices.
|
|
|
|
*/
|
|
|
|
status_t OpenGLRenderer::drawLines(float* points, int count, SkPaint* paint) {
|
|
|
|
if (mSnapshot->isIgnored() || count < 4) return DrawGlInfo::kStatusDone;
|
2012-04-04 11:38:54 -07:00
|
|
|
|
2012-12-10 17:56:27 -08:00
|
|
|
count &= ~0x3; // round down to nearest four
|
2012-04-04 11:38:54 -07:00
|
|
|
|
2012-12-10 17:56:27 -08:00
|
|
|
VertexBuffer buffer;
|
|
|
|
SkRect bounds;
|
|
|
|
PathTessellator::tessellateLines(points, count, paint, mSnapshot->transform, bounds, buffer);
|
2013-02-08 13:46:40 -08:00
|
|
|
|
|
|
|
if (quickReject(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom)) {
|
|
|
|
return DrawGlInfo::kStatusDone;
|
|
|
|
}
|
|
|
|
|
2013-03-05 10:27:35 -08:00
|
|
|
dirtyLayer(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, currentTransform());
|
2012-05-31 15:21:51 -07:00
|
|
|
|
2012-12-10 17:56:27 -08:00
|
|
|
bool useOffset = !paint->isAntiAlias();
|
|
|
|
return drawVertexBuffer(buffer, paint, useOffset);
|
2011-04-13 17:58:08 -07:00
|
|
|
}
|
|
|
|
|
2012-05-31 15:21:51 -07:00
|
|
|
status_t OpenGLRenderer::drawPoints(float* points, int count, SkPaint* paint) {
|
2013-05-08 18:35:44 -07:00
|
|
|
if (mSnapshot->isIgnored() || count < 2) return DrawGlInfo::kStatusDone;
|
2011-03-21 13:11:28 -07:00
|
|
|
|
2013-05-08 18:35:44 -07:00
|
|
|
count &= ~0x1; // round down to nearest two
|
2012-04-04 11:38:54 -07:00
|
|
|
|
2013-05-08 18:35:44 -07:00
|
|
|
VertexBuffer buffer;
|
|
|
|
SkRect bounds;
|
|
|
|
PathTessellator::tessellatePoints(points, count, paint, mSnapshot->transform, bounds, buffer);
|
2012-04-04 11:38:54 -07:00
|
|
|
|
2013-05-08 18:35:44 -07:00
|
|
|
if (quickReject(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom)) {
|
|
|
|
return DrawGlInfo::kStatusDone;
|
2011-03-21 13:11:28 -07:00
|
|
|
}
|
|
|
|
|
2013-05-08 18:35:44 -07:00
|
|
|
dirtyLayer(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, currentTransform());
|
2012-05-31 15:21:51 -07:00
|
|
|
|
2013-05-08 18:35:44 -07:00
|
|
|
bool useOffset = !paint->isAntiAlias();
|
|
|
|
return drawVertexBuffer(buffer, paint, useOffset);
|
2011-03-21 13:11:28 -07:00
|
|
|
}
|
|
|
|
|
2012-05-31 15:21:51 -07:00
|
|
|
status_t OpenGLRenderer::drawColor(int color, SkXfermode::Mode mode) {
|
2010-11-03 19:58:32 -07:00
|
|
|
// No need to check against the clip, we fill the clip region
|
2012-05-31 15:21:51 -07:00
|
|
|
if (mSnapshot->isIgnored()) return DrawGlInfo::kStatusDone;
|
2010-11-03 19:58:32 -07:00
|
|
|
|
2010-10-22 17:49:18 -07:00
|
|
|
Rect& clip(*mSnapshot->clipRect);
|
|
|
|
clip.snapToPixelBoundaries();
|
2010-12-13 18:24:33 -08:00
|
|
|
|
2010-07-14 16:34:53 -07:00
|
|
|
drawColorRect(clip.left, clip.top, clip.right, clip.bottom, color, mode, true);
|
2012-05-31 15:21:51 -07:00
|
|
|
|
|
|
|
return DrawGlInfo::kStatusDrew;
|
2010-06-25 13:41:57 -07:00
|
|
|
}
|
2010-06-24 19:30:36 -07:00
|
|
|
|
2012-05-31 15:21:51 -07:00
|
|
|
status_t OpenGLRenderer::drawShape(float left, float top, const PathTexture* texture,
|
|
|
|
SkPaint* paint) {
|
|
|
|
if (!texture) return DrawGlInfo::kStatusDone;
|
2011-01-19 21:54:02 -08:00
|
|
|
const AutoTexture autoCleanup(texture);
|
|
|
|
|
|
|
|
const float x = left + texture->left - texture->offset;
|
|
|
|
const float y = top + texture->top - texture->offset;
|
|
|
|
|
|
|
|
drawPathTexture(texture, x, y, paint);
|
2012-05-31 15:21:51 -07:00
|
|
|
|
|
|
|
return DrawGlInfo::kStatusDrew;
|
2011-01-19 21:54:02 -08:00
|
|
|
}
|
|
|
|
|
2012-05-31 15:21:51 -07:00
|
|
|
status_t OpenGLRenderer::drawRoundRect(float left, float top, float right, float bottom,
|
2012-09-17 17:25:49 -07:00
|
|
|
float rx, float ry, SkPaint* p) {
|
2013-03-20 16:31:12 -07:00
|
|
|
if (mSnapshot->isIgnored() || quickRejectPreStroke(left, top, right, bottom, p) ||
|
|
|
|
(p->getAlpha() == 0 && getXfermode(p->getXfermode()) != SkXfermode::kClear_Mode)) {
|
2012-09-17 17:25:49 -07:00
|
|
|
return DrawGlInfo::kStatusDone;
|
|
|
|
}
|
2011-01-19 21:54:02 -08:00
|
|
|
|
2012-09-25 12:00:29 -07:00
|
|
|
if (p->getPathEffect() != 0) {
|
2012-09-17 17:25:49 -07:00
|
|
|
mCaches.activeTexture(0);
|
2013-03-15 19:06:39 -07:00
|
|
|
const PathTexture* texture = mCaches.pathCache.getRoundRect(
|
2012-09-17 17:25:49 -07:00
|
|
|
right - left, bottom - top, rx, ry, p);
|
|
|
|
return drawShape(left, top, texture, p);
|
|
|
|
}
|
|
|
|
|
|
|
|
SkPath path;
|
|
|
|
SkRect rect = SkRect::MakeLTRB(left, top, right, bottom);
|
2012-09-25 12:00:29 -07:00
|
|
|
if (p->getStyle() == SkPaint::kStrokeAndFill_Style) {
|
|
|
|
float outset = p->getStrokeWidth() / 2;
|
|
|
|
rect.outset(outset, outset);
|
|
|
|
rx += outset;
|
|
|
|
ry += outset;
|
|
|
|
}
|
2012-09-17 17:25:49 -07:00
|
|
|
path.addRoundRect(rect, rx, ry);
|
2012-12-10 17:56:27 -08:00
|
|
|
return drawConvexPath(path, p);
|
2011-01-23 14:18:41 -08:00
|
|
|
}
|
2011-01-19 21:54:02 -08:00
|
|
|
|
2012-09-17 17:25:49 -07:00
|
|
|
status_t OpenGLRenderer::drawCircle(float x, float y, float radius, SkPaint* p) {
|
2012-09-25 12:00:29 -07:00
|
|
|
if (mSnapshot->isIgnored() || quickRejectPreStroke(x - radius, y - radius,
|
2013-03-20 16:31:12 -07:00
|
|
|
x + radius, y + radius, p) ||
|
|
|
|
(p->getAlpha() == 0 && getXfermode(p->getXfermode()) != SkXfermode::kClear_Mode)) {
|
2012-09-17 17:25:49 -07:00
|
|
|
return DrawGlInfo::kStatusDone;
|
|
|
|
}
|
2012-09-25 12:00:29 -07:00
|
|
|
if (p->getPathEffect() != 0) {
|
2012-09-17 17:25:49 -07:00
|
|
|
mCaches.activeTexture(0);
|
2013-03-15 19:06:39 -07:00
|
|
|
const PathTexture* texture = mCaches.pathCache.getCircle(radius, p);
|
2012-09-17 17:25:49 -07:00
|
|
|
return drawShape(x - radius, y - radius, texture, p);
|
|
|
|
}
|
|
|
|
|
|
|
|
SkPath path;
|
2012-09-25 12:00:29 -07:00
|
|
|
if (p->getStyle() == SkPaint::kStrokeAndFill_Style) {
|
|
|
|
path.addCircle(x, y, radius + p->getStrokeWidth() / 2);
|
|
|
|
} else {
|
|
|
|
path.addCircle(x, y, radius);
|
|
|
|
}
|
2012-12-10 17:56:27 -08:00
|
|
|
return drawConvexPath(path, p);
|
2011-01-23 14:18:41 -08:00
|
|
|
}
|
2011-01-19 21:54:02 -08:00
|
|
|
|
2012-05-31 15:21:51 -07:00
|
|
|
status_t OpenGLRenderer::drawOval(float left, float top, float right, float bottom,
|
2012-09-17 17:25:49 -07:00
|
|
|
SkPaint* p) {
|
2013-03-20 16:31:12 -07:00
|
|
|
if (mSnapshot->isIgnored() || quickRejectPreStroke(left, top, right, bottom, p) ||
|
|
|
|
(p->getAlpha() == 0 && getXfermode(p->getXfermode()) != SkXfermode::kClear_Mode)) {
|
2012-09-17 17:25:49 -07:00
|
|
|
return DrawGlInfo::kStatusDone;
|
|
|
|
}
|
2011-01-19 21:54:02 -08:00
|
|
|
|
2012-09-25 12:00:29 -07:00
|
|
|
if (p->getPathEffect() != 0) {
|
2012-09-17 17:25:49 -07:00
|
|
|
mCaches.activeTexture(0);
|
2013-03-15 19:06:39 -07:00
|
|
|
const PathTexture* texture = mCaches.pathCache.getOval(right - left, bottom - top, p);
|
2012-09-17 17:25:49 -07:00
|
|
|
return drawShape(left, top, texture, p);
|
|
|
|
}
|
|
|
|
|
|
|
|
SkPath path;
|
|
|
|
SkRect rect = SkRect::MakeLTRB(left, top, right, bottom);
|
2012-09-25 12:00:29 -07:00
|
|
|
if (p->getStyle() == SkPaint::kStrokeAndFill_Style) {
|
|
|
|
rect.outset(p->getStrokeWidth() / 2, p->getStrokeWidth() / 2);
|
|
|
|
}
|
2012-09-17 17:25:49 -07:00
|
|
|
path.addOval(rect);
|
2012-12-10 17:56:27 -08:00
|
|
|
return drawConvexPath(path, p);
|
2011-01-23 14:18:41 -08:00
|
|
|
}
|
|
|
|
|
2012-05-31 15:21:51 -07:00
|
|
|
status_t OpenGLRenderer::drawArc(float left, float top, float right, float bottom,
|
2012-10-04 14:10:49 -07:00
|
|
|
float startAngle, float sweepAngle, bool useCenter, SkPaint* p) {
|
2013-03-20 16:31:12 -07:00
|
|
|
if (mSnapshot->isIgnored() || quickRejectPreStroke(left, top, right, bottom, p) ||
|
|
|
|
(p->getAlpha() == 0 && getXfermode(p->getXfermode()) != SkXfermode::kClear_Mode)) {
|
2012-10-04 14:10:49 -07:00
|
|
|
return DrawGlInfo::kStatusDone;
|
|
|
|
}
|
2011-01-23 16:15:02 -08:00
|
|
|
|
|
|
|
if (fabs(sweepAngle) >= 360.0f) {
|
2012-10-04 14:10:49 -07:00
|
|
|
return drawOval(left, top, right, bottom, p);
|
2011-01-23 16:15:02 -08:00
|
|
|
}
|
|
|
|
|
2012-10-04 14:10:49 -07:00
|
|
|
// TODO: support fills (accounting for concavity if useCenter && sweepAngle > 180)
|
2012-12-10 17:56:27 -08:00
|
|
|
if (p->getStyle() != SkPaint::kStroke_Style || p->getPathEffect() != 0 || useCenter) {
|
2012-10-04 14:10:49 -07:00
|
|
|
mCaches.activeTexture(0);
|
2013-03-15 19:06:39 -07:00
|
|
|
const PathTexture* texture = mCaches.pathCache.getArc(right - left, bottom - top,
|
2012-10-04 14:10:49 -07:00
|
|
|
startAngle, sweepAngle, useCenter, p);
|
|
|
|
return drawShape(left, top, texture, p);
|
|
|
|
}
|
|
|
|
|
|
|
|
SkRect rect = SkRect::MakeLTRB(left, top, right, bottom);
|
|
|
|
if (p->getStyle() == SkPaint::kStrokeAndFill_Style) {
|
|
|
|
rect.outset(p->getStrokeWidth() / 2, p->getStrokeWidth() / 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
SkPath path;
|
|
|
|
if (useCenter) {
|
|
|
|
path.moveTo(rect.centerX(), rect.centerY());
|
|
|
|
}
|
|
|
|
path.arcTo(rect, startAngle, sweepAngle, !useCenter);
|
|
|
|
if (useCenter) {
|
|
|
|
path.close();
|
|
|
|
}
|
2012-12-10 17:56:27 -08:00
|
|
|
return drawConvexPath(path, p);
|
2011-01-23 16:15:02 -08:00
|
|
|
}
|
|
|
|
|
2012-10-02 12:32:25 -07:00
|
|
|
// See SkPaintDefaults.h
|
|
|
|
#define SkPaintDefaults_MiterLimit SkIntToScalar(4)
|
|
|
|
|
2012-05-31 15:21:51 -07:00
|
|
|
status_t OpenGLRenderer::drawRect(float left, float top, float right, float bottom, SkPaint* p) {
|
2013-03-20 16:31:12 -07:00
|
|
|
if (mSnapshot->isIgnored() || quickRejectPreStroke(left, top, right, bottom, p) ||
|
|
|
|
(p->getAlpha() == 0 && getXfermode(p->getXfermode()) != SkXfermode::kClear_Mode)) {
|
2012-05-31 15:21:51 -07:00
|
|
|
return DrawGlInfo::kStatusDone;
|
2010-07-12 20:20:03 -07:00
|
|
|
}
|
|
|
|
|
2012-09-17 17:25:49 -07:00
|
|
|
if (p->getStyle() != SkPaint::kFill_Style) {
|
2012-10-02 12:32:25 -07:00
|
|
|
// only fill style is supported by drawConvexPath, since others have to handle joins
|
|
|
|
if (p->getPathEffect() != 0 || p->getStrokeJoin() != SkPaint::kMiter_Join ||
|
|
|
|
p->getStrokeMiter() != SkPaintDefaults_MiterLimit) {
|
|
|
|
mCaches.activeTexture(0);
|
|
|
|
const PathTexture* texture =
|
2013-03-15 19:06:39 -07:00
|
|
|
mCaches.pathCache.getRect(right - left, bottom - top, p);
|
2012-10-02 12:32:25 -07:00
|
|
|
return drawShape(left, top, texture, p);
|
|
|
|
}
|
|
|
|
|
|
|
|
SkPath path;
|
|
|
|
SkRect rect = SkRect::MakeLTRB(left, top, right, bottom);
|
|
|
|
if (p->getStyle() == SkPaint::kStrokeAndFill_Style) {
|
|
|
|
rect.outset(p->getStrokeWidth() / 2, p->getStrokeWidth() / 2);
|
|
|
|
}
|
|
|
|
path.addRect(rect);
|
2012-12-10 17:56:27 -08:00
|
|
|
return drawConvexPath(path, p);
|
2010-06-28 17:12:22 -07:00
|
|
|
}
|
|
|
|
|
2013-03-05 10:27:35 -08:00
|
|
|
if (p->isAntiAlias() && !currentTransform().isSimple()) {
|
2012-09-17 17:25:49 -07:00
|
|
|
SkPath path;
|
|
|
|
path.addRect(left, top, right, bottom);
|
2012-12-10 17:56:27 -08:00
|
|
|
return drawConvexPath(path, p);
|
2011-05-12 09:06:00 -07:00
|
|
|
} else {
|
2012-09-17 17:25:49 -07:00
|
|
|
drawColorRect(left, top, right, bottom, p->getColor(), getXfermode(p->getXfermode()));
|
2012-12-10 17:56:27 -08:00
|
|
|
return DrawGlInfo::kStatusDrew;
|
2011-05-12 09:06:00 -07:00
|
|
|
}
|
2010-06-25 13:41:57 -07:00
|
|
|
}
|
2010-06-24 19:30:36 -07:00
|
|
|
|
2012-07-19 22:48:17 -07:00
|
|
|
void OpenGLRenderer::drawTextShadow(SkPaint* paint, const char* text, int bytesCount, int count,
|
|
|
|
const float* positions, FontRenderer& fontRenderer, int alpha, SkXfermode::Mode mode,
|
|
|
|
float x, float y) {
|
|
|
|
mCaches.activeTexture(0);
|
|
|
|
|
|
|
|
// NOTE: The drop shadow will not perform gamma correction
|
|
|
|
// if shader-based correction is enabled
|
|
|
|
mCaches.dropShadowCache.setFontRenderer(fontRenderer);
|
|
|
|
const ShadowTexture* shadow = mCaches.dropShadowCache.get(
|
2013-02-04 16:16:33 -08:00
|
|
|
paint, text, bytesCount, count, mDrawModifiers.mShadowRadius, positions);
|
2013-04-08 19:40:31 -07:00
|
|
|
// If the drop shadow exceeds the max texture size or couldn't be
|
|
|
|
// allocated, skip drawing
|
|
|
|
if (!shadow) return;
|
2012-07-19 22:48:17 -07:00
|
|
|
const AutoTexture autoCleanup(shadow);
|
|
|
|
|
2013-02-04 16:16:33 -08:00
|
|
|
const float sx = x - shadow->left + mDrawModifiers.mShadowDx;
|
|
|
|
const float sy = y - shadow->top + mDrawModifiers.mShadowDy;
|
2012-07-19 22:48:17 -07:00
|
|
|
|
2013-02-04 16:16:33 -08:00
|
|
|
const int shadowAlpha = ((mDrawModifiers.mShadowColor >> 24) & 0xFF) * mSnapshot->alpha;
|
|
|
|
int shadowColor = mDrawModifiers.mShadowColor;
|
|
|
|
if (mDrawModifiers.mShader) {
|
2012-07-19 22:48:17 -07:00
|
|
|
shadowColor = 0xffffffff;
|
|
|
|
}
|
|
|
|
|
|
|
|
setupDraw();
|
|
|
|
setupDrawWithTexture(true);
|
|
|
|
setupDrawAlpha8Color(shadowColor, shadowAlpha < 255 ? shadowAlpha : alpha);
|
|
|
|
setupDrawColorFilter();
|
|
|
|
setupDrawShader();
|
|
|
|
setupDrawBlending(true, mode);
|
|
|
|
setupDrawProgram();
|
|
|
|
setupDrawModelView(sx, sy, sx + shadow->width, sy + shadow->height);
|
|
|
|
setupDrawTexture(shadow->id);
|
|
|
|
setupDrawPureColorUniforms();
|
|
|
|
setupDrawColorFilterUniforms();
|
|
|
|
setupDrawShaderUniforms();
|
|
|
|
setupDrawMesh(NULL, (GLvoid*) gMeshTextureOffset);
|
|
|
|
|
|
|
|
glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount);
|
|
|
|
}
|
|
|
|
|
2013-02-27 13:50:45 -08:00
|
|
|
bool OpenGLRenderer::canSkipText(const SkPaint* paint) const {
|
|
|
|
float alpha = (mDrawModifiers.mHasShadow ? 1.0f : paint->getAlpha()) * mSnapshot->alpha;
|
|
|
|
return alpha == 0.0f && getXfermode(paint->getXfermode()) == SkXfermode::kSrcOver_Mode;
|
|
|
|
}
|
|
|
|
|
2013-03-20 16:31:12 -07:00
|
|
|
class TextSetupFunctor: public Functor {
|
|
|
|
public:
|
|
|
|
TextSetupFunctor(OpenGLRenderer& renderer, float x, float y, bool pureTranslate,
|
|
|
|
int alpha, SkXfermode::Mode mode, SkPaint* paint): Functor(),
|
|
|
|
renderer(renderer), x(x), y(y), pureTranslate(pureTranslate),
|
|
|
|
alpha(alpha), mode(mode), paint(paint) {
|
|
|
|
}
|
|
|
|
~TextSetupFunctor() { }
|
|
|
|
|
|
|
|
status_t operator ()(int what, void* data) {
|
|
|
|
renderer.setupDraw();
|
|
|
|
renderer.setupDrawTextGamma(paint);
|
|
|
|
renderer.setupDrawDirtyRegionsDisabled();
|
|
|
|
renderer.setupDrawWithTexture(true);
|
|
|
|
renderer.setupDrawAlpha8Color(paint->getColor(), alpha);
|
|
|
|
renderer.setupDrawColorFilter();
|
|
|
|
renderer.setupDrawShader();
|
|
|
|
renderer.setupDrawBlending(true, mode);
|
|
|
|
renderer.setupDrawProgram();
|
|
|
|
renderer.setupDrawModelView(x, y, x, y, pureTranslate, true);
|
|
|
|
// Calling setupDrawTexture with the name 0 will enable the
|
|
|
|
// uv attributes and increase the texture unit count
|
|
|
|
// texture binding will be performed by the font renderer as
|
|
|
|
// needed
|
|
|
|
renderer.setupDrawTexture(0);
|
|
|
|
renderer.setupDrawPureColorUniforms();
|
|
|
|
renderer.setupDrawColorFilterUniforms();
|
|
|
|
renderer.setupDrawShaderUniforms(pureTranslate);
|
|
|
|
renderer.setupDrawTextGammaUniforms();
|
|
|
|
|
|
|
|
return NO_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
OpenGLRenderer& renderer;
|
|
|
|
float x;
|
|
|
|
float y;
|
|
|
|
bool pureTranslate;
|
|
|
|
int alpha;
|
|
|
|
SkXfermode::Mode mode;
|
|
|
|
SkPaint* paint;
|
|
|
|
};
|
|
|
|
|
2012-05-31 15:21:51 -07:00
|
|
|
status_t OpenGLRenderer::drawPosText(const char* text, int bytesCount, int count,
|
2012-01-17 17:39:26 -08:00
|
|
|
const float* positions, SkPaint* paint) {
|
2013-02-27 13:50:45 -08:00
|
|
|
if (text == NULL || count == 0 || mSnapshot->isIgnored() || canSkipText(paint)) {
|
2012-05-31 15:21:51 -07:00
|
|
|
return DrawGlInfo::kStatusDone;
|
2012-01-17 17:39:26 -08:00
|
|
|
}
|
|
|
|
|
2012-01-18 12:39:17 -08:00
|
|
|
// NOTE: Skia does not support perspective transform on drawPosText yet
|
2013-03-05 10:27:35 -08:00
|
|
|
if (!currentTransform().isSimple()) {
|
2012-05-31 15:21:51 -07:00
|
|
|
return DrawGlInfo::kStatusDone;
|
2012-01-18 12:39:17 -08:00
|
|
|
}
|
|
|
|
|
2013-06-13 14:39:01 -07:00
|
|
|
mCaches.enableScissor();
|
|
|
|
|
2012-01-18 12:39:17 -08:00
|
|
|
float x = 0.0f;
|
|
|
|
float y = 0.0f;
|
2013-03-05 10:27:35 -08:00
|
|
|
const bool pureTranslate = currentTransform().isPureTranslate();
|
2012-01-18 12:39:17 -08:00
|
|
|
if (pureTranslate) {
|
2013-03-05 10:27:35 -08:00
|
|
|
x = (int) floorf(x + currentTransform().getTranslateX() + 0.5f);
|
|
|
|
y = (int) floorf(y + currentTransform().getTranslateY() + 0.5f);
|
2012-01-18 12:39:17 -08:00
|
|
|
}
|
|
|
|
|
2012-07-13 18:25:35 -07:00
|
|
|
FontRenderer& fontRenderer = mCaches.fontRenderer->getFontRenderer(paint);
|
2013-02-26 19:10:14 -08:00
|
|
|
fontRenderer.setFont(paint, mat4::identity());
|
2012-01-18 12:39:17 -08:00
|
|
|
|
|
|
|
int alpha;
|
|
|
|
SkXfermode::Mode mode;
|
|
|
|
getAlphaAndMode(paint, &alpha, &mode);
|
|
|
|
|
2013-02-04 16:16:33 -08:00
|
|
|
if (CC_UNLIKELY(mDrawModifiers.mHasShadow)) {
|
2013-01-08 11:15:30 -08:00
|
|
|
drawTextShadow(paint, text, bytesCount, count, positions, fontRenderer,
|
|
|
|
alpha, mode, 0.0f, 0.0f);
|
2012-07-19 22:48:17 -07:00
|
|
|
}
|
|
|
|
|
2012-01-18 12:39:17 -08:00
|
|
|
// Pick the appropriate texture filtering
|
2013-03-05 10:27:35 -08:00
|
|
|
bool linearFilter = currentTransform().changesBounds();
|
2012-01-18 12:39:17 -08:00
|
|
|
if (pureTranslate && !linearFilter) {
|
|
|
|
linearFilter = fabs(y - (int) y) > 0.0f || fabs(x - (int) x) > 0.0f;
|
|
|
|
}
|
2013-03-20 16:31:12 -07:00
|
|
|
fontRenderer.setTextureFiltering(linearFilter);
|
2012-01-18 12:39:17 -08:00
|
|
|
|
|
|
|
const Rect* clip = pureTranslate ? mSnapshot->clipRect : &mSnapshot->getLocalClip();
|
|
|
|
Rect bounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f);
|
|
|
|
|
2012-02-01 16:10:55 -08:00
|
|
|
const bool hasActiveLayer = hasLayer();
|
2012-01-18 12:39:17 -08:00
|
|
|
|
2013-03-20 16:31:12 -07:00
|
|
|
TextSetupFunctor functor(*this, x, y, pureTranslate, alpha, mode, paint);
|
2012-01-18 12:39:17 -08:00
|
|
|
if (fontRenderer.renderPosText(paint, clip, text, 0, bytesCount, count, x, y,
|
2013-03-20 16:31:12 -07:00
|
|
|
positions, hasActiveLayer ? &bounds : NULL, &functor)) {
|
2012-01-18 12:39:17 -08:00
|
|
|
if (hasActiveLayer) {
|
|
|
|
if (!pureTranslate) {
|
2013-03-05 10:27:35 -08:00
|
|
|
currentTransform().mapRect(bounds);
|
2012-01-18 12:39:17 -08:00
|
|
|
}
|
|
|
|
dirtyLayerUnchecked(bounds, getRegion());
|
|
|
|
}
|
|
|
|
}
|
2012-05-31 15:21:51 -07:00
|
|
|
|
|
|
|
return DrawGlInfo::kStatusDrew;
|
2012-01-17 17:39:26 -08:00
|
|
|
}
|
|
|
|
|
2013-03-05 16:43:31 -08:00
|
|
|
mat4 OpenGLRenderer::findBestFontTransform(const mat4& transform) const {
|
|
|
|
mat4 fontTransform;
|
|
|
|
if (CC_LIKELY(transform.isPureTranslate())) {
|
|
|
|
fontTransform = mat4::identity();
|
|
|
|
} else {
|
|
|
|
if (CC_UNLIKELY(transform.isPerspective())) {
|
2013-03-07 17:01:05 -08:00
|
|
|
fontTransform = mat4::identity();
|
2013-03-05 16:43:31 -08:00
|
|
|
} else {
|
|
|
|
float sx, sy;
|
|
|
|
currentTransform().decomposeScale(sx, sy);
|
|
|
|
fontTransform.loadScale(sx, sy, 1.0f);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return fontTransform;
|
|
|
|
}
|
|
|
|
|
2013-05-03 16:35:54 -07:00
|
|
|
status_t OpenGLRenderer::drawText(const char* text, int bytesCount, int count, float x, float y,
|
|
|
|
const float* positions, SkPaint* paint, float totalAdvance, const Rect& bounds,
|
2013-03-04 10:19:31 -08:00
|
|
|
DrawOpMode drawOpMode) {
|
|
|
|
|
|
|
|
if (drawOpMode == kDrawOpMode_Immediate) {
|
2013-05-31 11:38:03 -07:00
|
|
|
// The checks for corner-case ignorable text and quick rejection is only done for immediate
|
|
|
|
// drawing as ops from DeferredDisplayList are already filtered for these
|
|
|
|
if (text == NULL || count == 0 || mSnapshot->isIgnored() || canSkipText(paint) ||
|
|
|
|
quickReject(bounds)) {
|
|
|
|
return DrawGlInfo::kStatusDone;
|
|
|
|
}
|
2011-12-01 20:08:50 -08:00
|
|
|
}
|
|
|
|
|
2010-12-06 18:47:50 -08:00
|
|
|
const float oldX = x;
|
|
|
|
const float oldY = y;
|
2013-03-05 16:43:31 -08:00
|
|
|
|
|
|
|
const mat4& transform = currentTransform();
|
|
|
|
const bool pureTranslate = transform.isPureTranslate();
|
2013-02-26 19:10:14 -08:00
|
|
|
|
2012-02-01 16:10:55 -08:00
|
|
|
if (CC_LIKELY(pureTranslate)) {
|
2013-03-05 16:43:31 -08:00
|
|
|
x = (int) floorf(x + transform.getTranslateX() + 0.5f);
|
|
|
|
y = (int) floorf(y + transform.getTranslateY() + 0.5f);
|
2010-12-06 18:07:02 -08:00
|
|
|
}
|
|
|
|
|
2010-12-14 15:55:39 -08:00
|
|
|
int alpha;
|
|
|
|
SkXfermode::Mode mode;
|
|
|
|
getAlphaAndMode(paint, &alpha, &mode);
|
2010-10-15 16:06:03 -07:00
|
|
|
|
2013-02-26 19:10:14 -08:00
|
|
|
FontRenderer& fontRenderer = mCaches.fontRenderer->getFontRenderer(paint);
|
|
|
|
|
2013-02-04 16:16:33 -08:00
|
|
|
if (CC_UNLIKELY(mDrawModifiers.mHasShadow)) {
|
2013-02-26 19:10:14 -08:00
|
|
|
fontRenderer.setFont(paint, mat4::identity());
|
|
|
|
drawTextShadow(paint, text, bytesCount, count, positions, fontRenderer,
|
|
|
|
alpha, mode, oldX, oldY);
|
2010-08-13 19:39:53 -07:00
|
|
|
}
|
|
|
|
|
2013-03-04 11:14:26 -08:00
|
|
|
const bool hasActiveLayer = hasLayer();
|
|
|
|
|
2013-03-05 16:43:31 -08:00
|
|
|
// We only pass a partial transform to the font renderer. That partial
|
|
|
|
// matrix defines how glyphs are rasterized. Typically we want glyphs
|
|
|
|
// to be rasterized at their final size on screen, which means the partial
|
|
|
|
// matrix needs to take the scale factor into account.
|
|
|
|
// When a partial matrix is used to transform glyphs during rasterization,
|
|
|
|
// the mesh is generated with the inverse transform (in the case of scale,
|
|
|
|
// the mesh is generated at 1.0 / scale for instance.) This allows us to
|
|
|
|
// apply the full transform matrix at draw time in the vertex shader.
|
|
|
|
// Applying the full matrix in the shader is the easiest way to handle
|
|
|
|
// rotation and perspective and allows us to always generated quads in the
|
|
|
|
// font renderer which greatly simplifies the code, clipping in particular.
|
|
|
|
mat4 fontTransform = findBestFontTransform(transform);
|
2013-03-05 10:27:35 -08:00
|
|
|
fontRenderer.setFont(paint, fontTransform);
|
2013-02-26 19:10:14 -08:00
|
|
|
|
2010-12-06 18:07:02 -08:00
|
|
|
// Pick the appropriate texture filtering
|
2013-02-28 12:15:35 -08:00
|
|
|
bool linearFilter = !pureTranslate || fabs(y - (int) y) > 0.0f || fabs(x - (int) x) > 0.0f;
|
2013-03-20 16:31:12 -07:00
|
|
|
fontRenderer.setTextureFiltering(linearFilter);
|
2010-07-30 19:18:16 -07:00
|
|
|
|
2013-03-05 16:43:31 -08:00
|
|
|
// TODO: Implement better clipping for scaled/rotated text
|
|
|
|
const Rect* clip = !pureTranslate ? NULL : mSnapshot->clipRect;
|
2013-05-03 16:35:54 -07:00
|
|
|
Rect layerBounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f);
|
2010-10-27 18:57:51 -07:00
|
|
|
|
2012-07-23 15:22:52 -07:00
|
|
|
bool status;
|
2013-03-20 16:31:12 -07:00
|
|
|
TextSetupFunctor functor(*this, x, y, pureTranslate, alpha, mode, paint);
|
2013-03-04 10:19:31 -08:00
|
|
|
|
|
|
|
// don't call issuedrawcommand, do it at end of batch
|
|
|
|
bool forceFinish = (drawOpMode != kDrawOpMode_Defer);
|
2012-09-28 13:55:44 -07:00
|
|
|
if (CC_UNLIKELY(paint->getTextAlign() != SkPaint::kLeft_Align)) {
|
2012-07-30 15:50:00 -07:00
|
|
|
SkPaint paintCopy(*paint);
|
|
|
|
paintCopy.setTextAlign(SkPaint::kLeft_Align);
|
|
|
|
status = fontRenderer.renderPosText(&paintCopy, clip, text, 0, bytesCount, count, x, y,
|
2013-05-03 16:35:54 -07:00
|
|
|
positions, hasActiveLayer ? &layerBounds : NULL, &functor, forceFinish);
|
2012-07-23 15:22:52 -07:00
|
|
|
} else {
|
2012-07-30 15:50:00 -07:00
|
|
|
status = fontRenderer.renderPosText(paint, clip, text, 0, bytesCount, count, x, y,
|
2013-05-03 16:35:54 -07:00
|
|
|
positions, hasActiveLayer ? &layerBounds : NULL, &functor, forceFinish);
|
2012-07-23 15:22:52 -07:00
|
|
|
}
|
2012-08-06 14:51:10 -07:00
|
|
|
|
2013-03-04 10:19:31 -08:00
|
|
|
if ((status || drawOpMode != kDrawOpMode_Immediate) && hasActiveLayer) {
|
2013-03-05 16:43:31 -08:00
|
|
|
if (!pureTranslate) {
|
2013-05-03 16:35:54 -07:00
|
|
|
transform.mapRect(layerBounds);
|
2013-02-28 12:15:35 -08:00
|
|
|
}
|
2013-05-03 16:35:54 -07:00
|
|
|
dirtyLayerUnchecked(layerBounds, getRegion());
|
2010-10-27 18:57:51 -07:00
|
|
|
}
|
2010-07-21 21:33:20 -07:00
|
|
|
|
2013-05-03 16:35:54 -07:00
|
|
|
drawTextDecorations(text, bytesCount, totalAdvance, oldX, oldY, paint);
|
2012-05-31 15:21:51 -07:00
|
|
|
|
|
|
|
return DrawGlInfo::kStatusDrew;
|
2010-07-21 21:33:20 -07:00
|
|
|
}
|
|
|
|
|
2012-05-31 15:21:51 -07:00
|
|
|
status_t OpenGLRenderer::drawTextOnPath(const char* text, int bytesCount, int count, SkPath* path,
|
2012-02-24 16:48:34 -08:00
|
|
|
float hOffset, float vOffset, SkPaint* paint) {
|
2013-02-27 13:50:45 -08:00
|
|
|
if (text == NULL || count == 0 || mSnapshot->isIgnored() || canSkipText(paint)) {
|
2012-05-31 15:21:51 -07:00
|
|
|
return DrawGlInfo::kStatusDone;
|
2012-02-24 17:54:07 -08:00
|
|
|
}
|
|
|
|
|
2013-06-13 14:39:01 -07:00
|
|
|
// TODO: avoid scissor by calculating maximum bounds using path bounds + font metrics
|
|
|
|
mCaches.enableScissor();
|
|
|
|
|
2012-07-13 18:25:35 -07:00
|
|
|
FontRenderer& fontRenderer = mCaches.fontRenderer->getFontRenderer(paint);
|
2013-02-26 19:10:14 -08:00
|
|
|
fontRenderer.setFont(paint, mat4::identity());
|
2013-03-20 16:31:12 -07:00
|
|
|
fontRenderer.setTextureFiltering(true);
|
2012-02-24 17:54:07 -08:00
|
|
|
|
|
|
|
int alpha;
|
|
|
|
SkXfermode::Mode mode;
|
|
|
|
getAlphaAndMode(paint, &alpha, &mode);
|
|
|
|
|
|
|
|
setupDraw();
|
2012-07-16 17:04:24 -07:00
|
|
|
setupDrawTextGamma(paint);
|
2012-02-24 17:54:07 -08:00
|
|
|
setupDrawDirtyRegionsDisabled();
|
|
|
|
setupDrawWithTexture(true);
|
|
|
|
setupDrawAlpha8Color(paint->getColor(), alpha);
|
|
|
|
setupDrawColorFilter();
|
|
|
|
setupDrawShader();
|
|
|
|
setupDrawBlending(true, mode);
|
|
|
|
setupDrawProgram();
|
2012-02-28 18:17:02 -08:00
|
|
|
setupDrawModelView(0.0f, 0.0f, 0.0f, 0.0f, false, true);
|
2013-03-20 16:31:12 -07:00
|
|
|
// Calling setupDrawTexture with the name 0 will enable the
|
|
|
|
// uv attributes and increase the texture unit count
|
|
|
|
// texture binding will be performed by the font renderer as
|
|
|
|
// needed
|
|
|
|
setupDrawTexture(0);
|
2012-02-24 17:54:07 -08:00
|
|
|
setupDrawPureColorUniforms();
|
|
|
|
setupDrawColorFilterUniforms();
|
2012-02-28 18:17:02 -08:00
|
|
|
setupDrawShaderUniforms(false);
|
2012-07-16 17:04:24 -07:00
|
|
|
setupDrawTextGammaUniforms();
|
2012-02-28 18:17:02 -08:00
|
|
|
|
|
|
|
const Rect* clip = &mSnapshot->getLocalClip();
|
|
|
|
Rect bounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f);
|
2012-02-24 17:54:07 -08:00
|
|
|
|
2012-02-28 18:17:02 -08:00
|
|
|
const bool hasActiveLayer = hasLayer();
|
|
|
|
|
|
|
|
if (fontRenderer.renderTextOnPath(paint, clip, text, 0, bytesCount, count, path,
|
|
|
|
hOffset, vOffset, hasActiveLayer ? &bounds : NULL)) {
|
|
|
|
if (hasActiveLayer) {
|
2013-03-05 10:27:35 -08:00
|
|
|
currentTransform().mapRect(bounds);
|
2012-02-28 18:17:02 -08:00
|
|
|
dirtyLayerUnchecked(bounds, getRegion());
|
|
|
|
}
|
|
|
|
}
|
2012-05-31 15:21:51 -07:00
|
|
|
|
|
|
|
return DrawGlInfo::kStatusDrew;
|
2012-02-24 16:48:34 -08:00
|
|
|
}
|
|
|
|
|
2012-05-31 15:21:51 -07:00
|
|
|
status_t OpenGLRenderer::drawPath(SkPath* path, SkPaint* paint) {
|
|
|
|
if (mSnapshot->isIgnored()) return DrawGlInfo::kStatusDone;
|
2010-10-11 17:58:29 -07:00
|
|
|
|
2011-12-13 14:55:06 -08:00
|
|
|
mCaches.activeTexture(0);
|
2010-08-04 15:40:07 -07:00
|
|
|
|
2010-08-23 21:05:08 -07:00
|
|
|
const PathTexture* texture = mCaches.pathCache.get(path, paint);
|
2012-05-31 15:21:51 -07:00
|
|
|
if (!texture) return DrawGlInfo::kStatusDone;
|
2010-08-06 11:18:34 -07:00
|
|
|
const AutoTexture autoCleanup(texture);
|
2010-08-04 15:40:07 -07:00
|
|
|
|
2010-08-18 17:10:07 -07:00
|
|
|
const float x = texture->left - texture->offset;
|
|
|
|
const float y = texture->top - texture->offset;
|
|
|
|
|
2011-01-19 21:54:02 -08:00
|
|
|
drawPathTexture(texture, x, y, paint);
|
2012-05-31 15:21:51 -07:00
|
|
|
|
|
|
|
return DrawGlInfo::kStatusDrew;
|
2010-08-04 15:40:07 -07:00
|
|
|
}
|
|
|
|
|
2013-03-15 17:24:33 -07:00
|
|
|
status_t OpenGLRenderer::drawLayer(Layer* layer, float x, float y) {
|
2012-09-18 15:40:58 -07:00
|
|
|
if (!layer) {
|
2012-05-31 15:21:51 -07:00
|
|
|
return DrawGlInfo::kStatusDone;
|
2011-01-11 14:29:25 -08:00
|
|
|
}
|
|
|
|
|
2012-10-17 18:18:35 -07:00
|
|
|
mat4* transform = NULL;
|
|
|
|
if (layer->isTextureLayer()) {
|
|
|
|
transform = &layer->getTransform();
|
|
|
|
if (!transform->isIdentity()) {
|
|
|
|
save(0);
|
2013-03-05 10:27:35 -08:00
|
|
|
currentTransform().multiply(*transform);
|
2012-10-17 18:18:35 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-06-13 14:39:01 -07:00
|
|
|
bool clipRequired = false;
|
2012-09-18 15:40:58 -07:00
|
|
|
const bool rejected = quickRejectNoScissor(x, y,
|
2013-06-13 14:39:01 -07:00
|
|
|
x + layer->layer.getWidth(), y + layer->layer.getHeight(), &clipRequired);
|
2012-08-06 14:51:10 -07:00
|
|
|
|
2012-09-18 15:40:58 -07:00
|
|
|
if (rejected) {
|
2012-10-17 18:18:35 -07:00
|
|
|
if (transform && !transform->isIdentity()) {
|
|
|
|
restore();
|
|
|
|
}
|
2012-09-18 15:40:58 -07:00
|
|
|
return DrawGlInfo::kStatusDone;
|
|
|
|
}
|
|
|
|
|
2012-11-29 17:52:58 -08:00
|
|
|
updateLayer(layer, true);
|
2012-03-02 13:37:47 -08:00
|
|
|
|
2013-06-13 14:39:01 -07:00
|
|
|
mCaches.setScissorEnabled(mScissorOptimizationDisabled || clipRequired);
|
2011-12-13 14:55:06 -08:00
|
|
|
mCaches.activeTexture(0);
|
2011-01-11 14:29:25 -08:00
|
|
|
|
2012-02-01 16:10:55 -08:00
|
|
|
if (CC_LIKELY(!layer->region.isEmpty())) {
|
2013-02-04 16:16:33 -08:00
|
|
|
SkiaColorFilter* oldFilter = mDrawModifiers.mColorFilter;
|
|
|
|
mDrawModifiers.mColorFilter = layer->getColorFilter();
|
2012-09-26 11:23:17 -07:00
|
|
|
|
2011-01-22 00:32:12 -08:00
|
|
|
if (layer->region.isRect()) {
|
2013-04-15 16:08:28 -07:00
|
|
|
DRAW_DOUBLE_STENCIL_IF(!layer->hasDrawnSinceUpdate,
|
|
|
|
composeLayerRect(layer, layer->regionRect));
|
2011-01-22 00:32:12 -08:00
|
|
|
} else if (layer->mesh) {
|
2013-03-29 10:59:59 -07:00
|
|
|
const float a = getLayerAlpha(layer);
|
2011-01-22 00:32:12 -08:00
|
|
|
setupDraw();
|
|
|
|
setupDrawWithTexture();
|
2011-01-24 20:40:18 -08:00
|
|
|
setupDrawColor(a, a, a, a);
|
2011-01-22 00:32:12 -08:00
|
|
|
setupDrawColorFilter();
|
2011-07-07 20:50:11 -07:00
|
|
|
setupDrawBlending(layer->isBlend() || a < 1.0f, layer->getMode(), false);
|
2011-01-22 00:32:12 -08:00
|
|
|
setupDrawProgram();
|
|
|
|
setupDrawPureColorUniforms();
|
|
|
|
setupDrawColorFilterUniforms();
|
2011-07-07 20:50:11 -07:00
|
|
|
setupDrawTexture(layer->getTexture());
|
2013-03-05 10:27:35 -08:00
|
|
|
if (CC_LIKELY(currentTransform().isPureTranslate())) {
|
|
|
|
int tx = (int) floorf(x + currentTransform().getTranslateX() + 0.5f);
|
|
|
|
int ty = (int) floorf(y + currentTransform().getTranslateY() + 0.5f);
|
2011-07-07 20:50:11 -07:00
|
|
|
|
2011-11-30 20:21:23 -08:00
|
|
|
layer->setFilter(GL_NEAREST);
|
2012-08-06 14:51:10 -07:00
|
|
|
setupDrawModelViewTranslate(tx, ty,
|
|
|
|
tx + layer->layer.getWidth(), ty + layer->layer.getHeight(), true);
|
2011-07-07 20:50:11 -07:00
|
|
|
} else {
|
2011-11-30 20:21:23 -08:00
|
|
|
layer->setFilter(GL_LINEAR);
|
2011-07-07 20:50:11 -07:00
|
|
|
setupDrawModelViewTranslate(x, y,
|
|
|
|
x + layer->layer.getWidth(), y + layer->layer.getHeight());
|
|
|
|
}
|
2011-01-22 00:32:12 -08:00
|
|
|
setupDrawMesh(&layer->mesh[0].position[0], &layer->mesh[0].texture[0]);
|
|
|
|
|
2013-04-15 16:08:28 -07:00
|
|
|
DRAW_DOUBLE_STENCIL_IF(!layer->hasDrawnSinceUpdate,
|
|
|
|
glDrawElements(GL_TRIANGLES, layer->meshElementCount,
|
|
|
|
GL_UNSIGNED_SHORT, layer->meshIndices));
|
2011-01-22 00:32:12 -08:00
|
|
|
|
|
|
|
finishDrawTexture();
|
2011-02-01 22:59:58 -08:00
|
|
|
|
|
|
|
#if DEBUG_LAYERS_AS_REGIONS
|
|
|
|
drawRegionRects(layer->region);
|
|
|
|
#endif
|
2011-01-22 00:32:12 -08:00
|
|
|
}
|
2012-08-06 14:51:10 -07:00
|
|
|
|
2013-02-04 16:16:33 -08:00
|
|
|
mDrawModifiers.mColorFilter = oldFilter;
|
2012-09-26 11:23:17 -07:00
|
|
|
|
2012-11-29 17:52:58 -08:00
|
|
|
if (layer->debugDrawUpdate) {
|
|
|
|
layer->debugDrawUpdate = false;
|
2012-08-06 14:51:10 -07:00
|
|
|
drawColorRect(x, y, x + layer->layer.getWidth(), y + layer->layer.getHeight(),
|
|
|
|
0x7f00ff00, SkXfermode::kSrcOver_Mode);
|
|
|
|
}
|
2011-01-16 12:54:25 -08:00
|
|
|
}
|
2013-04-15 16:08:28 -07:00
|
|
|
layer->hasDrawnSinceUpdate = true;
|
2012-05-31 15:21:51 -07:00
|
|
|
|
2012-10-17 18:18:35 -07:00
|
|
|
if (transform && !transform->isIdentity()) {
|
|
|
|
restore();
|
|
|
|
}
|
|
|
|
|
2012-05-31 15:21:51 -07:00
|
|
|
return DrawGlInfo::kStatusDrew;
|
2011-01-11 14:29:25 -08:00
|
|
|
}
|
|
|
|
|
2010-07-14 19:18:51 -07:00
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Shaders
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
void OpenGLRenderer::resetShader() {
|
2013-02-04 16:16:33 -08:00
|
|
|
mDrawModifiers.mShader = NULL;
|
2010-07-30 19:18:16 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void OpenGLRenderer::setupShader(SkiaShader* shader) {
|
2013-02-04 16:16:33 -08:00
|
|
|
mDrawModifiers.mShader = shader;
|
|
|
|
if (mDrawModifiers.mShader) {
|
2013-06-04 18:00:09 -07:00
|
|
|
mDrawModifiers.mShader->setCaches(mCaches);
|
2010-07-30 19:18:16 -07:00
|
|
|
}
|
2010-07-16 17:10:13 -07:00
|
|
|
}
|
|
|
|
|
2010-08-02 18:50:22 -07:00
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Color filters
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
void OpenGLRenderer::resetColorFilter() {
|
2013-02-04 16:16:33 -08:00
|
|
|
mDrawModifiers.mColorFilter = NULL;
|
2010-08-02 18:50:22 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void OpenGLRenderer::setupColorFilter(SkiaColorFilter* filter) {
|
2013-02-04 16:16:33 -08:00
|
|
|
mDrawModifiers.mColorFilter = filter;
|
2010-08-02 18:50:22 -07:00
|
|
|
}
|
|
|
|
|
2010-08-13 19:39:53 -07:00
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Drop shadow
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
void OpenGLRenderer::resetShadow() {
|
2013-02-04 16:16:33 -08:00
|
|
|
mDrawModifiers.mHasShadow = false;
|
2010-08-13 19:39:53 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void OpenGLRenderer::setupShadow(float radius, float dx, float dy, int color) {
|
2013-02-04 16:16:33 -08:00
|
|
|
mDrawModifiers.mHasShadow = true;
|
|
|
|
mDrawModifiers.mShadowRadius = radius;
|
|
|
|
mDrawModifiers.mShadowDx = dx;
|
|
|
|
mDrawModifiers.mShadowDy = dy;
|
|
|
|
mDrawModifiers.mShadowColor = color;
|
2010-08-13 19:39:53 -07:00
|
|
|
}
|
|
|
|
|
2012-01-23 17:09:05 -08:00
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Draw filters
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
void OpenGLRenderer::resetPaintFilter() {
|
2013-03-04 10:19:31 -08:00
|
|
|
// when clearing the PaintFilter, the masks should also be cleared for simple DrawModifier
|
|
|
|
// comparison, see MergingDrawBatch::canMergeWith
|
2013-02-04 16:16:33 -08:00
|
|
|
mDrawModifiers.mHasDrawFilter = false;
|
2013-03-04 10:19:31 -08:00
|
|
|
mDrawModifiers.mPaintFilterClearBits = 0;
|
|
|
|
mDrawModifiers.mPaintFilterSetBits = 0;
|
2012-01-23 17:09:05 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
void OpenGLRenderer::setupPaintFilter(int clearBits, int setBits) {
|
2013-02-04 16:16:33 -08:00
|
|
|
mDrawModifiers.mHasDrawFilter = true;
|
|
|
|
mDrawModifiers.mPaintFilterClearBits = clearBits & SkPaint::kAllFlags;
|
|
|
|
mDrawModifiers.mPaintFilterSetBits = setBits & SkPaint::kAllFlags;
|
2012-01-23 17:09:05 -08:00
|
|
|
}
|
|
|
|
|
2013-03-15 17:24:33 -07:00
|
|
|
SkPaint* OpenGLRenderer::filterPaint(SkPaint* paint) {
|
2013-02-27 11:53:12 -08:00
|
|
|
if (CC_LIKELY(!mDrawModifiers.mHasDrawFilter || !paint)) {
|
|
|
|
return paint;
|
|
|
|
}
|
2012-01-23 17:09:05 -08:00
|
|
|
|
|
|
|
uint32_t flags = paint->getFlags();
|
|
|
|
|
|
|
|
mFilteredPaint = *paint;
|
2013-02-04 16:16:33 -08:00
|
|
|
mFilteredPaint.setFlags((flags & ~mDrawModifiers.mPaintFilterClearBits) |
|
|
|
|
mDrawModifiers.mPaintFilterSetBits);
|
2012-01-23 17:09:05 -08:00
|
|
|
|
|
|
|
return &mFilteredPaint;
|
|
|
|
}
|
|
|
|
|
2010-07-12 20:20:03 -07:00
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Drawing implementation
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
Pack preloaded framework assets in a texture atlas
When the Android runtime starts, the system preloads a series of assets
in the Zygote process. These assets are shared across all processes.
Unfortunately, each one of these assets is later uploaded in its own
OpenGL texture, once per process. This wastes memory and generates
unnecessary OpenGL state changes.
This CL introduces an asset server that provides an atlas to all processes.
Note: bitmaps used by skia shaders are *not* sampled from the atlas.
It's an uncommon use case and would require extra texture transforms
in the GL shaders.
WHAT IS THE ASSETS ATLAS
The "assets atlas" is a single, shareable graphic buffer that contains
all the system's preloaded bitmap drawables (this includes 9-patches.)
The atlas is made of two distinct objects: the graphic buffer that
contains the actual pixels and the map which indicates where each
preloaded bitmap can be found in the atlas (essentially a pair of
x and y coordinates.)
HOW IS THE ASSETS ATLAS GENERATED
Because we need to support a wide variety of devices and because it
is easy to change the list of preloaded drawables, the atlas is
generated at runtime, during the startup phase of the system process.
There are several steps that lead to the atlas generation:
1. If the device is booting for the first time, or if the device was
updated, we need to find the best atlas configuration. To do so,
the atlas service tries a number of width, height and algorithm
variations that allows us to pack as many assets as possible while
using as little memory as possible. Once a best configuration is found,
it gets written to disk in /data/system/framework_atlas
2. Given a best configuration (algorithm variant, dimensions and
number of bitmaps that can be packed in the atlas), the atlas service
packs all the preloaded bitmaps into a single graphic buffer object.
3. The packing is done using Skia in a temporary native bitmap. The
Skia bitmap is then copied into the graphic buffer using OpenGL ES
to benefit from texture swizzling.
HOW PROCESSES USE THE ATLAS
Whenever a process' hardware renderer initializes its EGL context,
it queries the atlas service for the graphic buffer and the map.
It is important to remember that both the context and the map will
be valid for the lifetime of the hardware renderer (if the system
process goes down, all apps get killed as well.)
Every time the hardware renderer needs to render a bitmap, it first
checks whether the bitmap can be found in the assets atlas. When
the bitmap is part of the atlas, texture coordinates are remapped
appropriately before rendering.
Change-Id: I8eaecf53e7f6a33d90da3d0047c5ceec89ea3af0
2013-04-17 18:54:38 -07:00
|
|
|
Texture* OpenGLRenderer::getTexture(SkBitmap* bitmap) {
|
|
|
|
Texture* texture = mCaches.assetAtlas.getEntryTexture(bitmap);
|
|
|
|
if (!texture) {
|
|
|
|
return mCaches.textureCache.get(bitmap);
|
|
|
|
}
|
|
|
|
return texture;
|
|
|
|
}
|
|
|
|
|
2011-01-19 21:54:02 -08:00
|
|
|
void OpenGLRenderer::drawPathTexture(const PathTexture* texture,
|
|
|
|
float x, float y, SkPaint* paint) {
|
|
|
|
if (quickReject(x, y, x + texture->width, y + texture->height)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
int alpha;
|
|
|
|
SkXfermode::Mode mode;
|
|
|
|
getAlphaAndMode(paint, &alpha, &mode);
|
|
|
|
|
|
|
|
setupDraw();
|
|
|
|
setupDrawWithTexture(true);
|
|
|
|
setupDrawAlpha8Color(paint->getColor(), alpha);
|
|
|
|
setupDrawColorFilter();
|
|
|
|
setupDrawShader();
|
|
|
|
setupDrawBlending(true, mode);
|
|
|
|
setupDrawProgram();
|
|
|
|
setupDrawModelView(x, y, x + texture->width, y + texture->height);
|
|
|
|
setupDrawTexture(texture->id);
|
|
|
|
setupDrawPureColorUniforms();
|
|
|
|
setupDrawColorFilterUniforms();
|
|
|
|
setupDrawShaderUniforms();
|
|
|
|
setupDrawMesh(NULL, (GLvoid*) gMeshTextureOffset);
|
|
|
|
|
|
|
|
glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount);
|
|
|
|
|
|
|
|
finishDrawTexture();
|
|
|
|
}
|
|
|
|
|
2010-09-10 19:20:06 -07:00
|
|
|
// Same values used by Skia
|
2010-08-16 20:26:20 -07:00
|
|
|
#define kStdStrikeThru_Offset (-6.0f / 21.0f)
|
|
|
|
#define kStdUnderline_Offset (1.0f / 9.0f)
|
|
|
|
#define kStdUnderline_Thickness (1.0f / 18.0f)
|
|
|
|
|
2013-05-03 16:35:54 -07:00
|
|
|
void OpenGLRenderer::drawTextDecorations(const char* text, int bytesCount, float underlineWidth,
|
2010-08-16 20:26:20 -07:00
|
|
|
float x, float y, SkPaint* paint) {
|
|
|
|
// Handle underline and strike-through
|
|
|
|
uint32_t flags = paint->getFlags();
|
|
|
|
if (flags & (SkPaint::kUnderlineText_Flag | SkPaint::kStrikeThruText_Flag)) {
|
2011-06-01 14:52:00 -07:00
|
|
|
SkPaint paintCopy(*paint);
|
2010-08-16 20:26:20 -07:00
|
|
|
|
2012-02-01 16:10:55 -08:00
|
|
|
if (CC_LIKELY(underlineWidth > 0.0f)) {
|
2011-06-01 14:52:00 -07:00
|
|
|
const float textSize = paintCopy.getTextSize();
|
2011-01-23 13:32:12 -08:00
|
|
|
const float strokeWidth = fmax(textSize * kStdUnderline_Thickness, 1.0f);
|
2010-08-16 20:26:20 -07:00
|
|
|
|
2012-07-30 15:50:00 -07:00
|
|
|
const float left = x;
|
2010-08-16 20:26:20 -07:00
|
|
|
float top = 0.0f;
|
2010-09-22 19:49:04 -07:00
|
|
|
|
2011-01-23 13:32:12 -08:00
|
|
|
int linesCount = 0;
|
|
|
|
if (flags & SkPaint::kUnderlineText_Flag) linesCount++;
|
|
|
|
if (flags & SkPaint::kStrikeThruText_Flag) linesCount++;
|
|
|
|
|
|
|
|
const int pointsCount = 4 * linesCount;
|
2010-09-22 19:49:04 -07:00
|
|
|
float points[pointsCount];
|
|
|
|
int currentPoint = 0;
|
2010-08-16 20:26:20 -07:00
|
|
|
|
|
|
|
if (flags & SkPaint::kUnderlineText_Flag) {
|
|
|
|
top = y + textSize * kStdUnderline_Offset;
|
2010-09-22 19:49:04 -07:00
|
|
|
points[currentPoint++] = left;
|
|
|
|
points[currentPoint++] = top;
|
|
|
|
points[currentPoint++] = left + underlineWidth;
|
|
|
|
points[currentPoint++] = top;
|
2010-08-16 20:26:20 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (flags & SkPaint::kStrikeThruText_Flag) {
|
|
|
|
top = y + textSize * kStdStrikeThru_Offset;
|
2010-09-22 19:49:04 -07:00
|
|
|
points[currentPoint++] = left;
|
|
|
|
points[currentPoint++] = top;
|
|
|
|
points[currentPoint++] = left + underlineWidth;
|
|
|
|
points[currentPoint++] = top;
|
2010-08-16 20:26:20 -07:00
|
|
|
}
|
2010-09-22 19:49:04 -07:00
|
|
|
|
2011-06-01 14:52:00 -07:00
|
|
|
paintCopy.setStrokeWidth(strokeWidth);
|
2010-09-22 19:49:04 -07:00
|
|
|
|
2011-06-01 14:52:00 -07:00
|
|
|
drawLines(&points[0], pointsCount, &paintCopy);
|
2010-08-16 20:26:20 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-01-04 19:05:13 -08:00
|
|
|
status_t OpenGLRenderer::drawRects(const float* rects, int count, SkPaint* paint) {
|
|
|
|
if (mSnapshot->isIgnored()) {
|
|
|
|
return DrawGlInfo::kStatusDone;
|
|
|
|
}
|
|
|
|
|
2012-12-03 12:34:51 -08:00
|
|
|
int color = paint->getColor();
|
|
|
|
// If a shader is set, preserve only the alpha
|
2013-02-04 16:16:33 -08:00
|
|
|
if (mDrawModifiers.mShader) {
|
2012-12-03 12:34:51 -08:00
|
|
|
color |= 0x00ffffff;
|
|
|
|
}
|
|
|
|
SkXfermode::Mode mode = getXfermode(paint->getXfermode());
|
|
|
|
|
|
|
|
return drawColorRects(rects, count, color, mode);
|
|
|
|
}
|
|
|
|
|
|
|
|
status_t OpenGLRenderer::drawColorRects(const float* rects, int count, int color,
|
2013-02-06 16:51:04 -08:00
|
|
|
SkXfermode::Mode mode, bool ignoreTransform, bool dirty, bool clip) {
|
2013-03-05 10:27:35 -08:00
|
|
|
if (count == 0) {
|
|
|
|
return DrawGlInfo::kStatusDone;
|
|
|
|
}
|
2012-12-03 12:34:51 -08:00
|
|
|
|
2013-01-04 19:05:13 -08:00
|
|
|
float left = FLT_MAX;
|
|
|
|
float top = FLT_MAX;
|
|
|
|
float right = FLT_MIN;
|
|
|
|
float bottom = FLT_MIN;
|
|
|
|
|
|
|
|
int vertexCount = 0;
|
|
|
|
Vertex mesh[count * 6];
|
|
|
|
Vertex* vertex = mesh;
|
|
|
|
|
2012-11-26 18:30:17 -08:00
|
|
|
for (int index = 0; index < count; index += 4) {
|
2013-01-04 19:05:13 -08:00
|
|
|
float l = rects[index + 0];
|
|
|
|
float t = rects[index + 1];
|
|
|
|
float r = rects[index + 2];
|
|
|
|
float b = rects[index + 3];
|
|
|
|
|
2013-03-05 10:27:35 -08:00
|
|
|
Vertex::set(vertex++, l, b);
|
|
|
|
Vertex::set(vertex++, l, t);
|
|
|
|
Vertex::set(vertex++, r, t);
|
|
|
|
Vertex::set(vertex++, l, b);
|
|
|
|
Vertex::set(vertex++, r, t);
|
|
|
|
Vertex::set(vertex++, r, b);
|
2013-01-04 19:05:13 -08:00
|
|
|
|
2013-03-05 10:27:35 -08:00
|
|
|
vertexCount += 6;
|
2013-01-04 19:05:13 -08:00
|
|
|
|
2013-03-05 10:27:35 -08:00
|
|
|
left = fminf(left, l);
|
|
|
|
top = fminf(top, t);
|
|
|
|
right = fmaxf(right, r);
|
|
|
|
bottom = fmaxf(bottom, b);
|
2013-01-04 19:05:13 -08:00
|
|
|
}
|
|
|
|
|
2013-03-05 10:27:35 -08:00
|
|
|
if (clip && quickReject(left, top, right, bottom)) {
|
2013-02-04 13:50:16 -08:00
|
|
|
return DrawGlInfo::kStatusDone;
|
|
|
|
}
|
2013-01-04 19:05:13 -08:00
|
|
|
|
|
|
|
setupDraw();
|
|
|
|
setupDrawNoTexture();
|
|
|
|
setupDrawColor(color, ((color >> 24) & 0xFF) * mSnapshot->alpha);
|
|
|
|
setupDrawShader();
|
|
|
|
setupDrawColorFilter();
|
|
|
|
setupDrawBlending(mode);
|
|
|
|
setupDrawProgram();
|
|
|
|
setupDrawDirtyRegionsDisabled();
|
2012-12-03 12:34:51 -08:00
|
|
|
setupDrawModelView(0.0f, 0.0f, 1.0f, 1.0f, ignoreTransform, true);
|
2013-01-04 19:05:13 -08:00
|
|
|
setupDrawColorUniforms();
|
|
|
|
setupDrawShaderUniforms();
|
|
|
|
setupDrawColorFilterUniforms();
|
|
|
|
setupDrawVertices((GLvoid*) &mesh[0].position[0]);
|
|
|
|
|
2013-01-15 18:51:42 -08:00
|
|
|
if (dirty && hasLayer()) {
|
2013-03-05 10:27:35 -08:00
|
|
|
dirtyLayer(left, top, right, bottom, currentTransform());
|
2013-01-04 19:05:13 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
glDrawArrays(GL_TRIANGLES, 0, vertexCount);
|
|
|
|
|
|
|
|
return DrawGlInfo::kStatusDrew;
|
|
|
|
}
|
|
|
|
|
2010-06-28 17:12:22 -07:00
|
|
|
void OpenGLRenderer::drawColorRect(float left, float top, float right, float bottom,
|
2010-09-13 18:00:09 -07:00
|
|
|
int color, SkXfermode::Mode mode, bool ignoreTransform) {
|
2010-07-14 19:18:51 -07:00
|
|
|
// If a shader is set, preserve only the alpha
|
2013-02-04 16:16:33 -08:00
|
|
|
if (mDrawModifiers.mShader) {
|
2010-07-14 19:18:51 -07:00
|
|
|
color |= 0x00ffffff;
|
|
|
|
}
|
|
|
|
|
2010-12-13 18:24:33 -08:00
|
|
|
setupDraw();
|
2011-12-13 13:11:32 -08:00
|
|
|
setupDrawNoTexture();
|
2012-07-13 12:13:07 -07:00
|
|
|
setupDrawColor(color, ((color >> 24) & 0xFF) * mSnapshot->alpha);
|
2010-12-13 18:24:33 -08:00
|
|
|
setupDrawShader();
|
|
|
|
setupDrawColorFilter();
|
|
|
|
setupDrawBlending(mode);
|
|
|
|
setupDrawProgram();
|
|
|
|
setupDrawModelView(left, top, right, bottom, ignoreTransform);
|
|
|
|
setupDrawColorUniforms();
|
|
|
|
setupDrawShaderUniforms(ignoreTransform);
|
|
|
|
setupDrawColorFilterUniforms();
|
|
|
|
setupDrawSimpleMesh();
|
2010-09-17 15:31:32 -07:00
|
|
|
|
|
|
|
glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount);
|
|
|
|
}
|
|
|
|
|
2010-07-09 13:25:56 -07:00
|
|
|
void OpenGLRenderer::drawTextureRect(float left, float top, float right, float bottom,
|
2010-10-25 18:03:28 -07:00
|
|
|
Texture* texture, SkPaint* paint) {
|
2010-07-09 13:25:56 -07:00
|
|
|
int alpha;
|
|
|
|
SkXfermode::Mode mode;
|
|
|
|
getAlphaAndMode(paint, &alpha, &mode);
|
|
|
|
|
2011-11-30 20:21:23 -08:00
|
|
|
texture->setWrap(GL_CLAMP_TO_EDGE, true);
|
2010-10-25 18:03:28 -07:00
|
|
|
|
Pack preloaded framework assets in a texture atlas
When the Android runtime starts, the system preloads a series of assets
in the Zygote process. These assets are shared across all processes.
Unfortunately, each one of these assets is later uploaded in its own
OpenGL texture, once per process. This wastes memory and generates
unnecessary OpenGL state changes.
This CL introduces an asset server that provides an atlas to all processes.
Note: bitmaps used by skia shaders are *not* sampled from the atlas.
It's an uncommon use case and would require extra texture transforms
in the GL shaders.
WHAT IS THE ASSETS ATLAS
The "assets atlas" is a single, shareable graphic buffer that contains
all the system's preloaded bitmap drawables (this includes 9-patches.)
The atlas is made of two distinct objects: the graphic buffer that
contains the actual pixels and the map which indicates where each
preloaded bitmap can be found in the atlas (essentially a pair of
x and y coordinates.)
HOW IS THE ASSETS ATLAS GENERATED
Because we need to support a wide variety of devices and because it
is easy to change the list of preloaded drawables, the atlas is
generated at runtime, during the startup phase of the system process.
There are several steps that lead to the atlas generation:
1. If the device is booting for the first time, or if the device was
updated, we need to find the best atlas configuration. To do so,
the atlas service tries a number of width, height and algorithm
variations that allows us to pack as many assets as possible while
using as little memory as possible. Once a best configuration is found,
it gets written to disk in /data/system/framework_atlas
2. Given a best configuration (algorithm variant, dimensions and
number of bitmaps that can be packed in the atlas), the atlas service
packs all the preloaded bitmaps into a single graphic buffer object.
3. The packing is done using Skia in a temporary native bitmap. The
Skia bitmap is then copied into the graphic buffer using OpenGL ES
to benefit from texture swizzling.
HOW PROCESSES USE THE ATLAS
Whenever a process' hardware renderer initializes its EGL context,
it queries the atlas service for the graphic buffer and the map.
It is important to remember that both the context and the map will
be valid for the lifetime of the hardware renderer (if the system
process goes down, all apps get killed as well.)
Every time the hardware renderer needs to render a bitmap, it first
checks whether the bitmap can be found in the assets atlas. When
the bitmap is part of the atlas, texture coordinates are remapped
appropriately before rendering.
Change-Id: I8eaecf53e7f6a33d90da3d0047c5ceec89ea3af0
2013-04-17 18:54:38 -07:00
|
|
|
GLvoid* vertices = (GLvoid*) NULL;
|
|
|
|
GLvoid* texCoords = (GLvoid*) gMeshTextureOffset;
|
|
|
|
|
|
|
|
if (texture->uvMapper) {
|
|
|
|
vertices = &mMeshVertices[0].position[0];
|
|
|
|
texCoords = &mMeshVertices[0].texture[0];
|
|
|
|
|
|
|
|
Rect uvs(0.0f, 0.0f, 1.0f, 1.0f);
|
|
|
|
texture->uvMapper->map(uvs);
|
|
|
|
|
|
|
|
resetDrawTextureTexCoords(uvs.left, uvs.top, uvs.right, uvs.bottom);
|
|
|
|
}
|
|
|
|
|
2013-03-05 10:27:35 -08:00
|
|
|
if (CC_LIKELY(currentTransform().isPureTranslate())) {
|
|
|
|
const float x = (int) floorf(left + currentTransform().getTranslateX() + 0.5f);
|
|
|
|
const float y = (int) floorf(top + currentTransform().getTranslateY() + 0.5f);
|
2010-12-06 18:07:02 -08:00
|
|
|
|
2011-11-30 20:21:23 -08:00
|
|
|
texture->setFilter(GL_NEAREST, true);
|
2010-12-06 18:07:02 -08:00
|
|
|
drawTextureMesh(x, y, x + texture->width, y + texture->height, texture->id,
|
Pack preloaded framework assets in a texture atlas
When the Android runtime starts, the system preloads a series of assets
in the Zygote process. These assets are shared across all processes.
Unfortunately, each one of these assets is later uploaded in its own
OpenGL texture, once per process. This wastes memory and generates
unnecessary OpenGL state changes.
This CL introduces an asset server that provides an atlas to all processes.
Note: bitmaps used by skia shaders are *not* sampled from the atlas.
It's an uncommon use case and would require extra texture transforms
in the GL shaders.
WHAT IS THE ASSETS ATLAS
The "assets atlas" is a single, shareable graphic buffer that contains
all the system's preloaded bitmap drawables (this includes 9-patches.)
The atlas is made of two distinct objects: the graphic buffer that
contains the actual pixels and the map which indicates where each
preloaded bitmap can be found in the atlas (essentially a pair of
x and y coordinates.)
HOW IS THE ASSETS ATLAS GENERATED
Because we need to support a wide variety of devices and because it
is easy to change the list of preloaded drawables, the atlas is
generated at runtime, during the startup phase of the system process.
There are several steps that lead to the atlas generation:
1. If the device is booting for the first time, or if the device was
updated, we need to find the best atlas configuration. To do so,
the atlas service tries a number of width, height and algorithm
variations that allows us to pack as many assets as possible while
using as little memory as possible. Once a best configuration is found,
it gets written to disk in /data/system/framework_atlas
2. Given a best configuration (algorithm variant, dimensions and
number of bitmaps that can be packed in the atlas), the atlas service
packs all the preloaded bitmaps into a single graphic buffer object.
3. The packing is done using Skia in a temporary native bitmap. The
Skia bitmap is then copied into the graphic buffer using OpenGL ES
to benefit from texture swizzling.
HOW PROCESSES USE THE ATLAS
Whenever a process' hardware renderer initializes its EGL context,
it queries the atlas service for the graphic buffer and the map.
It is important to remember that both the context and the map will
be valid for the lifetime of the hardware renderer (if the system
process goes down, all apps get killed as well.)
Every time the hardware renderer needs to render a bitmap, it first
checks whether the bitmap can be found in the assets atlas. When
the bitmap is part of the atlas, texture coordinates are remapped
appropriately before rendering.
Change-Id: I8eaecf53e7f6a33d90da3d0047c5ceec89ea3af0
2013-04-17 18:54:38 -07:00
|
|
|
alpha / 255.0f, mode, texture->blend, vertices, texCoords,
|
|
|
|
GL_TRIANGLE_STRIP, gMeshCount, false, true);
|
2010-12-06 18:07:02 -08:00
|
|
|
} else {
|
2011-11-30 20:21:23 -08:00
|
|
|
texture->setFilter(FILTER(paint), true);
|
2010-12-06 18:07:02 -08:00
|
|
|
drawTextureMesh(left, top, right, bottom, texture->id, alpha / 255.0f, mode,
|
Pack preloaded framework assets in a texture atlas
When the Android runtime starts, the system preloads a series of assets
in the Zygote process. These assets are shared across all processes.
Unfortunately, each one of these assets is later uploaded in its own
OpenGL texture, once per process. This wastes memory and generates
unnecessary OpenGL state changes.
This CL introduces an asset server that provides an atlas to all processes.
Note: bitmaps used by skia shaders are *not* sampled from the atlas.
It's an uncommon use case and would require extra texture transforms
in the GL shaders.
WHAT IS THE ASSETS ATLAS
The "assets atlas" is a single, shareable graphic buffer that contains
all the system's preloaded bitmap drawables (this includes 9-patches.)
The atlas is made of two distinct objects: the graphic buffer that
contains the actual pixels and the map which indicates where each
preloaded bitmap can be found in the atlas (essentially a pair of
x and y coordinates.)
HOW IS THE ASSETS ATLAS GENERATED
Because we need to support a wide variety of devices and because it
is easy to change the list of preloaded drawables, the atlas is
generated at runtime, during the startup phase of the system process.
There are several steps that lead to the atlas generation:
1. If the device is booting for the first time, or if the device was
updated, we need to find the best atlas configuration. To do so,
the atlas service tries a number of width, height and algorithm
variations that allows us to pack as many assets as possible while
using as little memory as possible. Once a best configuration is found,
it gets written to disk in /data/system/framework_atlas
2. Given a best configuration (algorithm variant, dimensions and
number of bitmaps that can be packed in the atlas), the atlas service
packs all the preloaded bitmaps into a single graphic buffer object.
3. The packing is done using Skia in a temporary native bitmap. The
Skia bitmap is then copied into the graphic buffer using OpenGL ES
to benefit from texture swizzling.
HOW PROCESSES USE THE ATLAS
Whenever a process' hardware renderer initializes its EGL context,
it queries the atlas service for the graphic buffer and the map.
It is important to remember that both the context and the map will
be valid for the lifetime of the hardware renderer (if the system
process goes down, all apps get killed as well.)
Every time the hardware renderer needs to render a bitmap, it first
checks whether the bitmap can be found in the assets atlas. When
the bitmap is part of the atlas, texture coordinates are remapped
appropriately before rendering.
Change-Id: I8eaecf53e7f6a33d90da3d0047c5ceec89ea3af0
2013-04-17 18:54:38 -07:00
|
|
|
texture->blend, vertices, texCoords, GL_TRIANGLE_STRIP, gMeshCount);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (texture->uvMapper) {
|
|
|
|
resetDrawTextureTexCoords(0.0f, 0.0f, 1.0f, 1.0f);
|
2010-12-06 18:07:02 -08:00
|
|
|
}
|
2010-06-22 13:11:24 -07:00
|
|
|
}
|
|
|
|
|
2010-06-26 00:13:53 -07:00
|
|
|
void OpenGLRenderer::drawTextureRect(float left, float top, float right, float bottom,
|
2010-07-13 11:37:54 -07:00
|
|
|
GLuint texture, float alpha, SkXfermode::Mode mode, bool blend) {
|
|
|
|
drawTextureMesh(left, top, right, bottom, texture, alpha, mode, blend,
|
2010-10-18 14:06:08 -07:00
|
|
|
(GLvoid*) NULL, (GLvoid*) gMeshTextureOffset, GL_TRIANGLE_STRIP, gMeshCount);
|
2010-07-08 19:17:03 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void OpenGLRenderer::drawTextureMesh(float left, float top, float right, float bottom,
|
2010-07-13 11:37:54 -07:00
|
|
|
GLuint texture, float alpha, SkXfermode::Mode mode, bool blend,
|
2010-09-15 18:11:50 -07:00
|
|
|
GLvoid* vertices, GLvoid* texCoords, GLenum drawMode, GLsizei elementsCount,
|
2010-10-27 18:57:51 -07:00
|
|
|
bool swapSrcDst, bool ignoreTransform, GLuint vbo, bool ignoreScale, bool dirty) {
|
2010-09-12 13:02:16 -07:00
|
|
|
|
2010-12-13 18:24:33 -08:00
|
|
|
setupDraw();
|
|
|
|
setupDrawWithTexture();
|
|
|
|
setupDrawColor(alpha, alpha, alpha, alpha);
|
|
|
|
setupDrawColorFilter();
|
|
|
|
setupDrawBlending(blend, mode, swapSrcDst);
|
|
|
|
setupDrawProgram();
|
2013-01-04 12:26:18 -08:00
|
|
|
if (!dirty) setupDrawDirtyRegionsDisabled();
|
2010-10-27 18:57:51 -07:00
|
|
|
if (!ignoreScale) {
|
2010-12-13 18:24:33 -08:00
|
|
|
setupDrawModelView(left, top, right, bottom, ignoreTransform);
|
2010-10-18 14:06:08 -07:00
|
|
|
} else {
|
2010-12-13 18:24:33 -08:00
|
|
|
setupDrawModelViewTranslate(left, top, right, bottom, ignoreTransform);
|
2010-08-02 18:50:22 -07:00
|
|
|
}
|
2013-01-04 12:26:18 -08:00
|
|
|
setupDrawTexture(texture);
|
2010-12-14 15:55:39 -08:00
|
|
|
setupDrawPureColorUniforms();
|
2010-12-13 18:24:33 -08:00
|
|
|
setupDrawColorFilterUniforms();
|
|
|
|
setupDrawMesh(vertices, texCoords, vbo);
|
2010-08-02 18:50:22 -07:00
|
|
|
|
2010-09-15 18:11:50 -07:00
|
|
|
glDrawArrays(drawMode, 0, elementsCount);
|
2010-12-13 18:24:33 -08:00
|
|
|
|
|
|
|
finishDrawTexture();
|
2010-07-09 13:25:56 -07:00
|
|
|
}
|
|
|
|
|
Pack preloaded framework assets in a texture atlas
When the Android runtime starts, the system preloads a series of assets
in the Zygote process. These assets are shared across all processes.
Unfortunately, each one of these assets is later uploaded in its own
OpenGL texture, once per process. This wastes memory and generates
unnecessary OpenGL state changes.
This CL introduces an asset server that provides an atlas to all processes.
Note: bitmaps used by skia shaders are *not* sampled from the atlas.
It's an uncommon use case and would require extra texture transforms
in the GL shaders.
WHAT IS THE ASSETS ATLAS
The "assets atlas" is a single, shareable graphic buffer that contains
all the system's preloaded bitmap drawables (this includes 9-patches.)
The atlas is made of two distinct objects: the graphic buffer that
contains the actual pixels and the map which indicates where each
preloaded bitmap can be found in the atlas (essentially a pair of
x and y coordinates.)
HOW IS THE ASSETS ATLAS GENERATED
Because we need to support a wide variety of devices and because it
is easy to change the list of preloaded drawables, the atlas is
generated at runtime, during the startup phase of the system process.
There are several steps that lead to the atlas generation:
1. If the device is booting for the first time, or if the device was
updated, we need to find the best atlas configuration. To do so,
the atlas service tries a number of width, height and algorithm
variations that allows us to pack as many assets as possible while
using as little memory as possible. Once a best configuration is found,
it gets written to disk in /data/system/framework_atlas
2. Given a best configuration (algorithm variant, dimensions and
number of bitmaps that can be packed in the atlas), the atlas service
packs all the preloaded bitmaps into a single graphic buffer object.
3. The packing is done using Skia in a temporary native bitmap. The
Skia bitmap is then copied into the graphic buffer using OpenGL ES
to benefit from texture swizzling.
HOW PROCESSES USE THE ATLAS
Whenever a process' hardware renderer initializes its EGL context,
it queries the atlas service for the graphic buffer and the map.
It is important to remember that both the context and the map will
be valid for the lifetime of the hardware renderer (if the system
process goes down, all apps get killed as well.)
Every time the hardware renderer needs to render a bitmap, it first
checks whether the bitmap can be found in the assets atlas. When
the bitmap is part of the atlas, texture coordinates are remapped
appropriately before rendering.
Change-Id: I8eaecf53e7f6a33d90da3d0047c5ceec89ea3af0
2013-04-17 18:54:38 -07:00
|
|
|
void OpenGLRenderer::drawIndexedTextureMesh(float left, float top, float right, float bottom,
|
|
|
|
GLuint texture, float alpha, SkXfermode::Mode mode, bool blend,
|
|
|
|
GLvoid* vertices, GLvoid* texCoords, GLenum drawMode, GLsizei elementsCount,
|
|
|
|
bool swapSrcDst, bool ignoreTransform, GLuint vbo, bool ignoreScale, bool dirty) {
|
|
|
|
|
|
|
|
setupDraw();
|
|
|
|
setupDrawWithTexture();
|
|
|
|
setupDrawColor(alpha, alpha, alpha, alpha);
|
|
|
|
setupDrawColorFilter();
|
|
|
|
setupDrawBlending(blend, mode, swapSrcDst);
|
|
|
|
setupDrawProgram();
|
|
|
|
if (!dirty) setupDrawDirtyRegionsDisabled();
|
|
|
|
if (!ignoreScale) {
|
|
|
|
setupDrawModelView(left, top, right, bottom, ignoreTransform);
|
|
|
|
} else {
|
|
|
|
setupDrawModelViewTranslate(left, top, right, bottom, ignoreTransform);
|
|
|
|
}
|
|
|
|
setupDrawTexture(texture);
|
|
|
|
setupDrawPureColorUniforms();
|
|
|
|
setupDrawColorFilterUniforms();
|
|
|
|
setupDrawMeshIndices(vertices, texCoords, vbo);
|
|
|
|
|
|
|
|
glDrawElements(drawMode, elementsCount, GL_UNSIGNED_SHORT, NULL);
|
|
|
|
|
|
|
|
finishDrawTexture();
|
|
|
|
}
|
|
|
|
|
2013-01-04 12:26:18 -08:00
|
|
|
void OpenGLRenderer::drawAlpha8TextureMesh(float left, float top, float right, float bottom,
|
|
|
|
GLuint texture, bool hasColor, int color, int alpha, SkXfermode::Mode mode,
|
|
|
|
GLvoid* vertices, GLvoid* texCoords, GLenum drawMode, GLsizei elementsCount,
|
2013-03-04 10:19:31 -08:00
|
|
|
bool ignoreTransform, bool ignoreScale, bool dirty) {
|
2013-01-04 12:26:18 -08:00
|
|
|
|
|
|
|
setupDraw();
|
|
|
|
setupDrawWithTexture(true);
|
|
|
|
if (hasColor) {
|
|
|
|
setupDrawAlpha8Color(color, alpha);
|
|
|
|
}
|
|
|
|
setupDrawColorFilter();
|
|
|
|
setupDrawShader();
|
|
|
|
setupDrawBlending(true, mode);
|
|
|
|
setupDrawProgram();
|
|
|
|
if (!dirty) setupDrawDirtyRegionsDisabled();
|
2013-03-04 10:19:31 -08:00
|
|
|
if (!ignoreScale) {
|
|
|
|
setupDrawModelView(left, top, right, bottom, ignoreTransform);
|
|
|
|
} else {
|
|
|
|
setupDrawModelViewTranslate(left, top, right, bottom, ignoreTransform);
|
|
|
|
}
|
2013-01-04 12:26:18 -08:00
|
|
|
setupDrawTexture(texture);
|
|
|
|
setupDrawPureColorUniforms();
|
|
|
|
setupDrawColorFilterUniforms();
|
|
|
|
setupDrawShaderUniforms();
|
|
|
|
setupDrawMesh(vertices, texCoords);
|
|
|
|
|
|
|
|
glDrawArrays(drawMode, 0, elementsCount);
|
|
|
|
|
|
|
|
finishDrawTexture();
|
|
|
|
}
|
|
|
|
|
2010-09-09 14:42:43 -07:00
|
|
|
void OpenGLRenderer::chooseBlending(bool blend, SkXfermode::Mode mode,
|
2010-09-10 19:20:06 -07:00
|
|
|
ProgramDescription& description, bool swapSrcDst) {
|
2013-05-03 14:24:16 -07:00
|
|
|
if (mCountOverdraw) {
|
|
|
|
if (!mCaches.blend) glEnable(GL_BLEND);
|
|
|
|
if (mCaches.lastSrcMode != GL_ONE || mCaches.lastDstMode != GL_ONE) {
|
|
|
|
glBlendFunc(GL_ONE, GL_ONE);
|
|
|
|
}
|
|
|
|
|
|
|
|
mCaches.blend = true;
|
|
|
|
mCaches.lastSrcMode = GL_ONE;
|
|
|
|
mCaches.lastDstMode = GL_ONE;
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2010-07-09 13:25:56 -07:00
|
|
|
blend = blend || mode != SkXfermode::kSrcOver_Mode;
|
2012-04-25 20:02:53 -07:00
|
|
|
|
2010-07-09 13:25:56 -07:00
|
|
|
if (blend) {
|
2012-01-03 14:13:39 -08:00
|
|
|
// These blend modes are not supported by OpenGL directly and have
|
|
|
|
// to be implemented using shaders. Since the shader will perform
|
|
|
|
// the blending, turn blending off here
|
|
|
|
// If the blend mode cannot be implemented using shaders, fall
|
|
|
|
// back to the default SrcOver blend mode instead
|
2012-08-07 19:09:57 -07:00
|
|
|
if (CC_UNLIKELY(mode > SkXfermode::kScreen_Mode)) {
|
2013-02-06 16:51:04 -08:00
|
|
|
if (CC_UNLIKELY(mExtensions.hasFramebufferFetch())) {
|
2010-09-09 14:42:43 -07:00
|
|
|
description.framebufferMode = mode;
|
2010-09-10 19:20:06 -07:00
|
|
|
description.swapSrcDst = swapSrcDst;
|
2010-09-09 14:42:43 -07:00
|
|
|
|
2012-01-03 14:13:39 -08:00
|
|
|
if (mCaches.blend) {
|
|
|
|
glDisable(GL_BLEND);
|
|
|
|
mCaches.blend = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
} else {
|
|
|
|
mode = SkXfermode::kSrcOver_Mode;
|
2010-09-09 14:42:43 -07:00
|
|
|
}
|
2012-01-03 14:13:39 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!mCaches.blend) {
|
|
|
|
glEnable(GL_BLEND);
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
mCaches.lastSrcMode = sourceMode;
|
|
|
|
mCaches.lastDstMode = destMode;
|
2010-07-09 13:25:56 -07:00
|
|
|
}
|
2010-08-23 21:05:08 -07:00
|
|
|
} else if (mCaches.blend) {
|
2010-07-09 13:25:56 -07:00
|
|
|
glDisable(GL_BLEND);
|
|
|
|
}
|
2010-08-23 21:05:08 -07:00
|
|
|
mCaches.blend = blend;
|
2010-06-26 00:13:53 -07:00
|
|
|
}
|
|
|
|
|
2010-07-29 14:37:42 -07:00
|
|
|
bool OpenGLRenderer::useProgram(Program* program) {
|
2010-07-14 19:18:51 -07:00
|
|
|
if (!program->isInUse()) {
|
2010-08-23 21:05:08 -07:00
|
|
|
if (mCaches.currentProgram != NULL) mCaches.currentProgram->remove();
|
2010-07-14 19:18:51 -07:00
|
|
|
program->use();
|
2010-08-23 21:05:08 -07:00
|
|
|
mCaches.currentProgram = program;
|
2010-07-12 20:20:03 -07:00
|
|
|
return false;
|
2010-07-12 14:41:06 -07:00
|
|
|
}
|
2010-07-12 20:20:03 -07:00
|
|
|
return true;
|
2010-07-12 14:41:06 -07:00
|
|
|
}
|
|
|
|
|
2010-06-28 17:12:22 -07:00
|
|
|
void OpenGLRenderer::resetDrawTextureTexCoords(float u1, float v1, float u2, float v2) {
|
2010-07-27 17:39:27 -07:00
|
|
|
TextureVertex* v = &mMeshVertices[0];
|
2010-07-09 13:25:56 -07:00
|
|
|
TextureVertex::setUV(v++, u1, v1);
|
|
|
|
TextureVertex::setUV(v++, u2, v1);
|
|
|
|
TextureVertex::setUV(v++, u1, v2);
|
|
|
|
TextureVertex::setUV(v++, u2, v2);
|
2010-06-30 19:21:21 -07:00
|
|
|
}
|
|
|
|
|
2013-03-29 10:59:59 -07:00
|
|
|
void OpenGLRenderer::getAlphaAndMode(SkPaint* paint, int* alpha, SkXfermode::Mode* mode) const {
|
2012-07-16 12:41:17 -07:00
|
|
|
getAlphaAndModeDirect(paint, alpha, mode);
|
2013-03-29 10:59:59 -07:00
|
|
|
if (mDrawModifiers.mOverrideLayerAlpha < 1.0f) {
|
|
|
|
// if drawing a layer, ignore the paint's alpha
|
2013-05-03 17:42:27 -07:00
|
|
|
*alpha = mDrawModifiers.mOverrideLayerAlpha * 255;
|
2013-03-29 10:59:59 -07:00
|
|
|
}
|
2012-03-21 18:54:18 -07:00
|
|
|
*alpha *= mSnapshot->alpha;
|
2010-06-28 17:12:22 -07:00
|
|
|
}
|
|
|
|
|
2013-03-29 10:59:59 -07:00
|
|
|
float OpenGLRenderer::getLayerAlpha(Layer* layer) const {
|
|
|
|
float alpha;
|
|
|
|
if (mDrawModifiers.mOverrideLayerAlpha < 1.0f) {
|
|
|
|
alpha = mDrawModifiers.mOverrideLayerAlpha;
|
|
|
|
} else {
|
|
|
|
alpha = layer->getAlpha() / 255.0f;
|
|
|
|
}
|
|
|
|
return alpha * mSnapshot->alpha;
|
|
|
|
}
|
|
|
|
|
2010-06-24 19:30:36 -07:00
|
|
|
}; // namespace uirenderer
|
2010-06-16 18:44:05 -07:00
|
|
|
}; // namespace android
|