/* * Copyright (C) 2016 eschao <esc.chao@gmail.com> * * 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. */ package com.eschao.android.widget.pageflip; import android.graphics.Bitmap; import android.graphics.Color; import android.graphics.PointF; import android.opengl.GLUtils; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.FloatBuffer; import static android.opengl.GLES20.GL_FLOAT; import static android.opengl.GLES20.GL_LINEAR; import static android.opengl.GLES20.GL_TEXTURE0; import static android.opengl.GLES20.GL_TEXTURE_2D; import static android.opengl.GLES20.GL_TEXTURE_MAG_FILTER; import static android.opengl.GLES20.GL_TEXTURE_MIN_FILTER; import static android.opengl.GLES20.GL_TRIANGLE_FAN; import static android.opengl.GLES20.GL_TRIANGLE_STRIP; import static android.opengl.GLES20.glActiveTexture; import static android.opengl.GLES20.glBindTexture; import static android.opengl.GLES20.glDeleteTextures; import static android.opengl.GLES20.glDrawArrays; import static android.opengl.GLES20.glEnableVertexAttribArray; import static android.opengl.GLES20.glGenTextures; import static android.opengl.GLES20.glTexParameterf; import static android.opengl.GLES20.glUniform1i; import static android.opengl.GLES20.glUniformMatrix4fv; import static android.opengl.GLES20.glVertexAttribPointer; /** * Page class * <p> * Page holds content textures and show them on screen. In single page mode, a * page represents the whole screen area. But in double pages mode, there are * two pages to depict the entire screen size, in the left part is called left * page and the right part is called right page. * Every page has the below properties: * </p> * <ul> * <li>Page size: left/right/top/bottom and width/height</li> * <li>Holding 3 content textures for drawing: * <ul> * <li>The first texture: which is showing on screen when page is * stationary, we can relatively call it as the first 'Page' at * some extend</li> * <li>The second texture: normally it can be called the second * 'Page' against the first texture. It will be appeared when page * is flipping or flip is over, in the later, the second texture * will eventually become the first one</li> * <li>The back texture: in single page mode, the back texture is * always same with the first texture, thus, the caller shouldn't * set it before drawing. But in double pages mode, it should be * set with a different texture and can be called the second 'Page' * , at this time, the second texture will be called the third * 'Page' as like we're reading a book</li> * <li>Every texture should be set with a bitmap by outside caller * </li> * </ul> * </li> * </ul> * * @author eschao */ public class Page { private final static int TEXTURE_SIZE = 3; private final static int FIRST_TEXTURE_ID = 0; private final static int SECOND_TEXTURE_ID = 1; private final static int BACK_TEXTURE_ID = 2; private final static int INVALID_TEXTURE_ID = -1; /** * <p> * 4 apexes of page has different permutation order according to original * point since original point will be changed when user click to curl page * from different direction. There are 4 kinds of order: * </p><pre> * A B C D * 2 1 3 0 0 3 1 2 * +----+ +----+ +----+ +----+ * | | | | | | | | * +----+ +----+ +----+ +----+ * 3 0 2 1 1 2 0 3 * From A From A From A * 0 <-> 1 0 <-> 2 0 <-> 3 * 3 <-> 2 3 <-> 1 1 <-> 2 * </pre> * <ul> * <li>0 always represents the origin point, accordingly 2 is diagonal * point</li> * <li>Case A is default order: 0 -> 1 -> 2 -> 3</li> * <li>Every apex data is stored in mApexes following the case A order * and never changed</li> * <li>This array is mapping apex order (case A - D) to real apex data * stored in mApexes. For example: * <ul> * <li>Case A has same order with storing sequence of apex data in * mApexes</li> * <li>Case B: the 0 apex is stored in 1 position in mApexes</li> * </ul></li> * </ul> */ private final static int[][] mPageApexOrders = new int[][] { new int[] {0, 1, 2, 3}, // for case A new int[] {1, 0, 3, 2}, // for case B new int[] {2, 3, 0, 1}, // for case C new int[] {3, 2, 1, 0}, // for case D }; /** * <p>When page is curled, there are 4 kinds of vertexes orders for drawing * first texture and second texture with TRIANGLE_STRIP way</p><pre> * A B C D * 2 1 2 X 1 2 X 1 2 1 * +-------+ +-----.-+ +-.-----+ +-------+ * | | | F / | |/ | | F | * | F .Y | / | Y. S | X.-------.Y * | /| | / | | | | S | * +-----.-+ +-.-----+ +-------+ +-------+ * 3 X 0 3 Y 0 3 0 3 0 * </pre> * <ul> * <li>All cases are based on the apex order case A(0 -> 1 -> 2 -> 3) * </li> * <li>F means the first texture area, S means the second texture area * </li> * <li>X is xFoldX point, Y is yFoldY point</li> * <li>Case A means: xFoldX and yFoldY are both in page</li> * <li>Case B means: xFoldX is in page, but yFoldY is the intersecting * point with line 1->2 since yFoldY is outside the page</li> * <li>Case C means: xFoldX and yFoldY are both outside the page</li> * <li>Case D means: xFoldX outside page but yFoldY is in the page</li> * <li>Combining {@link #mPageApexOrders} with this array, we can get * the right apex data from mApexes array which will help us quickly * organizing triangle data for openGL drawing</li> * <li>The last array(Case E) in this array means: xFoldX and yFoldY * are both outside the page and the whole page will be draw with * second texture</li> * </ul> */ private final static int[][] mFoldVexOrders = new int[][] { new int[] {4, 3, 1, 2, 0}, // Case A new int[] {3, 3, 2, 0, 1}, // Case B new int[] {3, 2, 1, 3, 0}, // Case C new int[] {2, 2, 3, 1, 0}, // Case D new int[] {1, 0, 1, 3, 2}, // Case E }; // page size float left; float right; float top; float bottom; float width; float height; // texture size for rendering page, normally they are same with page width // and height float texWidth; float texHeight; /** * <p>origin point and diagonal point</p> * <pre> * 0-----+ * | | * | | * +-----1 * </pre> * <p>if origin(x, y) is 1, the diagonal(x, y) is 0</p> */ GLPoint originP; GLPoint diagonalP; private GLPoint mXFoldP; private GLPoint mYFoldP; // vertexes and texture coordinates buffer for full page private FloatBuffer mFullPageVexBuf; private FloatBuffer mFullPageTexCoordsBuf; // storing 4 apexes data of page private float[] mApexes; // texture coordinates for page apex private float[] mApexTexCoords; // vertex size of front of fold page and unfold page private int mFrontVertexSize; // index of apex order array for current original point private int mApexOrderIndex; // mask color of back texture float[][] maskColor; // texture(front, back and second) ids allocated by OpenGL private int[] mTexIDs; // unused texture ids, will be deleted when next OpenGL drawing private int[] mUnusedTexIDs; // actual size of mUnusedTexIDs private int mUnusedTexSize; /** * Constructor */ public Page() { init(0, 0, 0, 0); } /** * Constructor with page size */ public Page(float l, float r, float t, float b) { init(l, r, t, b); } private void init(float l, float r, float t, float b) { top = t; left = l; right = r; bottom = b; width = right - left; height = top - bottom; texWidth = width; texHeight = height; mFrontVertexSize = 0; mApexOrderIndex = 0; mXFoldP = new GLPoint(); mYFoldP = new GLPoint(); originP = new GLPoint(); diagonalP = new GLPoint(); maskColor = new float[][] {new float[] {0, 0, 0}, new float[] {0, 0, 0}, new float[] {0, 0, 0}}; mTexIDs = new int[] {INVALID_TEXTURE_ID, INVALID_TEXTURE_ID, INVALID_TEXTURE_ID}; mUnusedTexSize = 0; mUnusedTexIDs = new int[] {INVALID_TEXTURE_ID, INVALID_TEXTURE_ID, INVALID_TEXTURE_ID}; createVertexesBuffer(); buildVertexesOfFullPage(); } /** * Is the left page? * <p>Left page represents the left screen in double pages mode</p> * * @return true if current page is left page */ public boolean isLeftPage() { return right <= 0; } /** * Is the right page? * <p>Right page represents the right screen in double pages mode</p> * * @return true if current page is right page */ public boolean isRightPage() { return left >= 0; } /** * Get page width * * @return page width */ public float width() { return width; } /** * Gets page height * * @return page height */ public float height() { return height; } /** * Is the first texture set? * * @return true if the first texture is set */ public boolean isFirstTextureSet() { return mTexIDs[FIRST_TEXTURE_ID] != INVALID_TEXTURE_ID; } /** * Is the second texture set ? * * @return true if the second texture is set */ public boolean isSecondTextureSet() { return mTexIDs[SECOND_TEXTURE_ID] != INVALID_TEXTURE_ID; } /** * Is the back texture set ? * * @return true if the back texture is set */ public boolean isBackTextureSet() { return mTexIDs[BACK_TEXTURE_ID] != INVALID_TEXTURE_ID; } /** * Deletes unused texture ids * <p>It should be called in OpenGL thread</p> */ public void deleteUnusedTextures() { if (mUnusedTexSize > 0) { glDeleteTextures(mUnusedTexSize, mUnusedTexIDs, 0); mUnusedTexSize = 0; } } /** * Recycle the first texture id and set it with the second texture * <p>Manually call this function to set the first texture with the second * one after page forward flipped over in single page mode.</p> * * @return self */ public Page setFirstTextureWithSecond() { if (mTexIDs[FIRST_TEXTURE_ID] > INVALID_TEXTURE_ID) { mUnusedTexIDs[mUnusedTexSize++] = mTexIDs[FIRST_TEXTURE_ID]; } maskColor[FIRST_TEXTURE_ID][0] = maskColor[SECOND_TEXTURE_ID][0]; maskColor[FIRST_TEXTURE_ID][1] = maskColor[SECOND_TEXTURE_ID][1]; maskColor[FIRST_TEXTURE_ID][2] = maskColor[SECOND_TEXTURE_ID][2]; mTexIDs[FIRST_TEXTURE_ID] = mTexIDs[SECOND_TEXTURE_ID]; mTexIDs[SECOND_TEXTURE_ID] = INVALID_TEXTURE_ID; return this; } /** * Recycle the second texture id and set it with the first texture * <p>Manually call this function to set the second texture with the first * one when page is backward flipping in single page mode.</p> * * @return self */ public Page setSecondTextureWithFirst() { if (mTexIDs[SECOND_TEXTURE_ID] > INVALID_TEXTURE_ID) { mUnusedTexIDs[mUnusedTexSize++] = mTexIDs[SECOND_TEXTURE_ID]; } maskColor[SECOND_TEXTURE_ID][0] = maskColor[FIRST_TEXTURE_ID][0]; maskColor[SECOND_TEXTURE_ID][1] = maskColor[FIRST_TEXTURE_ID][1]; maskColor[SECOND_TEXTURE_ID][2] = maskColor[FIRST_TEXTURE_ID][2]; mTexIDs[SECOND_TEXTURE_ID] = mTexIDs[FIRST_TEXTURE_ID]; mTexIDs[FIRST_TEXTURE_ID] = INVALID_TEXTURE_ID; return this; } /** * Swap textures of two pages and recycle unused texture ids * <p>Call this function when page is flipped over in double pages mode</p> * * @param page another page * @return self */ public Page swapTexturesWithPage(Page page) { // [second page]: second -> first mUnusedTexIDs[mUnusedTexSize++] = mTexIDs[SECOND_TEXTURE_ID]; mTexIDs[SECOND_TEXTURE_ID] = mTexIDs[FIRST_TEXTURE_ID]; // [first page] first -> [second page] back of first mUnusedTexIDs[mUnusedTexSize++] = mTexIDs[BACK_TEXTURE_ID]; mTexIDs[BACK_TEXTURE_ID] = page.mTexIDs[FIRST_TEXTURE_ID]; // [first page] back of first -> [second page] first mTexIDs[FIRST_TEXTURE_ID] = page.mTexIDs[BACK_TEXTURE_ID]; page.mTexIDs[BACK_TEXTURE_ID] = INVALID_TEXTURE_ID; // [first page] second -> [first page] first page.mTexIDs[FIRST_TEXTURE_ID] = page.mTexIDs[SECOND_TEXTURE_ID]; page.mTexIDs[SECOND_TEXTURE_ID] = INVALID_TEXTURE_ID; return this; } /** * Get back texture ID * * @return back texture id, If it is not set, return the first texture id */ int getBackTextureID() { // In single page mode, the back texture is same with the first texture if (mTexIDs[BACK_TEXTURE_ID] == INVALID_TEXTURE_ID) { return mTexIDs[FIRST_TEXTURE_ID]; } else { return mTexIDs[BACK_TEXTURE_ID]; } } /** * Is given point(x, y) in page? * * @param x x coordinate * @param y y coordinate * @return true if the point is in page */ boolean contains(float x, float y) { return left < right && bottom < top && left <= x && x < right && bottom <= y && y < top; } /** * Is given x coordinate in specified page range? * * @param x x coordinate * @param ratio range ratio based on page width, start from OriginP.x * @return True if x is in specified range */ boolean isXInRange(float x, float ratio) { final float w = width * ratio; return originP.x < 0 ? x < (originP.x + w) : x > (originP.x - w); } /** * Is given x coordinate outside page width? * * @param x x coordinate * @return true if given x is not in page */ boolean isXOutsidePage(float x) { return originP.x < 0 ? x > diagonalP.x : x < diagonalP.x; } /** * Compute index of page apexes order for current original point */ private void computeIndexOfApexOrder() { mApexOrderIndex = 0; if (originP.x < right && originP.y < 0) { mApexOrderIndex = 3; } else { if (originP.y > 0) { mApexOrderIndex++; } if (originP.x < right) { mApexOrderIndex++; } } } /** * Set original point and diagonal point * * @param hasSecondPage has the second page in double pages mode? * @param dy relative finger movement on Y axis * @return self */ Page setOriginAndDiagonalPoints(boolean hasSecondPage, float dy) { if (hasSecondPage && left < 0) { originP.x = left; diagonalP.x = right; } else { originP.x = right; diagonalP.x = left; } if (dy > 0) { originP.y = bottom; diagonalP.y = top; } else { originP.y = top; diagonalP.y = bottom; } computeIndexOfApexOrder(); // set texture coordinates originP.texX = (originP.x - left) / texWidth; originP.texY = (top - originP.y) / texHeight; diagonalP.texX = (diagonalP.x - left) / texWidth; diagonalP.texY = (top - diagonalP.y) / texHeight; return this; } /** * Invert Y coordinate of original point and diagonal point */ void invertYOfOriginPoint() { float t = originP.y; originP.y = diagonalP.y; diagonalP.y = t; t = originP.texY; originP.texY = diagonalP.texY; diagonalP.texY = t; // re-compute index for apex order since original point is changed computeIndexOfApexOrder(); } /** * Compute X coordinate of texture * * @param x x coordinate * @return x coordinate of texture, value is in [0 .. 1] */ public float textureX(float x) { return (x - left) / texWidth; } /** * Compute Y coordinate of texture * * @param y y coordinate * @return y coordinate of texture, value is in [0 .. 1] */ public float textureY(float y) { return (top - y) / texHeight; } /** * Delete all textures */ public void deleteAllTextures() { glDeleteTextures(TEXTURE_SIZE, mTexIDs, 0); mTexIDs[FIRST_TEXTURE_ID] = INVALID_TEXTURE_ID; mTexIDs[SECOND_TEXTURE_ID] = INVALID_TEXTURE_ID; mTexIDs[BACK_TEXTURE_ID] = INVALID_TEXTURE_ID; } /** * Set the first texture with given bitmap * * @param b Bitmap object for creating texture */ public void setFirstTexture(Bitmap b) { // compute mask color int color = PageFlipUtils.computeAverageColor(b, 30); maskColor[FIRST_TEXTURE_ID][0] = Color.red(color) / 255.0f; maskColor[FIRST_TEXTURE_ID][1] = Color.green(color) / 255.0f; maskColor[FIRST_TEXTURE_ID][2] = Color.blue(color) / 255.0f; glGenTextures(1, mTexIDs, FIRST_TEXTURE_ID); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, mTexIDs[FIRST_TEXTURE_ID]); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); GLUtils.texImage2D(GL_TEXTURE_2D, 0, b, 0); } /** * Set the second texture with given bitmap * * @param b Bitmap object for creating texture */ public void setSecondTexture(Bitmap b) { // compute mask color int color = PageFlipUtils.computeAverageColor(b, 30); maskColor[SECOND_TEXTURE_ID][0] = Color.red(color) / 255.0f; maskColor[SECOND_TEXTURE_ID][1] = Color.green(color) / 255.0f; maskColor[SECOND_TEXTURE_ID][2] = Color.blue(color) / 255.0f; glGenTextures(1, mTexIDs, SECOND_TEXTURE_ID); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, mTexIDs[SECOND_TEXTURE_ID]); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); GLUtils.texImage2D(GL_TEXTURE_2D, 0, b, 0); } /** * Set the back texture with given bitmap * <p>If given bitmap is null, the back texture will be same with the first * texture</p> * * @param b Bitmap object for creating back texture */ public void setBackTexture(Bitmap b) { if (b == null) { // back texture is same with the first texture if (mTexIDs[BACK_TEXTURE_ID] != INVALID_TEXTURE_ID) { mUnusedTexIDs[mUnusedTexSize++] = mTexIDs[BACK_TEXTURE_ID]; } mTexIDs[BACK_TEXTURE_ID] = INVALID_TEXTURE_ID; } else { // compute mask color int color = PageFlipUtils.computeAverageColor(b, 50); maskColor[BACK_TEXTURE_ID][0] = Color.red(color) / 255.0f; maskColor[BACK_TEXTURE_ID][1] = Color.green(color) / 255.0f; maskColor[BACK_TEXTURE_ID][2] = Color.blue(color) / 255.0f; glGenTextures(1, mTexIDs, BACK_TEXTURE_ID); glBindTexture(GL_TEXTURE_2D, mTexIDs[BACK_TEXTURE_ID]); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); GLUtils.texImage2D(GL_TEXTURE_2D, 0, b, 0); } } /** * Draw front page when page is flipping * * @param program GL shader program * @param vertexes Vertexes of the curled front page */ public void drawFrontPage(VertexProgram program, Vertexes vertexes) { // 1. draw unfold part and curled part with the first texture glUniformMatrix4fv(program.mMVPMatrixLoc, 1, false, VertexProgram.MVPMatrix, 0); glBindTexture(GL_TEXTURE_2D, mTexIDs[FIRST_TEXTURE_ID]); glUniform1i(program.mTextureLoc, 0); vertexes.drawWith(GL_TRIANGLE_STRIP, program.mVertexPosLoc, program.mTexCoordLoc, 0, mFrontVertexSize); // 2. draw the second texture glBindTexture(GL_TEXTURE_2D, mTexIDs[SECOND_TEXTURE_ID]); glUniform1i(program.mTextureLoc, 0); glDrawArrays(GL_TRIANGLE_STRIP, mFrontVertexSize, vertexes.mVertexesSize - mFrontVertexSize); } /** * Draw full page * * @param program GL shader program * @param isFirst use the first or second texture to draw */ public void drawFullPage(VertexProgram program, boolean isFirst) { if (isFirst) { drawFullPage(program, mTexIDs[FIRST_TEXTURE_ID]); } else { drawFullPage(program, mTexIDs[SECOND_TEXTURE_ID]); } } /** * Draw full page with given texture id */ private void drawFullPage(VertexProgram program, int textureID) { glBindTexture(GL_TEXTURE_2D, textureID); glUniform1i(program.mTextureLoc, 0); glVertexAttribPointer(program.mVertexPosLoc, 3, GL_FLOAT, false, 0, mFullPageVexBuf); glEnableVertexAttribArray(program.mVertexPosLoc); glVertexAttribPointer(program.mTexCoordLoc, 2, GL_FLOAT, false, 0, mFullPageTexCoordsBuf); glEnableVertexAttribArray(program.mTexCoordLoc); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); } /** * Create vertexes buffer */ private void createVertexesBuffer() { // 4 vertexes for full page mFullPageVexBuf = ByteBuffer.allocateDirect(48) .order(ByteOrder.nativeOrder()) .asFloatBuffer(); mFullPageTexCoordsBuf = ByteBuffer.allocateDirect(32) .order(ByteOrder.nativeOrder()) .asFloatBuffer(); mApexes = new float[12]; mApexTexCoords = new float[8]; } /** * Build vertexes of page when page is flipping vertically * <pre> * <---- flip * 1 fY 2 * +--------#-----+ * | | | * | | | * | | | * +--------#-----+ * 4 fX 3 * </pre> * <p> * There is only one case to draw when page is flipping vertically * </p> * <ul> * <li>Page is flipping from right -> left</li> * <li>Origin point: 3</li> * <li>Diagonal point: 1</li> * <li>xFoldP1.y: fY, xFoldP2.x: fX</li> * <li>Drawing front part with the first texture(GL_TRIANGLE_STRIP): * fX -> fY -> 4 -> 1</li> * <li>Drawing back part with the second texture(GL_TRIANGLE_STRIP): * 3 -> 2 -> fX -> fY</li> * </ul> * * @param frontVertexes vertexes for drawing font part of page * @param xFoldP1 fold point on X axis */ public void buildVertexesOfPageWhenVertical(Vertexes frontVertexes, PointF xFoldP1) { // if xFoldX and yFoldY are both outside the page, use the last vertex // order to draw page int index = 4; // compute xFoldX and yFoldY points if (!isXOutsidePage(xFoldP1.x)) { // use the case B of vertex order to draw page index = 1; float cx = textureX(xFoldP1.x); mXFoldP.set(xFoldP1.x, originP.y, 0, cx, originP.texY); mYFoldP.set(xFoldP1.x, diagonalP.y, 0, cx, diagonalP.texY); } // get apex order and fold vertex order final int[] apexOrder = mPageApexOrders[mApexOrderIndex]; final int[] vexOrder = mFoldVexOrders[index]; // need to draw first texture, add xFoldX and yFoldY first. Remember // the adding order of vertex in float buffer is X point prior to Y // point if (vexOrder[0] > 1) { frontVertexes.addVertex(mXFoldP).addVertex(mYFoldP); } // add the leftover vertexes for the first texture for (int i = 1; i < vexOrder[0]; ++i) { int k = apexOrder[vexOrder[i]]; int m = k * 3; int n = k << 1; frontVertexes.addVertex(mApexes[m], mApexes[m + 1], 0, mApexTexCoords[n], mApexTexCoords[n + 1]); } // the vertex size for drawing front of fold page and first texture mFrontVertexSize = frontVertexes.mNext / 3; // if xFoldX and yFoldY are in the page, need add them for drawing the // second texture if (vexOrder[0] > 1) { mXFoldP.z = mYFoldP.z = -1; frontVertexes.addVertex(mXFoldP).addVertex(mYFoldP); } // add the remaining vertexes for the second texture for (int i = vexOrder[0]; i < vexOrder.length; ++i) { int k = apexOrder[vexOrder[i]]; int m = k * 3; int n = k << 1; frontVertexes.addVertex(mApexes[m], mApexes[m + 1], -1, mApexTexCoords[n], mApexTexCoords[n + 1]); } } /** * Build vertexes of page when page flip is slope * <p>See {@link #mApexOrderIndex} and {@link #mFoldVexOrders} to get more * details</p> * * @param frontVertexes vertexes for drawing front part of page * @param xFoldP1 fold point on X axis * @param yFoldP1 fold point on Y axis * @param kValue tan value of page curling angle */ public void buildVertexesOfPageWhenSlope(Vertexes frontVertexes, PointF xFoldP1, PointF yFoldP1, float kValue) { // compute xFoldX point float halfH = height * 0.5f; int index = 0; mXFoldP.set(xFoldP1.x, originP.y, 0, textureX(xFoldP1.x), originP.texY); if (isXOutsidePage(xFoldP1.x)) { index = 2; mXFoldP.x = diagonalP.x; mXFoldP.y = originP.y + (xFoldP1.x - diagonalP.x) / kValue; mXFoldP.texX = diagonalP.texX; mXFoldP.texY = textureY(mXFoldP.y); } // compute yFoldY point mYFoldP.set(originP.x, yFoldP1.y, 0, originP.texX, textureY(yFoldP1.y)); if (Math.abs(yFoldP1.y) > halfH) { index++; mYFoldP.x = originP.x + kValue * (yFoldP1.y - diagonalP.y); if (isXOutsidePage(mYFoldP.x)) { index++; } else { mYFoldP.y = diagonalP.y; mYFoldP.texX = textureX(mYFoldP.x); mYFoldP.texY = diagonalP.texY; } } // get apex order and fold vertex order final int[] apexOrder = mPageApexOrders[mApexOrderIndex]; final int[] vexOrder = mFoldVexOrders[index]; // need to draw first texture, add xFoldX and yFoldY first. Remember // the adding order of vertex in float buffer is X point prior to Y // point if (vexOrder[0] > 1) { frontVertexes.addVertex(mXFoldP).addVertex(mYFoldP); } // add the leftover vertexes for the first texture for (int i = 1; i < vexOrder[0]; ++i) { int k = apexOrder[vexOrder[i]]; int m = k * 3; int n = k << 1; frontVertexes.addVertex(mApexes[m], mApexes[m + 1], 0, mApexTexCoords[n], mApexTexCoords[n + 1]); } // the vertex size for drawing front of fold page and first texture mFrontVertexSize = frontVertexes.mNext / 3; // if xFoldX and yFoldY are in the page, need add them for drawing the // second texture if (vexOrder[0] > 1) { mXFoldP.z = mYFoldP.z = -1; frontVertexes.addVertex(mXFoldP).addVertex(mYFoldP); } // add the remaining vertexes for the second texture for (int i = vexOrder[0]; i < vexOrder.length; ++i) { int k = apexOrder[vexOrder[i]]; int m = k * 3; int n = k << 1; frontVertexes.addVertex(mApexes[m], mApexes[m + 1], -1, mApexTexCoords[n], mApexTexCoords[n + 1]); } } /** * Build vertexes of full page * <pre> * <---- flip * 3 2 * +--------------+ * | | * | | * | | * | | * +--------------+ * 4 1 * </pre> * <ul> * <li>Page is flipping from right -> left</li> * <li>Origin point: 3</li> * <li>Diagonal point: 1</li> * <li>xFoldP1.y: fY, xFoldP2.x: fX</li> * <li>Drawing order: 3 -> 2 -> 4 -> 1</li> * </ul> */ private void buildVertexesOfFullPage() { int i = 0; int j = 0; mApexes[i++] = right; mApexes[i++] = bottom; mApexes[i++] = 0; mApexTexCoords[j++] = textureX(right); mApexTexCoords[j++] = textureY(bottom); mApexes[i++] = right; mApexes[i++] = top; mApexes[i++] = 0; mApexTexCoords[j++] = textureX(right); mApexTexCoords[j++] = textureY(top); mApexes[i++] = left; mApexes[i++] = top; mApexes[i++] = 0; mApexTexCoords[j++] = textureX(left); mApexTexCoords[j++] = textureY(top); mApexes[i++] = left; mApexes[i++] = bottom; mApexes[i] = 0; mApexTexCoords[j++] = textureX(left); mApexTexCoords[j] = textureY(bottom); mFullPageVexBuf.put(mApexes, 0, 12).position(0); mFullPageTexCoordsBuf.put(mApexTexCoords, 0, 8).position(0); } }