package com.glview.hwui; import android.opengl.Matrix; import com.glview.App; import com.glview.graphics.Bitmap; import com.glview.graphics.Rect; import com.glview.graphics.RectF; import com.glview.graphics.drawable.ninepatch.NinePatch; import com.glview.graphics.mesh.BasicMesh; import com.glview.graphics.shader.BaseShader; import com.glview.graphics.shader.DefaultColorShader; import com.glview.graphics.shader.DefaultTextureShader; import com.glview.hwui.GLPaint.Style; import com.glview.hwui.font.FontRenderer; import com.glview.libgdx.graphics.Mesh; import com.glview.libgdx.graphics.VertexAttribute; import com.glview.libgdx.graphics.VertexAttributes.Usage; import com.glview.libgdx.graphics.glutils.ShaderProgram; import com.glview.libgdx.graphics.opengl.GL20; import com.glview.util.MatrixUtil; import com.glview.util.Utils; /** * This canvas use OpenGL ES 2.0 * * @author lijing.lj */ class GL20Canvas extends StatefullBaseCanvas implements InnerGLCanvas { private static final String TAG = "GL20Canvas"; private static final int OFFSET_FILL_RECT = 0; private static final int OFFSET_DRAW_LINE = 6; private static final int OFFSET_DRAW_RECT = 8; private static final int COUNT_DRAW_TXETURE = 6; private static final int COUNT_DRAW_LINE = 2; private static final int COUNT_DRAW_RECT = 4; private static final int COUNT_TOTAL = COUNT_DRAW_TXETURE + COUNT_DRAW_LINE + COUNT_DRAW_RECT; private final RectF mDrawTextureSourceRect = new RectF(); private final RectF mDrawTextureTargetRect = new RectF(); final float mFinalMatrix[] = new float[16]; boolean tmpRet; Mesh mMesh; float mVertices[]; Batch mBatch; FontRenderer mFontRenderer; ShaderManager mShaderManager; GL20 mGL; RenderState mRenderState; Caches mCaches; public GL20Canvas(RenderState renderState) { mCaches = Caches.getInstance(); mGL = App.getGL20(); mRenderState = renderState; initialize(); mShaderManager = new ShaderManager(); mBatch = new Batch(this); mFontRenderer = FontRenderer.instance(); initialMesh(); } private void initialMesh() { mMesh = new Mesh(false, 4, COUNT_TOTAL, new VertexAttribute( Usage.Position, 2, ShaderProgram.POSITION_ATTRIBUTE), // position // new VertexAttribute(Usage.ColorPacked, 4, // ShaderProgram.COLOR_ATTRIBUTE), new VertexAttribute(Usage.TextureCoordinates, 2, ShaderProgram.TEXCOORD_ATTRIBUTE));// texture position initialVertices(); mMesh.setVertices(mVertices); setIndex(); } private void setIndex() { short indices[] = new short[COUNT_TOTAL]; indices[0] = (short) (0); indices[1] = (short) (1); indices[2] = (short) (2); indices[3] = (short) (1); indices[4] = (short) (3); indices[5] = (short) (2); indices[6] = (short) (0); indices[7] = (short) (3); indices[8] = (short) (0); indices[9] = (short) (1); indices[10] = (short) (3); indices[11] = (short) (2); mMesh.setIndices(indices); } private void setVerticesXY(float left, float top, float right, float bottom) { // 1 mVertices[0] = left; mVertices[1] = top; // 2 mVertices[4] = left; mVertices[5] = bottom; // 3 mVertices[8] = right; mVertices[9] = top; // 4 mVertices[12] = right; mVertices[13] = bottom; } private void setVerticesUV(float left, float top, float right, float bottom) { // 1 mVertices[2] = left; mVertices[3] = top; // 2 mVertices[6] = left; mVertices[7] = bottom; // 3 mVertices[10] = right; mVertices[11] = top; // 4 mVertices[14] = right; mVertices[15] = bottom; } private void initialVertices() { mVertices = new float[4 * 4]; setVerticesXY(0, 0, 1, 1); setVerticesUV(0, 0, 1, 1); } public void setSize(int width, int height) { super.setSize(width, height); Utils.assertTrue(width >= 0 && height >= 0); // ////////////////////////// // gl10--->gl20 start // 设置视窗大小及位置 mRenderState.setViewport(width, height); setCameraAndProject(0, 0); final float matrix[] = currentSnapshot().transform; Matrix.setIdentityM(matrix, 0); Matrix.translateM(matrix, 0, 0, height, 0); Matrix.scaleM(matrix, 0, 1, -1, 1); // 裁剪平面 //App.getGL20().glEnable(GL20.GL_CULL_FACE); //App.getGL20().glCullFace(GL20.GL_BACK); } private void setCameraAndProject(float centerX, float centerY) { float near = 1; float camera = 500; float far = 5000; // 调用此方法产生摄像机9参数位置矩阵 setCamera(centerX, centerY, camera, centerX, centerY, 0f, 0f, 1.0f, 0.0f); setProjectFrustum(- centerX / camera, mWidth / camera - centerX / camera, - centerY / camera, mHeight / camera - centerY / camera, near, far); } @Override public void translate(float x, float y) { flushBatch(); super.translate(x, y); } @Override public void translate(float x, float y, float z) { flushBatch(); if (z != 0) { float[] center = MatrixUtil.mapPoint(currentSnapshot().transform, 0, 0); setCameraAndProject(center[0], center[1]); mRenderState.setDepthEnabled(true); } super.translate(x, y, z); } @Override public void rotate(float degrees, float x, float y, float z) { if (degrees == 0) return; flushBatch(); if (x != 0 || y != 0) { float[] center = MatrixUtil.mapPoint(currentSnapshot().transform, 0, 0); setCameraAndProject(center[0], center[1]); mRenderState.setDepthEnabled(true); } super.rotate(degrees, x, y, z); } @Override public void rotate(float degrees) { flushBatch(); super.rotate(degrees); } @Override public void scale(float sx, float sy, float sz) { flushBatch(); super.scale(sx, sy, sz); } @Override public void restore() { flushBatch(); super.restore(); } @Override public void restoreToCount(int saveCount) { flushBatch(); super.restoreToCount(saveCount); } private void initialize() { } @Override public void beginFrame() { setupDraw(); mGL.glClearColor(0.0f, 0.0f, 0.0f, 0.0f); mGL.glClearDepthf(1f); mGL.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT); mFontRenderer.setGLCanvas(this); mRenderState.setDepthEnabled(false); mRenderState.setBlendEnabled(false); dirtyClip(); Caches.getInstance().disableScissor(); // mRenderState.setBlendEnabled(true); } @Override public void endFrame() { flushBatch(); flushFont(); mFontRenderer.end(this); mFontRenderer.setGLCanvas(null); } private void setupDraw() { if (mDirtyClip) { if (mCaches.scissorEnabled) { setScissorFromClip(); } } } @Override public void drawRect(float left, float top, float right, float bottom, GLPaint paint) { paint = getGLPaint(paint); if (paint.getStyle() == Style.FILL) { fillRect(left, top, right, bottom, paint); return; } int color = paint.getColor(); mRenderState.setLineWidth(paint.getStrokeWidth()); setVerticesXY(left, top, right, bottom); drawColor(color, GL20.GL_LINE_LOOP, OFFSET_DRAW_RECT, COUNT_DRAW_RECT, paint); } private GLPaint getGLPaint(GLPaint paint) { if (paint != null) return paint; return mRenderState.getDefaultPaint(); } public void drawLine(float x1, float y1, float x2, float y2, GLPaint paint) { paint = getGLPaint(paint); int color = paint.getColor(); mRenderState.setLineWidth(paint.getStrokeWidth()); setVerticesXY(x1, y1, x2, y2); drawColor(color, GL20.GL_LINE_STRIP, OFFSET_DRAW_LINE, COUNT_DRAW_LINE, paint); } private void fillRect(float left, float top, float right, float bottom, GLPaint paint) { int color = paint.getColor(); setVerticesXY(left, top, right, bottom); drawColor(color, GL20.GL_TRIANGLES, OFFSET_FILL_RECT, COUNT_DRAW_TXETURE, paint); } private void drawColor(int color, int mode, int offset, int count, GLPaint paint) { setupDraw(); flushBatch(); // If a shader is set, preserve only the alpha if (paint.getShader() != null) { color |= 0x00ffffff; } float alpha = currentSnapshot().alpha * paint.getAlpha() / 255; float prealpha = ((color >>> 24) & 0xFF) * alpha / 255; float colorR = Math.round(((color >> 16) & 0xFF) * prealpha) * 1.0f / 255; float colorG = Math.round(((color >> 8) & 0xFF) * prealpha) * 1.0f / 255; float colorB = Math.round((color & 0xFF) * prealpha) * 1.0f / 255; float colorA = Math.round(255 * prealpha) * 1.0f / 255; BaseShader useShader = mShaderManager.setupColorShader(colorR, colorG, colorB, colorA, paint, false); mMesh.setVertices(mVertices); mMesh.render(useShader.getShaderProgram(), mode, offset, count, true); mRenderState.setDepthMask(true); } private void textureRect(float x, float y, float width, float height, boolean hasAlpha, GLPaint paint) { setupDraw(); flushBatch(); setVerticesXY(x, y, x + width, y + height); mMesh.setVertices(mVertices); BaseShader useShader = mShaderManager.setupTextureShader(paint, x, y, width, height, hasAlpha, true, false); mMesh.render(useShader.getShaderProgram(), GL20.GL_TRIANGLES, OFFSET_FILL_RECT, COUNT_DRAW_TXETURE, true); mRenderState.setDepthMask(true); } @Override public void drawTexture(Texture texture, float x, float y, float width, float height, GLPaint paint) { drawTexture(texture, x, y, width, height, true, paint); } public void drawTexture(Texture texture, float x, float y, float width, float height, boolean hasAlpha, GLPaint paint) { if (texture != null && texture.mId > 0) { mCaches.bindTexture(texture); setVerticesUV(0, 0, 1, 1); textureRect(x, y, width, height, hasAlpha, getGLPaint(paint)); } } @Override public void drawBitmap(Bitmap bitmap, float x, float y, GLPaint paint) { Texture texture = mCaches.textureCache.get(bitmap); if (texture == null) return; drawTexture(texture, x, y, bitmap.getWidth(), bitmap.getHeight(), bitmap.hasAlpha(), paint); } @Override public void drawBitmap(Bitmap bitmap, RectF source, RectF target, GLPaint paint) { paint = getGLPaint(paint); Texture texture = mCaches.textureCache.get(bitmap); if (texture == null) return; // Copy the input to avoid changing it. if (source != null && !source.isEmpty()) { mDrawTextureSourceRect.set(source); } else { mDrawTextureSourceRect.set(0, 0, bitmap.getWidth(), bitmap.getHeight()); } mDrawTextureTargetRect.set(target); mCaches.bindTexture(texture); convertCoordinate(mDrawTextureSourceRect, texture); setVerticesUV(mDrawTextureSourceRect.left, mDrawTextureSourceRect.top, mDrawTextureSourceRect.right, mDrawTextureSourceRect.bottom); textureRect(mDrawTextureTargetRect.left, mDrawTextureTargetRect.top, mDrawTextureTargetRect.width(), mDrawTextureTargetRect.height(), bitmap.hasAlpha(), paint); } @Override public void drawBitmap(Bitmap bitmap, Rect source, Rect target, GLPaint paint) { paint = getGLPaint(paint); Texture texture = mCaches.textureCache.get(bitmap); if (texture == null) return; // Copy the input to avoid changing it. if (source != null && !source.isEmpty()) { mDrawTextureSourceRect.set(source); } else { mDrawTextureSourceRect.set(0, 0, bitmap.getWidth(), bitmap.getHeight()); } mDrawTextureTargetRect.set(target); mCaches.bindTexture(texture); convertCoordinate(mDrawTextureSourceRect, texture); setVerticesUV(mDrawTextureSourceRect.left, mDrawTextureSourceRect.top, mDrawTextureSourceRect.right, mDrawTextureSourceRect.bottom); textureRect(mDrawTextureTargetRect.left, mDrawTextureTargetRect.top, mDrawTextureTargetRect.width(), mDrawTextureTargetRect.height(), bitmap.hasAlpha(), paint); } @Override public void drawBitmapBatch(Bitmap bitmap, Rect source, Rect target, GLPaint paint) { paint = getGLPaint(paint); if (source == null || source.isEmpty()) { mBatch.drawBitmap(bitmap, target.left, target.top, target.width(), target.height(), 0, 0, bitmap.getWidth(), bitmap.getHeight(), currentSnapshot().alpha, paint); } else { mBatch.drawBitmap(bitmap, target.left, target.top, target.width(), target.height(), source.left, source.top, source.width(), source.height(), currentSnapshot().alpha, paint); } } @Override public void applyMatrix(BaseShader shader, float[] transform) { // 将最终变换矩阵传入shader程序 float[] m = getFinalMatrix(mFinalMatrix, transform == null ? currentSnapshot().transform : transform); shader.setupViewModelMatrices(m); } // This function changes the source coordinate to the texture coordinates. // It also clips the source and target coordinates if it is beyond the // bound of the texture. private static void convertCoordinate(RectF source, Texture texture) { int width = texture.getWidth(); int height = texture.getHeight(); float invTexWidth = 1.0f / width; float invTexHeight = 1.0f / height; // Convert to texture coordinates source.left *= invTexWidth; source.right *= invTexWidth; source.top *= invTexHeight; source.bottom *= invTexHeight; } @Override public void drawPatch(NinePatch patch, Rect rect, GLPaint paint) { paint = getGLPaint(paint); Texture texture = mCaches.textureCache.get(patch.getBitmap()); if (texture == null) return; Mesh mesh = mCaches.patchCache.get(rect.width(), rect.height(), patch); if (mesh == null) return; setupDraw(); flushBatch(); mCaches.bindTexture(texture); translate(rect.left, rect.top); BaseShader useShader = mShaderManager.setupTextureShader(paint, 0, 0, texture.getWidth(), texture.getHeight(), patch.getBitmap().hasAlpha(), true, false); mesh.render(useShader.getShaderProgram(), GL20.GL_TRIANGLE_STRIP, 0, mesh.getNumIndices(), true); mRenderState.setDepthMask(true); // 恢复现状 translate(-rect.left, -rect.top); } @Override public void drawMesh(BasicMesh basicMesh, GLPaint paint) { paint = getGLPaint(paint); Mesh mesh = mCaches.meshCache.get(basicMesh); if (mesh == null) return; setupDraw(); flushBatch(); mRenderState.setLineWidth(paint.getStrokeWidth()); int color = paint.getColor(); float alpha = currentSnapshot().alpha * paint.getAlpha() / 255; float prealpha = ((color >>> 24) & 0xFF) * alpha / 255; float colorR = Math.round(((color >> 16) & 0xFF) * prealpha) * 1.0f / 255; float colorG = Math.round(((color >> 8) & 0xFF) * prealpha) * 1.0f / 255; float colorB = Math.round((color & 0xFF) * prealpha) * 1.0f / 255; float colorA = Math.round(255 * prealpha) * 1.0f / 255; BaseShader useShader = mShaderManager.setupColorShader(colorR, colorG, colorB, colorA, paint, basicMesh.hasColorAttr()); if (paint.getStyle() == Style.FILL) { mesh.render(useShader.getShaderProgram(), basicMesh.getDrawMode()); } else { mesh.render(useShader.getShaderProgram(), GL20.GL_LINE_LOOP); } mRenderState.setDepthMask(true); } @Override public void drawBitmapMesh(Bitmap bitmap, BasicMesh basicMesh, GLPaint paint) { paint = getGLPaint(paint); Texture texture = mCaches.textureCache.get(bitmap); if (texture == null) return; Mesh mesh = mCaches.meshCache.get(basicMesh); if (mesh == null) return; setupDraw(); flushBatch(); mCaches.bindTexture(texture); BaseShader useShader = mShaderManager.setupTextureShader(paint, 0, 0, texture.getWidth(), texture.getHeight(), bitmap.hasAlpha(), basicMesh.hasTexCoordsAttr(), basicMesh.hasColorAttr()); mesh.render(useShader.getShaderProgram(), basicMesh.getDrawMode()); mRenderState.setDepthMask(true); } public void setTextureTarget(int target) { mRenderState.setTextureTarget(target); } @Override public void drawText(CharSequence text, int start, int end, float x, float y, GLPaint paint, boolean drawDefer) { setupDraw(); mFontRenderer.renderText(this, text, start, end, x, y, currentSnapshot().alpha, getGLPaint(paint), currentSnapshot().clipRect, currentSnapshot().transform, !drawDefer); } class ShaderManager { private DefaultTextureShader mDefaultTextureShader; private DefaultTextureShader mTextureCoordsShader; private DefaultTextureShader mColorAttrTextureShader; private DefaultTextureShader mColorAttrTextureCoordsShader; private DefaultColorShader mDefaultColorShader; private DefaultColorShader mColorAttrShader; public ShaderManager() { mDefaultTextureShader = new DefaultTextureShader(); mDefaultTextureShader.setHasColorAttr(false); mDefaultTextureShader.setHasTexcoordsAttr(false); mTextureCoordsShader = new DefaultTextureShader(); mTextureCoordsShader.setHasColorAttr(false); mTextureCoordsShader.setHasTexcoordsAttr(true); mColorAttrTextureShader = new DefaultTextureShader(); mColorAttrTextureShader.setHasColorAttr(true); mColorAttrTextureShader.setHasTexcoordsAttr(false); mColorAttrTextureCoordsShader = new DefaultTextureShader(); mColorAttrTextureCoordsShader.setHasColorAttr(true); mColorAttrTextureCoordsShader.setHasTexcoordsAttr(true); mDefaultColorShader = new DefaultColorShader(); mDefaultColorShader.setHasColorAttr(false); mColorAttrShader = new DefaultColorShader(); mColorAttrShader.setHasColorAttr(true); } public BaseShader getDefaultTextureShader(boolean texCoords, boolean colorAttr) { if (texCoords) { if (colorAttr) { return mColorAttrTextureCoordsShader; } else { return mTextureCoordsShader; } } else { if (colorAttr) { return mColorAttrTextureShader; } else { return mDefaultTextureShader; } } } public BaseShader getDefaultColorShader(boolean colorAttr) { if (colorAttr) { return mColorAttrShader; } else { return mDefaultColorShader; } } public BaseShader setupTextureShader(GLPaint paint, float x, float y, float width, float height, boolean hasAlpha, boolean texCoords, boolean colorAttr) { BaseShader useShader = paint.getShader(); float alpha = currentSnapshot().alpha * paint.getAlpha() / 255; int color = paint.getColor(); if (useShader == null) { useShader = getDefaultTextureShader(texCoords, colorAttr); } useShader.setHasTexture(true); mCaches.useProgram(useShader.getShaderProgram()); // 设置alpha值 float prealpha = ((color >>> 24) & 0xFF) * alpha / 255; float colorR = Math.round(((color >> 16) & 0xFF) * prealpha) * 1.0f / 255; float colorG = Math.round(((color >> 8) & 0xFF) * prealpha) * 1.0f / 255; float colorB = Math.round((color & 0xFF) * prealpha) * 1.0f / 255; float colorA = Math.round(255 * prealpha) * 1.0f / 255; if (hasAlpha) { mRenderState.setBlendEnabled(true); } else { mRenderState.setColorMode(colorA); } useShader.setupColor(colorR, colorG, colorB, colorA); useShader.setupTextureCoords(x, y, width, height); // 将最终变换矩阵传入shader程序 float[] m = getFinalMatrix(mFinalMatrix, currentSnapshot().transform); useShader.setupViewModelMatrices(m); useShader.setupCustomValues(); return useShader; } public BaseShader setupColorShader(float r, float g, float b, float a, GLPaint paint, boolean colorAttr) { BaseShader useShader = paint.getShader(); if (useShader == null) { useShader = getDefaultColorShader(colorAttr); } useShader.setHasTexture(false); mCaches.useProgram(useShader.getShaderProgram()); mRenderState.setColorMode(a); useShader.setupColor(r, g, b, a); // 将最终变换矩阵传入shader程序 float[] m = getFinalMatrix(mFinalMatrix, currentSnapshot().transform); useShader.setupViewModelMatrices(m); useShader.setupCustomValues(); return useShader; } } protected void onSnapshotRestored(Snapshot removed, Snapshot restore) { super.onSnapshotRestored(removed, restore); boolean restoreViewport = (removed.flags & Snapshot.kFlagIsFboLayer) != 0; boolean restoreClip = (removed.flags & Snapshot.kFlagClipSet) != 0; if (restoreViewport) { mRenderState.setViewport(getWidth(), getHeight()); } if (restoreClip) { flushFont(); dirtyClip(); } }; /////////////////////////////////////////////////////////////////////////////// //Clipping /////////////////////////////////////////////////////////////////////////////// void setScissorFromClip() { Rect clip = currentClipRect(); if (mCaches.setScissor(clip.left, clip.top, clip.width(), clip.height())) { } mDirtyClip = false; } @Override public void clipRect(float left, float top, float right, float bottom) { flushBatch(); flushFont(); super.clipRect(left, top, right, bottom); if (mDirtyClip) { Caches.getInstance().enableScissor(); } } private void flushBatch() { mBatch.flush(); } private void flushFont() { mFontRenderer.flushBatch(); } }