pull/1794/head
			
			
		
		
							parent
							
								
									9235244abe
								
							
						
					
					
						commit
						15ba7bda89
					
				@ -1,957 +0,0 @@ | 
				
			||||
package io.legado.app.ui.book.read.page.delegate.curl; | 
				
			||||
 | 
				
			||||
import android.graphics.Bitmap; | 
				
			||||
import android.graphics.Color; | 
				
			||||
import android.graphics.PointF; | 
				
			||||
import android.graphics.RectF; | 
				
			||||
import android.opengl.GLUtils; | 
				
			||||
 | 
				
			||||
import java.nio.ByteBuffer; | 
				
			||||
import java.nio.ByteOrder; | 
				
			||||
import java.nio.FloatBuffer; | 
				
			||||
 | 
				
			||||
import javax.microedition.khronos.opengles.GL10; | 
				
			||||
 | 
				
			||||
/** | 
				
			||||
 * Class implementing actual curl/page rendering. | 
				
			||||
 * | 
				
			||||
 * @author harism | 
				
			||||
 */ | 
				
			||||
public class CurlMesh { | 
				
			||||
 | 
				
			||||
    // Flag for rendering some lines used for developing. Shows
 | 
				
			||||
    // curl position and one for the direction from the
 | 
				
			||||
    // position given. Comes handy once playing around with different
 | 
				
			||||
    // ways for following pointer.
 | 
				
			||||
    private static final boolean DRAW_CURL_POSITION = false; | 
				
			||||
    // Flag for drawing polygon outlines. Using this flag crashes on emulator
 | 
				
			||||
    // due to reason unknown to me. Leaving it here anyway as seeing polygon
 | 
				
			||||
    // outlines gives good insight how original rectangle is divided.
 | 
				
			||||
    private static final boolean DRAW_POLYGON_OUTLINES = false; | 
				
			||||
    // Flag for enabling shadow rendering.
 | 
				
			||||
    private static final boolean DRAW_SHADOW = true; | 
				
			||||
    // Flag for texture rendering. While this is likely something you
 | 
				
			||||
    // don't want to do it's been used for development purposes as texture
 | 
				
			||||
    // rendering is rather slow on emulator.
 | 
				
			||||
    private static final boolean DRAW_TEXTURE = true; | 
				
			||||
 | 
				
			||||
    // Colors for shadow. Inner one is the color drawn next to surface where
 | 
				
			||||
    // shadowed area starts and outer one is color shadow ends to.
 | 
				
			||||
    private static final float[] SHADOW_INNER_COLOR = {0f, 0f, 0f, .5f}; | 
				
			||||
    private static final float[] SHADOW_OUTER_COLOR = {0f, 0f, 0f, .0f}; | 
				
			||||
 | 
				
			||||
    // Let's avoid using 'new' as much as possible. Meaning we introduce arrays
 | 
				
			||||
    // once here and reuse them on runtime. Doesn't really have very much effect
 | 
				
			||||
    // but avoids some garbage collections from happening.
 | 
				
			||||
    private final Array<ShadowVertex> mArrDropShadowVertices; | 
				
			||||
    private final Array<Vertex> mArrIntersections; | 
				
			||||
    private final Array<Vertex> mArrOutputVertices; | 
				
			||||
    private final Array<Vertex> mArrRotatedVertices; | 
				
			||||
    private final Array<Double> mArrScanLines; | 
				
			||||
    private final Array<ShadowVertex> mArrSelfShadowVertices; | 
				
			||||
    private final Array<ShadowVertex> mArrTempShadowVertices; | 
				
			||||
    private final Array<Vertex> mArrTempVertices; | 
				
			||||
 | 
				
			||||
    // Buffers for feeding rasterizer.
 | 
				
			||||
    private final FloatBuffer mBufColors; | 
				
			||||
    private FloatBuffer mBufCurlPositionLines; | 
				
			||||
    private final FloatBuffer mBufShadowColors; | 
				
			||||
    private final FloatBuffer mBufShadowVertices; | 
				
			||||
    private final FloatBuffer mBufTexCoords; | 
				
			||||
    private final FloatBuffer mBufVertices; | 
				
			||||
 | 
				
			||||
    private int mCurlPositionLinesCount; | 
				
			||||
    private int mDropShadowCount; | 
				
			||||
 | 
				
			||||
    // Boolean for 'flipping' texture sideways.
 | 
				
			||||
    private boolean mFlipTexture = false; | 
				
			||||
    // Maximum number of split lines used for creating a curl.
 | 
				
			||||
    private final int mMaxCurlSplits; | 
				
			||||
 | 
				
			||||
    // Bounding rectangle for this mesh. mRectagle[0] = top-left corner,
 | 
				
			||||
    // mRectangle[1] = bottom-left, mRectangle[2] = top-right and mRectangle[3]
 | 
				
			||||
    // bottom-right.
 | 
				
			||||
    private final Vertex[] mRectangle = new Vertex[4]; | 
				
			||||
    private int mSelfShadowCount; | 
				
			||||
 | 
				
			||||
    private boolean mTextureBack = false; | 
				
			||||
    // Texture ids and other variables.
 | 
				
			||||
    private int[] mTextureIds = null; | 
				
			||||
    private final CurlPage mTexturePage = new CurlPage(); | 
				
			||||
    private final RectF mTextureRectBack = new RectF(); | 
				
			||||
    private final RectF mTextureRectFront = new RectF(); | 
				
			||||
 | 
				
			||||
    private int mVerticesCountBack; | 
				
			||||
    private int mVerticesCountFront; | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * Constructor for mesh object. | 
				
			||||
     * | 
				
			||||
     * @param maxCurlSplits Maximum number curl can be divided into. The bigger the value | 
				
			||||
     *                      the smoother curl will be. With the cost of having more | 
				
			||||
     *                      polygons for drawing. | 
				
			||||
     */ | 
				
			||||
    public CurlMesh(int maxCurlSplits) { | 
				
			||||
        // There really is no use for 0 splits.
 | 
				
			||||
        mMaxCurlSplits = maxCurlSplits < 1 ? 1 : maxCurlSplits; | 
				
			||||
 | 
				
			||||
        mArrScanLines = new Array<Double>(maxCurlSplits + 2); | 
				
			||||
        mArrOutputVertices = new Array<Vertex>(7); | 
				
			||||
        mArrRotatedVertices = new Array<Vertex>(4); | 
				
			||||
        mArrIntersections = new Array<Vertex>(2); | 
				
			||||
        mArrTempVertices = new Array<Vertex>(7 + 4); | 
				
			||||
        for (int i = 0; i < 7 + 4; ++i) { | 
				
			||||
            mArrTempVertices.add(new Vertex()); | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        if (DRAW_SHADOW) { | 
				
			||||
            mArrSelfShadowVertices = new Array<ShadowVertex>( | 
				
			||||
                    (mMaxCurlSplits + 2) * 2); | 
				
			||||
            mArrDropShadowVertices = new Array<ShadowVertex>( | 
				
			||||
                    (mMaxCurlSplits + 2) * 2); | 
				
			||||
            mArrTempShadowVertices = new Array<ShadowVertex>( | 
				
			||||
                    (mMaxCurlSplits + 2) * 2); | 
				
			||||
            for (int i = 0; i < (mMaxCurlSplits + 2) * 2; ++i) { | 
				
			||||
                mArrTempShadowVertices.add(new ShadowVertex()); | 
				
			||||
            } | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        // Rectangle consists of 4 vertices. Index 0 = top-left, index 1 =
 | 
				
			||||
        // bottom-left, index 2 = top-right and index 3 = bottom-right.
 | 
				
			||||
        for (int i = 0; i < 4; ++i) { | 
				
			||||
            mRectangle[i] = new Vertex(); | 
				
			||||
        } | 
				
			||||
        // Set up shadow penumbra direction to each vertex. We do fake 'self
 | 
				
			||||
        // shadow' calculations based on this information.
 | 
				
			||||
        mRectangle[0].mPenumbraX = mRectangle[1].mPenumbraX = mRectangle[1].mPenumbraY = mRectangle[3].mPenumbraY = -1; | 
				
			||||
        mRectangle[0].mPenumbraY = mRectangle[2].mPenumbraX = mRectangle[2].mPenumbraY = mRectangle[3].mPenumbraX = 1; | 
				
			||||
 | 
				
			||||
        if (DRAW_CURL_POSITION) { | 
				
			||||
            mCurlPositionLinesCount = 3; | 
				
			||||
            ByteBuffer hvbb = ByteBuffer | 
				
			||||
                    .allocateDirect(mCurlPositionLinesCount * 2 * 2 * 4); | 
				
			||||
            hvbb.order(ByteOrder.nativeOrder()); | 
				
			||||
            mBufCurlPositionLines = hvbb.asFloatBuffer(); | 
				
			||||
            mBufCurlPositionLines.position(0); | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        // There are 4 vertices from bounding rect, max 2 from adding split line
 | 
				
			||||
        // to two corners and curl consists of max mMaxCurlSplits lines each
 | 
				
			||||
        // outputting 2 vertices.
 | 
				
			||||
        int maxVerticesCount = 4 + 2 + (2 * mMaxCurlSplits); | 
				
			||||
        ByteBuffer vbb = ByteBuffer.allocateDirect(maxVerticesCount * 3 * 4); | 
				
			||||
        vbb.order(ByteOrder.nativeOrder()); | 
				
			||||
        mBufVertices = vbb.asFloatBuffer(); | 
				
			||||
        mBufVertices.position(0); | 
				
			||||
 | 
				
			||||
        if (DRAW_TEXTURE) { | 
				
			||||
            ByteBuffer tbb = ByteBuffer | 
				
			||||
                    .allocateDirect(maxVerticesCount * 2 * 4); | 
				
			||||
            tbb.order(ByteOrder.nativeOrder()); | 
				
			||||
            mBufTexCoords = tbb.asFloatBuffer(); | 
				
			||||
            mBufTexCoords.position(0); | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        ByteBuffer cbb = ByteBuffer.allocateDirect(maxVerticesCount * 4 * 4); | 
				
			||||
        cbb.order(ByteOrder.nativeOrder()); | 
				
			||||
        mBufColors = cbb.asFloatBuffer(); | 
				
			||||
        mBufColors.position(0); | 
				
			||||
 | 
				
			||||
        if (DRAW_SHADOW) { | 
				
			||||
            int maxShadowVerticesCount = (mMaxCurlSplits + 2) * 2 * 2; | 
				
			||||
            ByteBuffer scbb = ByteBuffer | 
				
			||||
                    .allocateDirect(maxShadowVerticesCount * 4 * 4); | 
				
			||||
            scbb.order(ByteOrder.nativeOrder()); | 
				
			||||
            mBufShadowColors = scbb.asFloatBuffer(); | 
				
			||||
            mBufShadowColors.position(0); | 
				
			||||
 | 
				
			||||
            ByteBuffer sibb = ByteBuffer | 
				
			||||
                    .allocateDirect(maxShadowVerticesCount * 3 * 4); | 
				
			||||
            sibb.order(ByteOrder.nativeOrder()); | 
				
			||||
            mBufShadowVertices = sibb.asFloatBuffer(); | 
				
			||||
            mBufShadowVertices.position(0); | 
				
			||||
 | 
				
			||||
            mDropShadowCount = mSelfShadowCount = 0; | 
				
			||||
        } | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * Adds vertex to buffers. | 
				
			||||
     */ | 
				
			||||
    private void addVertex(Vertex vertex) { | 
				
			||||
        mBufVertices.put((float) vertex.mPosX); | 
				
			||||
        mBufVertices.put((float) vertex.mPosY); | 
				
			||||
        mBufVertices.put((float) vertex.mPosZ); | 
				
			||||
        mBufColors.put(vertex.mColorFactor * Color.red(vertex.mColor) / 255f); | 
				
			||||
        mBufColors.put(vertex.mColorFactor * Color.green(vertex.mColor) / 255f); | 
				
			||||
        mBufColors.put(vertex.mColorFactor * Color.blue(vertex.mColor) / 255f); | 
				
			||||
        mBufColors.put(Color.alpha(vertex.mColor) / 255f); | 
				
			||||
        if (DRAW_TEXTURE) { | 
				
			||||
            mBufTexCoords.put((float) vertex.mTexX); | 
				
			||||
            mBufTexCoords.put((float) vertex.mTexY); | 
				
			||||
        } | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * Sets curl for this mesh. | 
				
			||||
     * | 
				
			||||
     * @param curlPos Position for curl 'center'. Can be any point on line collinear | 
				
			||||
     *                to curl. | 
				
			||||
     * @param curlDir Curl direction, should be normalized. | 
				
			||||
     * @param radius  Radius of curl. | 
				
			||||
     */ | 
				
			||||
    public synchronized void curl(PointF curlPos, PointF curlDir, double radius) { | 
				
			||||
 | 
				
			||||
        // First add some 'helper' lines used for development.
 | 
				
			||||
        if (DRAW_CURL_POSITION) { | 
				
			||||
            mBufCurlPositionLines.position(0); | 
				
			||||
 | 
				
			||||
            mBufCurlPositionLines.put(curlPos.x); | 
				
			||||
            mBufCurlPositionLines.put(curlPos.y - 1.0f); | 
				
			||||
            mBufCurlPositionLines.put(curlPos.x); | 
				
			||||
            mBufCurlPositionLines.put(curlPos.y + 1.0f); | 
				
			||||
            mBufCurlPositionLines.put(curlPos.x - 1.0f); | 
				
			||||
            mBufCurlPositionLines.put(curlPos.y); | 
				
			||||
            mBufCurlPositionLines.put(curlPos.x + 1.0f); | 
				
			||||
            mBufCurlPositionLines.put(curlPos.y); | 
				
			||||
 | 
				
			||||
            mBufCurlPositionLines.put(curlPos.x); | 
				
			||||
            mBufCurlPositionLines.put(curlPos.y); | 
				
			||||
            mBufCurlPositionLines.put(curlPos.x + curlDir.x * 2); | 
				
			||||
            mBufCurlPositionLines.put(curlPos.y + curlDir.y * 2); | 
				
			||||
 | 
				
			||||
            mBufCurlPositionLines.position(0); | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        // Actual 'curl' implementation starts here.
 | 
				
			||||
        mBufVertices.position(0); | 
				
			||||
        mBufColors.position(0); | 
				
			||||
        if (DRAW_TEXTURE) { | 
				
			||||
            mBufTexCoords.position(0); | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        // Calculate curl angle from direction.
 | 
				
			||||
        double curlAngle = Math.acos(curlDir.x); | 
				
			||||
        curlAngle = curlDir.y > 0 ? -curlAngle : curlAngle; | 
				
			||||
 | 
				
			||||
        // Initiate rotated rectangle which's is translated to curlPos and
 | 
				
			||||
        // rotated so that curl direction heads to right (1,0). Vertices are
 | 
				
			||||
        // ordered in ascending order based on x -coordinate at the same time.
 | 
				
			||||
        // And using y -coordinate in very rare case in which two vertices have
 | 
				
			||||
        // same x -coordinate.
 | 
				
			||||
        mArrTempVertices.addAll(mArrRotatedVertices); | 
				
			||||
        mArrRotatedVertices.clear(); | 
				
			||||
        for (int i = 0; i < 4; ++i) { | 
				
			||||
            Vertex v = mArrTempVertices.remove(0); | 
				
			||||
            v.set(mRectangle[i]); | 
				
			||||
            v.translate(-curlPos.x, -curlPos.y); | 
				
			||||
            v.rotateZ(-curlAngle); | 
				
			||||
            int j = 0; | 
				
			||||
            for (; j < mArrRotatedVertices.size(); ++j) { | 
				
			||||
                Vertex v2 = mArrRotatedVertices.get(j); | 
				
			||||
                if (v.mPosX > v2.mPosX) { | 
				
			||||
                    break; | 
				
			||||
                } | 
				
			||||
                if (v.mPosX == v2.mPosX && v.mPosY > v2.mPosY) { | 
				
			||||
                    break; | 
				
			||||
                } | 
				
			||||
            } | 
				
			||||
            mArrRotatedVertices.add(j, v); | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        // Rotated rectangle lines/vertex indices. We need to find bounding
 | 
				
			||||
        // lines for rotated rectangle. After sorting vertices according to
 | 
				
			||||
        // their x -coordinate we don't have to worry about vertices at indices
 | 
				
			||||
        // 0 and 1. But due to inaccuracy it's possible vertex 3 is not the
 | 
				
			||||
        // opposing corner from vertex 0. So we are calculating distance from
 | 
				
			||||
        // vertex 0 to vertices 2 and 3 - and altering line indices if needed.
 | 
				
			||||
        // Also vertices/lines are given in an order first one has x -coordinate
 | 
				
			||||
        // at least the latter one. This property is used in getIntersections to
 | 
				
			||||
        // see if there is an intersection.
 | 
				
			||||
        int[][] lines = {{0, 1}, {0, 2}, {1, 3}, {2, 3}}; | 
				
			||||
        { | 
				
			||||
            // TODO: There really has to be more 'easier' way of doing this -
 | 
				
			||||
            // not including extensive use of sqrt.
 | 
				
			||||
            Vertex v0 = mArrRotatedVertices.get(0); | 
				
			||||
            Vertex v2 = mArrRotatedVertices.get(2); | 
				
			||||
            Vertex v3 = mArrRotatedVertices.get(3); | 
				
			||||
            double dist2 = Math.sqrt((v0.mPosX - v2.mPosX) | 
				
			||||
                    * (v0.mPosX - v2.mPosX) + (v0.mPosY - v2.mPosY) | 
				
			||||
                    * (v0.mPosY - v2.mPosY)); | 
				
			||||
            double dist3 = Math.sqrt((v0.mPosX - v3.mPosX) | 
				
			||||
                    * (v0.mPosX - v3.mPosX) + (v0.mPosY - v3.mPosY) | 
				
			||||
                    * (v0.mPosY - v3.mPosY)); | 
				
			||||
            if (dist2 > dist3) { | 
				
			||||
                lines[1][1] = 3; | 
				
			||||
                lines[2][1] = 2; | 
				
			||||
            } | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        mVerticesCountFront = mVerticesCountBack = 0; | 
				
			||||
 | 
				
			||||
        if (DRAW_SHADOW) { | 
				
			||||
            mArrTempShadowVertices.addAll(mArrDropShadowVertices); | 
				
			||||
            mArrTempShadowVertices.addAll(mArrSelfShadowVertices); | 
				
			||||
            mArrDropShadowVertices.clear(); | 
				
			||||
            mArrSelfShadowVertices.clear(); | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        // Length of 'curl' curve.
 | 
				
			||||
        double curlLength = Math.PI * radius; | 
				
			||||
        // Calculate scan lines.
 | 
				
			||||
        // TODO: Revisit this code one day. There is room for optimization here.
 | 
				
			||||
        mArrScanLines.clear(); | 
				
			||||
        if (mMaxCurlSplits > 0) { | 
				
			||||
            mArrScanLines.add((double) 0); | 
				
			||||
        } | 
				
			||||
        for (int i = 1; i < mMaxCurlSplits; ++i) { | 
				
			||||
            mArrScanLines.add((-curlLength * i) / (mMaxCurlSplits - 1)); | 
				
			||||
        } | 
				
			||||
        // As mRotatedVertices is ordered regarding x -coordinate, adding
 | 
				
			||||
        // this scan line produces scan area picking up vertices which are
 | 
				
			||||
        // rotated completely. One could say 'until infinity'.
 | 
				
			||||
        mArrScanLines.add(mArrRotatedVertices.get(3).mPosX - 1); | 
				
			||||
 | 
				
			||||
        // Start from right most vertex. Pretty much the same as first scan area
 | 
				
			||||
        // is starting from 'infinity'.
 | 
				
			||||
        double scanXmax = mArrRotatedVertices.get(0).mPosX + 1; | 
				
			||||
 | 
				
			||||
        for (int i = 0; i < mArrScanLines.size(); ++i) { | 
				
			||||
            // Once we have scanXmin and scanXmax we have a scan area to start
 | 
				
			||||
            // working with.
 | 
				
			||||
            double scanXmin = mArrScanLines.get(i); | 
				
			||||
            // First iterate 'original' rectangle vertices within scan area.
 | 
				
			||||
            for (int j = 0; j < mArrRotatedVertices.size(); ++j) { | 
				
			||||
                Vertex v = mArrRotatedVertices.get(j); | 
				
			||||
                // Test if vertex lies within this scan area.
 | 
				
			||||
                // TODO: Frankly speaking, can't remember why equality check was
 | 
				
			||||
                // added to both ends. Guessing it was somehow related to case
 | 
				
			||||
                // where radius=0f, which, given current implementation, could
 | 
				
			||||
                // be handled much more effectively anyway.
 | 
				
			||||
                if (v.mPosX >= scanXmin && v.mPosX <= scanXmax) { | 
				
			||||
                    // Pop out a vertex from temp vertices.
 | 
				
			||||
                    Vertex n = mArrTempVertices.remove(0); | 
				
			||||
                    n.set(v); | 
				
			||||
                    // This is done solely for triangulation reasons. Given a
 | 
				
			||||
                    // rotated rectangle it has max 2 vertices having
 | 
				
			||||
                    // intersection.
 | 
				
			||||
                    Array<Vertex> intersections = getIntersections( | 
				
			||||
                            mArrRotatedVertices, lines, n.mPosX); | 
				
			||||
                    // In a sense one could say we're adding vertices always in
 | 
				
			||||
                    // two, positioned at the ends of intersecting line. And for
 | 
				
			||||
                    // triangulation to work properly they are added based on y
 | 
				
			||||
                    // -coordinate. And this if-else is doing it for us.
 | 
				
			||||
                    if (intersections.size() == 1 | 
				
			||||
                            && intersections.get(0).mPosY > v.mPosY) { | 
				
			||||
                        // In case intersecting vertex is higher add it first.
 | 
				
			||||
                        mArrOutputVertices.addAll(intersections); | 
				
			||||
                        mArrOutputVertices.add(n); | 
				
			||||
                    } else if (intersections.size() <= 1) { | 
				
			||||
                        // Otherwise add original vertex first.
 | 
				
			||||
                        mArrOutputVertices.add(n); | 
				
			||||
                        mArrOutputVertices.addAll(intersections); | 
				
			||||
                    } else { | 
				
			||||
                        // There should never be more than 1 intersecting
 | 
				
			||||
                        // vertex. But if it happens as a fallback simply skip
 | 
				
			||||
                        // everything.
 | 
				
			||||
                        mArrTempVertices.add(n); | 
				
			||||
                        mArrTempVertices.addAll(intersections); | 
				
			||||
                    } | 
				
			||||
                } | 
				
			||||
            } | 
				
			||||
 | 
				
			||||
            // Search for scan line intersections.
 | 
				
			||||
            Array<Vertex> intersections = getIntersections(mArrRotatedVertices, | 
				
			||||
                    lines, scanXmin); | 
				
			||||
 | 
				
			||||
            // We expect to get 0 or 2 vertices. In rare cases there's only one
 | 
				
			||||
            // but in general given a scan line intersecting rectangle there
 | 
				
			||||
            // should be 2 intersecting vertices.
 | 
				
			||||
            if (intersections.size() == 2) { | 
				
			||||
                // There were two intersections, add them based on y
 | 
				
			||||
                // -coordinate, higher first, lower last.
 | 
				
			||||
                Vertex v1 = intersections.get(0); | 
				
			||||
                Vertex v2 = intersections.get(1); | 
				
			||||
                if (v1.mPosY < v2.mPosY) { | 
				
			||||
                    mArrOutputVertices.add(v2); | 
				
			||||
                    mArrOutputVertices.add(v1); | 
				
			||||
                } else { | 
				
			||||
                    mArrOutputVertices.addAll(intersections); | 
				
			||||
                } | 
				
			||||
            } else if (intersections.size() != 0) { | 
				
			||||
                // This happens in a case in which there is a original vertex
 | 
				
			||||
                // exactly at scan line or something went very much wrong if
 | 
				
			||||
                // there are 3+ vertices. What ever the reason just return the
 | 
				
			||||
                // vertices to temp vertices for later use. In former case it
 | 
				
			||||
                // was handled already earlier once iterating through
 | 
				
			||||
                // mRotatedVertices, in latter case it's better to avoid doing
 | 
				
			||||
                // anything with them.
 | 
				
			||||
                mArrTempVertices.addAll(intersections); | 
				
			||||
            } | 
				
			||||
 | 
				
			||||
            // Add vertices found during this iteration to vertex etc buffers.
 | 
				
			||||
            while (mArrOutputVertices.size() > 0) { | 
				
			||||
                Vertex v = mArrOutputVertices.remove(0); | 
				
			||||
                mArrTempVertices.add(v); | 
				
			||||
 | 
				
			||||
                // Local texture front-facing flag.
 | 
				
			||||
                boolean textureFront; | 
				
			||||
 | 
				
			||||
                // Untouched vertices.
 | 
				
			||||
                if (i == 0) { | 
				
			||||
                    textureFront = true; | 
				
			||||
                    mVerticesCountFront++; | 
				
			||||
                } | 
				
			||||
                // 'Completely' rotated vertices.
 | 
				
			||||
                else if (i == mArrScanLines.size() - 1 || curlLength == 0) { | 
				
			||||
                    v.mPosX = -(curlLength + v.mPosX); | 
				
			||||
                    v.mPosZ = 2 * radius; | 
				
			||||
                    v.mPenumbraX = -v.mPenumbraX; | 
				
			||||
 | 
				
			||||
                    textureFront = false; | 
				
			||||
                    mVerticesCountBack++; | 
				
			||||
                } | 
				
			||||
                // Vertex lies within 'curl'.
 | 
				
			||||
                else { | 
				
			||||
                    // Even though it's not obvious from the if-else clause,
 | 
				
			||||
                    // here v.mPosX is between [-curlLength, 0]. And we can do
 | 
				
			||||
                    // calculations around a half cylinder.
 | 
				
			||||
                    double rotY = Math.PI * (v.mPosX / curlLength); | 
				
			||||
                    v.mPosX = radius * Math.sin(rotY); | 
				
			||||
                    v.mPosZ = radius - (radius * Math.cos(rotY)); | 
				
			||||
                    v.mPenumbraX *= Math.cos(rotY); | 
				
			||||
                    // Map color multiplier to [.1f, 1f] range.
 | 
				
			||||
                    v.mColorFactor = (float) (.1f + .9f * Math.sqrt(Math | 
				
			||||
                            .sin(rotY) + 1)); | 
				
			||||
 | 
				
			||||
                    if (v.mPosZ >= radius) { | 
				
			||||
                        textureFront = false; | 
				
			||||
                        mVerticesCountBack++; | 
				
			||||
                    } else { | 
				
			||||
                        textureFront = true; | 
				
			||||
                        mVerticesCountFront++; | 
				
			||||
                    } | 
				
			||||
                } | 
				
			||||
 | 
				
			||||
                // We use local textureFront for flipping backside texture
 | 
				
			||||
                // locally. Plus additionally if mesh is in flip texture mode,
 | 
				
			||||
                // we'll make the procedure "backwards". Also, until this point,
 | 
				
			||||
                // texture coordinates are within [0, 1] range so we'll adjust
 | 
				
			||||
                // them to final texture coordinates too.
 | 
				
			||||
                if (textureFront != mFlipTexture) { | 
				
			||||
                    v.mTexX *= mTextureRectFront.right; | 
				
			||||
                    v.mTexY *= mTextureRectFront.bottom; | 
				
			||||
                    v.mColor = mTexturePage.getColor(CurlPage.SIDE_FRONT); | 
				
			||||
                } else { | 
				
			||||
                    v.mTexX *= mTextureRectBack.right; | 
				
			||||
                    v.mTexY *= mTextureRectBack.bottom; | 
				
			||||
                    v.mColor = mTexturePage.getColor(CurlPage.SIDE_BACK); | 
				
			||||
                } | 
				
			||||
 | 
				
			||||
                // Move vertex back to 'world' coordinates.
 | 
				
			||||
                v.rotateZ(curlAngle); | 
				
			||||
                v.translate(curlPos.x, curlPos.y); | 
				
			||||
                addVertex(v); | 
				
			||||
 | 
				
			||||
                // Drop shadow is cast 'behind' the curl.
 | 
				
			||||
                if (DRAW_SHADOW && v.mPosZ > 0 && v.mPosZ <= radius) { | 
				
			||||
                    ShadowVertex sv = mArrTempShadowVertices.remove(0); | 
				
			||||
                    sv.mPosX = v.mPosX; | 
				
			||||
                    sv.mPosY = v.mPosY; | 
				
			||||
                    sv.mPosZ = v.mPosZ; | 
				
			||||
                    sv.mPenumbraX = (v.mPosZ / 2) * -curlDir.x; | 
				
			||||
                    sv.mPenumbraY = (v.mPosZ / 2) * -curlDir.y; | 
				
			||||
                    sv.mPenumbraColor = v.mPosZ / radius; | 
				
			||||
                    int idx = (mArrDropShadowVertices.size() + 1) / 2; | 
				
			||||
                    mArrDropShadowVertices.add(idx, sv); | 
				
			||||
                } | 
				
			||||
                // Self shadow is cast partly over mesh.
 | 
				
			||||
                if (DRAW_SHADOW && v.mPosZ > radius) { | 
				
			||||
                    ShadowVertex sv = mArrTempShadowVertices.remove(0); | 
				
			||||
                    sv.mPosX = v.mPosX; | 
				
			||||
                    sv.mPosY = v.mPosY; | 
				
			||||
                    sv.mPosZ = v.mPosZ; | 
				
			||||
                    sv.mPenumbraX = ((v.mPosZ - radius) / 3) * v.mPenumbraX; | 
				
			||||
                    sv.mPenumbraY = ((v.mPosZ - radius) / 3) * v.mPenumbraY; | 
				
			||||
                    sv.mPenumbraColor = (v.mPosZ - radius) / (2 * radius); | 
				
			||||
                    int idx = (mArrSelfShadowVertices.size() + 1) / 2; | 
				
			||||
                    mArrSelfShadowVertices.add(idx, sv); | 
				
			||||
                } | 
				
			||||
            } | 
				
			||||
 | 
				
			||||
            // Switch scanXmin as scanXmax for next iteration.
 | 
				
			||||
            scanXmax = scanXmin; | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        mBufVertices.position(0); | 
				
			||||
        mBufColors.position(0); | 
				
			||||
        if (DRAW_TEXTURE) { | 
				
			||||
            mBufTexCoords.position(0); | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        // Add shadow Vertices.
 | 
				
			||||
        if (DRAW_SHADOW) { | 
				
			||||
            mBufShadowColors.position(0); | 
				
			||||
            mBufShadowVertices.position(0); | 
				
			||||
            mDropShadowCount = 0; | 
				
			||||
 | 
				
			||||
            for (int i = 0; i < mArrDropShadowVertices.size(); ++i) { | 
				
			||||
                ShadowVertex sv = mArrDropShadowVertices.get(i); | 
				
			||||
                mBufShadowVertices.put((float) sv.mPosX); | 
				
			||||
                mBufShadowVertices.put((float) sv.mPosY); | 
				
			||||
                mBufShadowVertices.put((float) sv.mPosZ); | 
				
			||||
                mBufShadowVertices.put((float) (sv.mPosX + sv.mPenumbraX)); | 
				
			||||
                mBufShadowVertices.put((float) (sv.mPosY + sv.mPenumbraY)); | 
				
			||||
                mBufShadowVertices.put((float) sv.mPosZ); | 
				
			||||
                for (int j = 0; j < 4; ++j) { | 
				
			||||
                    double color = SHADOW_OUTER_COLOR[j] | 
				
			||||
                            + (SHADOW_INNER_COLOR[j] - SHADOW_OUTER_COLOR[j]) | 
				
			||||
                            * sv.mPenumbraColor; | 
				
			||||
                    mBufShadowColors.put((float) color); | 
				
			||||
                } | 
				
			||||
                mBufShadowColors.put(SHADOW_OUTER_COLOR); | 
				
			||||
                mDropShadowCount += 2; | 
				
			||||
            } | 
				
			||||
            mSelfShadowCount = 0; | 
				
			||||
            for (int i = 0; i < mArrSelfShadowVertices.size(); ++i) { | 
				
			||||
                ShadowVertex sv = mArrSelfShadowVertices.get(i); | 
				
			||||
                mBufShadowVertices.put((float) sv.mPosX); | 
				
			||||
                mBufShadowVertices.put((float) sv.mPosY); | 
				
			||||
                mBufShadowVertices.put((float) sv.mPosZ); | 
				
			||||
                mBufShadowVertices.put((float) (sv.mPosX + sv.mPenumbraX)); | 
				
			||||
                mBufShadowVertices.put((float) (sv.mPosY + sv.mPenumbraY)); | 
				
			||||
                mBufShadowVertices.put((float) sv.mPosZ); | 
				
			||||
                for (int j = 0; j < 4; ++j) { | 
				
			||||
                    double color = SHADOW_OUTER_COLOR[j] | 
				
			||||
                            + (SHADOW_INNER_COLOR[j] - SHADOW_OUTER_COLOR[j]) | 
				
			||||
                            * sv.mPenumbraColor; | 
				
			||||
                    mBufShadowColors.put((float) color); | 
				
			||||
                } | 
				
			||||
                mBufShadowColors.put(SHADOW_OUTER_COLOR); | 
				
			||||
                mSelfShadowCount += 2; | 
				
			||||
            } | 
				
			||||
            mBufShadowColors.position(0); | 
				
			||||
            mBufShadowVertices.position(0); | 
				
			||||
        } | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * Calculates intersections for given scan line. | 
				
			||||
     */ | 
				
			||||
    private Array<Vertex> getIntersections(Array<Vertex> vertices, | 
				
			||||
                                           int[][] lineIndices, double scanX) { | 
				
			||||
        mArrIntersections.clear(); | 
				
			||||
        // Iterate through rectangle lines each re-presented as a pair of
 | 
				
			||||
        // vertices.
 | 
				
			||||
        for (int j = 0; j < lineIndices.length; j++) { | 
				
			||||
            Vertex v1 = vertices.get(lineIndices[j][0]); | 
				
			||||
            Vertex v2 = vertices.get(lineIndices[j][1]); | 
				
			||||
            // Here we expect that v1.mPosX >= v2.mPosX and wont do intersection
 | 
				
			||||
            // test the opposite way.
 | 
				
			||||
            if (v1.mPosX > scanX && v2.mPosX < scanX) { | 
				
			||||
                // There is an intersection, calculate coefficient telling 'how
 | 
				
			||||
                // far' scanX is from v2.
 | 
				
			||||
                double c = (scanX - v2.mPosX) / (v1.mPosX - v2.mPosX); | 
				
			||||
                Vertex n = mArrTempVertices.remove(0); | 
				
			||||
                n.set(v2); | 
				
			||||
                n.mPosX = scanX; | 
				
			||||
                n.mPosY += (v1.mPosY - v2.mPosY) * c; | 
				
			||||
                if (DRAW_TEXTURE) { | 
				
			||||
                    n.mTexX += (v1.mTexX - v2.mTexX) * c; | 
				
			||||
                    n.mTexY += (v1.mTexY - v2.mTexY) * c; | 
				
			||||
                } | 
				
			||||
                if (DRAW_SHADOW) { | 
				
			||||
                    n.mPenumbraX += (v1.mPenumbraX - v2.mPenumbraX) * c; | 
				
			||||
                    n.mPenumbraY += (v1.mPenumbraY - v2.mPenumbraY) * c; | 
				
			||||
                } | 
				
			||||
                mArrIntersections.add(n); | 
				
			||||
            } | 
				
			||||
        } | 
				
			||||
        return mArrIntersections; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * Getter for textures page for this mesh. | 
				
			||||
     */ | 
				
			||||
    public synchronized CurlPage getTexturePage() { | 
				
			||||
        return mTexturePage; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * Renders our page curl mesh. | 
				
			||||
     */ | 
				
			||||
    public synchronized void onDrawFrame(GL10 gl) { | 
				
			||||
        // First allocate texture if there is not one yet.
 | 
				
			||||
        if (DRAW_TEXTURE && mTextureIds == null) { | 
				
			||||
            // Generate texture.
 | 
				
			||||
            mTextureIds = new int[2]; | 
				
			||||
            gl.glGenTextures(2, mTextureIds, 0); | 
				
			||||
            for (int textureId : mTextureIds) { | 
				
			||||
                // Set texture attributes.
 | 
				
			||||
                gl.glBindTexture(GL10.GL_TEXTURE_2D, textureId); | 
				
			||||
                gl.glTexParameterf(GL10.GL_TEXTURE_2D, | 
				
			||||
                        GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST); | 
				
			||||
                gl.glTexParameterf(GL10.GL_TEXTURE_2D, | 
				
			||||
                        GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_NEAREST); | 
				
			||||
                gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, | 
				
			||||
                        GL10.GL_CLAMP_TO_EDGE); | 
				
			||||
                gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, | 
				
			||||
                        GL10.GL_CLAMP_TO_EDGE); | 
				
			||||
            } | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        if (DRAW_TEXTURE && mTexturePage.getTexturesChanged()) { | 
				
			||||
            gl.glBindTexture(GL10.GL_TEXTURE_2D, mTextureIds[0]); | 
				
			||||
            Bitmap texture = mTexturePage.getTexture(mTextureRectFront, | 
				
			||||
                    CurlPage.SIDE_FRONT); | 
				
			||||
            GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, texture, 0); | 
				
			||||
            texture.recycle(); | 
				
			||||
 | 
				
			||||
            mTextureBack = mTexturePage.hasBackTexture(); | 
				
			||||
            if (mTextureBack) { | 
				
			||||
                gl.glBindTexture(GL10.GL_TEXTURE_2D, mTextureIds[1]); | 
				
			||||
                texture = mTexturePage.getTexture(mTextureRectBack, | 
				
			||||
                        CurlPage.SIDE_BACK); | 
				
			||||
                GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, texture, 0); | 
				
			||||
                texture.recycle(); | 
				
			||||
            } else { | 
				
			||||
                mTextureRectBack.set(mTextureRectFront); | 
				
			||||
            } | 
				
			||||
 | 
				
			||||
            mTexturePage.recycle(); | 
				
			||||
            reset(); | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        // Some 'global' settings.
 | 
				
			||||
        gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); | 
				
			||||
 | 
				
			||||
        // TODO: Drop shadow drawing is done temporarily here to hide some
 | 
				
			||||
        // problems with its calculation.
 | 
				
			||||
        if (DRAW_SHADOW) { | 
				
			||||
            gl.glDisable(GL10.GL_TEXTURE_2D); | 
				
			||||
            gl.glEnable(GL10.GL_BLEND); | 
				
			||||
            gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA); | 
				
			||||
            gl.glEnableClientState(GL10.GL_COLOR_ARRAY); | 
				
			||||
            gl.glColorPointer(4, GL10.GL_FLOAT, 0, mBufShadowColors); | 
				
			||||
            gl.glVertexPointer(3, GL10.GL_FLOAT, 0, mBufShadowVertices); | 
				
			||||
            gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, mDropShadowCount); | 
				
			||||
            gl.glDisableClientState(GL10.GL_COLOR_ARRAY); | 
				
			||||
            gl.glDisable(GL10.GL_BLEND); | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        if (DRAW_TEXTURE) { | 
				
			||||
            gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY); | 
				
			||||
            gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, mBufTexCoords); | 
				
			||||
        } | 
				
			||||
        gl.glVertexPointer(3, GL10.GL_FLOAT, 0, mBufVertices); | 
				
			||||
        // Enable color array.
 | 
				
			||||
        gl.glEnableClientState(GL10.GL_COLOR_ARRAY); | 
				
			||||
        gl.glColorPointer(4, GL10.GL_FLOAT, 0, mBufColors); | 
				
			||||
 | 
				
			||||
        // Draw front facing blank vertices.
 | 
				
			||||
        gl.glDisable(GL10.GL_TEXTURE_2D); | 
				
			||||
        gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, mVerticesCountFront); | 
				
			||||
 | 
				
			||||
        // Draw front facing texture.
 | 
				
			||||
        if (DRAW_TEXTURE) { | 
				
			||||
            gl.glEnable(GL10.GL_BLEND); | 
				
			||||
            gl.glEnable(GL10.GL_TEXTURE_2D); | 
				
			||||
 | 
				
			||||
            if (!mFlipTexture || !mTextureBack) { | 
				
			||||
                gl.glBindTexture(GL10.GL_TEXTURE_2D, mTextureIds[0]); | 
				
			||||
            } else { | 
				
			||||
                gl.glBindTexture(GL10.GL_TEXTURE_2D, mTextureIds[1]); | 
				
			||||
            } | 
				
			||||
 | 
				
			||||
            gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA); | 
				
			||||
            gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, mVerticesCountFront); | 
				
			||||
 | 
				
			||||
            gl.glDisable(GL10.GL_BLEND); | 
				
			||||
            gl.glDisable(GL10.GL_TEXTURE_2D); | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        int backStartIdx = Math.max(0, mVerticesCountFront - 2); | 
				
			||||
        int backCount = mVerticesCountFront + mVerticesCountBack - backStartIdx; | 
				
			||||
 | 
				
			||||
        // Draw back facing blank vertices.
 | 
				
			||||
        gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, backStartIdx, backCount); | 
				
			||||
 | 
				
			||||
        // Draw back facing texture.
 | 
				
			||||
        if (DRAW_TEXTURE) { | 
				
			||||
            gl.glEnable(GL10.GL_BLEND); | 
				
			||||
            gl.glEnable(GL10.GL_TEXTURE_2D); | 
				
			||||
 | 
				
			||||
            if (mFlipTexture || !mTextureBack) { | 
				
			||||
                gl.glBindTexture(GL10.GL_TEXTURE_2D, mTextureIds[0]); | 
				
			||||
            } else { | 
				
			||||
                gl.glBindTexture(GL10.GL_TEXTURE_2D, mTextureIds[1]); | 
				
			||||
            } | 
				
			||||
 | 
				
			||||
            gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA); | 
				
			||||
            gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, backStartIdx, backCount); | 
				
			||||
 | 
				
			||||
            gl.glDisable(GL10.GL_BLEND); | 
				
			||||
            gl.glDisable(GL10.GL_TEXTURE_2D); | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        // Disable textures and color array.
 | 
				
			||||
        gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY); | 
				
			||||
        gl.glDisableClientState(GL10.GL_COLOR_ARRAY); | 
				
			||||
 | 
				
			||||
        if (DRAW_POLYGON_OUTLINES) { | 
				
			||||
            gl.glEnable(GL10.GL_BLEND); | 
				
			||||
            gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA); | 
				
			||||
            gl.glLineWidth(1.0f); | 
				
			||||
            gl.glColor4f(0.5f, 0.5f, 1.0f, 1.0f); | 
				
			||||
            gl.glVertexPointer(3, GL10.GL_FLOAT, 0, mBufVertices); | 
				
			||||
            gl.glDrawArrays(GL10.GL_LINE_STRIP, 0, mVerticesCountFront); | 
				
			||||
            gl.glDisable(GL10.GL_BLEND); | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        if (DRAW_CURL_POSITION) { | 
				
			||||
            gl.glEnable(GL10.GL_BLEND); | 
				
			||||
            gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA); | 
				
			||||
            gl.glLineWidth(1.0f); | 
				
			||||
            gl.glColor4f(1.0f, 0.5f, 0.5f, 1.0f); | 
				
			||||
            gl.glVertexPointer(2, GL10.GL_FLOAT, 0, mBufCurlPositionLines); | 
				
			||||
            gl.glDrawArrays(GL10.GL_LINES, 0, mCurlPositionLinesCount * 2); | 
				
			||||
            gl.glDisable(GL10.GL_BLEND); | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        if (DRAW_SHADOW) { | 
				
			||||
            gl.glEnable(GL10.GL_BLEND); | 
				
			||||
            gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA); | 
				
			||||
            gl.glEnableClientState(GL10.GL_COLOR_ARRAY); | 
				
			||||
            gl.glColorPointer(4, GL10.GL_FLOAT, 0, mBufShadowColors); | 
				
			||||
            gl.glVertexPointer(3, GL10.GL_FLOAT, 0, mBufShadowVertices); | 
				
			||||
            gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, mDropShadowCount, | 
				
			||||
                    mSelfShadowCount); | 
				
			||||
            gl.glDisableClientState(GL10.GL_COLOR_ARRAY); | 
				
			||||
            gl.glDisable(GL10.GL_BLEND); | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        gl.glDisableClientState(GL10.GL_VERTEX_ARRAY); | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * Resets mesh to 'initial' state. Meaning this mesh will draw a plain | 
				
			||||
     * textured rectangle after call to this method. | 
				
			||||
     */ | 
				
			||||
    public synchronized void reset() { | 
				
			||||
        mBufVertices.position(0); | 
				
			||||
        mBufColors.position(0); | 
				
			||||
        if (DRAW_TEXTURE) { | 
				
			||||
            mBufTexCoords.position(0); | 
				
			||||
        } | 
				
			||||
        for (int i = 0; i < 4; ++i) { | 
				
			||||
            Vertex tmp = mArrTempVertices.get(0); | 
				
			||||
            tmp.set(mRectangle[i]); | 
				
			||||
 | 
				
			||||
            if (mFlipTexture) { | 
				
			||||
                tmp.mTexX *= mTextureRectBack.right; | 
				
			||||
                tmp.mTexY *= mTextureRectBack.bottom; | 
				
			||||
                tmp.mColor = mTexturePage.getColor(CurlPage.SIDE_BACK); | 
				
			||||
            } else { | 
				
			||||
                tmp.mTexX *= mTextureRectFront.right; | 
				
			||||
                tmp.mTexY *= mTextureRectFront.bottom; | 
				
			||||
                tmp.mColor = mTexturePage.getColor(CurlPage.SIDE_FRONT); | 
				
			||||
            } | 
				
			||||
 | 
				
			||||
            addVertex(tmp); | 
				
			||||
        } | 
				
			||||
        mVerticesCountFront = 4; | 
				
			||||
        mVerticesCountBack = 0; | 
				
			||||
        mBufVertices.position(0); | 
				
			||||
        mBufColors.position(0); | 
				
			||||
        if (DRAW_TEXTURE) { | 
				
			||||
            mBufTexCoords.position(0); | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        mDropShadowCount = mSelfShadowCount = 0; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * Resets allocated texture id forcing creation of new one. After calling | 
				
			||||
     * this method you most likely want to set bitmap too as it's lost. This | 
				
			||||
     * method should be called only once e.g GL context is re-created as this | 
				
			||||
     * method does not release previous texture id, only makes sure new one is | 
				
			||||
     * requested on next render. | 
				
			||||
     */ | 
				
			||||
    public synchronized void resetTexture() { | 
				
			||||
        mTextureIds = null; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * If true, flips texture sideways. | 
				
			||||
     */ | 
				
			||||
    public synchronized void setFlipTexture(boolean flipTexture) { | 
				
			||||
        mFlipTexture = flipTexture; | 
				
			||||
        if (flipTexture) { | 
				
			||||
            setTexCoords(1f, 0f, 0f, 1f); | 
				
			||||
        } else { | 
				
			||||
            setTexCoords(0f, 0f, 1f, 1f); | 
				
			||||
        } | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * Update mesh bounds. | 
				
			||||
     */ | 
				
			||||
    public void setRect(RectF r) { | 
				
			||||
        mRectangle[0].mPosX = r.left; | 
				
			||||
        mRectangle[0].mPosY = r.top; | 
				
			||||
        mRectangle[1].mPosX = r.left; | 
				
			||||
        mRectangle[1].mPosY = r.bottom; | 
				
			||||
        mRectangle[2].mPosX = r.right; | 
				
			||||
        mRectangle[2].mPosY = r.top; | 
				
			||||
        mRectangle[3].mPosX = r.right; | 
				
			||||
        mRectangle[3].mPosY = r.bottom; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * Sets texture coordinates to mRectangle vertices. | 
				
			||||
     */ | 
				
			||||
    private synchronized void setTexCoords(float left, float top, float right, | 
				
			||||
                                           float bottom) { | 
				
			||||
        mRectangle[0].mTexX = left; | 
				
			||||
        mRectangle[0].mTexY = top; | 
				
			||||
        mRectangle[1].mTexX = left; | 
				
			||||
        mRectangle[1].mTexY = bottom; | 
				
			||||
        mRectangle[2].mTexX = right; | 
				
			||||
        mRectangle[2].mTexY = top; | 
				
			||||
        mRectangle[3].mTexX = right; | 
				
			||||
        mRectangle[3].mTexY = bottom; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * Simple fixed size array implementation. | 
				
			||||
     */ | 
				
			||||
    private class Array<T> { | 
				
			||||
        private final Object[] mArray; | 
				
			||||
        private final int mCapacity; | 
				
			||||
        private int mSize; | 
				
			||||
 | 
				
			||||
        public Array(int capacity) { | 
				
			||||
            mCapacity = capacity; | 
				
			||||
            mArray = new Object[capacity]; | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        public void add(int index, T item) { | 
				
			||||
            if (index < 0 || index > mSize || mSize >= mCapacity) { | 
				
			||||
                throw new IndexOutOfBoundsException(); | 
				
			||||
            } | 
				
			||||
            for (int i = mSize; i > index; --i) { | 
				
			||||
                mArray[i] = mArray[i - 1]; | 
				
			||||
            } | 
				
			||||
            mArray[index] = item; | 
				
			||||
            ++mSize; | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        public void add(T item) { | 
				
			||||
            if (mSize >= mCapacity) { | 
				
			||||
                throw new IndexOutOfBoundsException(); | 
				
			||||
            } | 
				
			||||
            mArray[mSize++] = item; | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        public void addAll(Array<T> array) { | 
				
			||||
            if (mSize + array.size() > mCapacity) { | 
				
			||||
                throw new IndexOutOfBoundsException(); | 
				
			||||
            } | 
				
			||||
            for (int i = 0; i < array.size(); ++i) { | 
				
			||||
                mArray[mSize++] = array.get(i); | 
				
			||||
            } | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        public void clear() { | 
				
			||||
            mSize = 0; | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        @SuppressWarnings("unchecked") | 
				
			||||
        public T get(int index) { | 
				
			||||
            if (index < 0 || index >= mSize) { | 
				
			||||
                throw new IndexOutOfBoundsException(); | 
				
			||||
            } | 
				
			||||
            return (T) mArray[index]; | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        @SuppressWarnings("unchecked") | 
				
			||||
        public T remove(int index) { | 
				
			||||
            if (index < 0 || index >= mSize) { | 
				
			||||
                throw new IndexOutOfBoundsException(); | 
				
			||||
            } | 
				
			||||
            T item = (T) mArray[index]; | 
				
			||||
            for (int i = index; i < mSize - 1; ++i) { | 
				
			||||
                mArray[i] = mArray[i + 1]; | 
				
			||||
            } | 
				
			||||
            --mSize; | 
				
			||||
            return item; | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        public int size() { | 
				
			||||
            return mSize; | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * Holder for shadow vertex information. | 
				
			||||
     */ | 
				
			||||
    private class ShadowVertex { | 
				
			||||
        public double mPenumbraColor; | 
				
			||||
        public double mPenumbraX; | 
				
			||||
        public double mPenumbraY; | 
				
			||||
        public double mPosX; | 
				
			||||
        public double mPosY; | 
				
			||||
        public double mPosZ; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * Holder for vertex information. | 
				
			||||
     */ | 
				
			||||
    private class Vertex { | 
				
			||||
        public int mColor; | 
				
			||||
        public float mColorFactor; | 
				
			||||
        public double mPenumbraX; | 
				
			||||
        public double mPenumbraY; | 
				
			||||
        public double mPosX; | 
				
			||||
        public double mPosY; | 
				
			||||
        public double mPosZ; | 
				
			||||
        public double mTexX; | 
				
			||||
        public double mTexY; | 
				
			||||
 | 
				
			||||
        public Vertex() { | 
				
			||||
            mPosX = mPosY = mPosZ = mTexX = mTexY = 0; | 
				
			||||
            mColorFactor = 1.0f; | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        public void rotateZ(double theta) { | 
				
			||||
            double cos = Math.cos(theta); | 
				
			||||
            double sin = Math.sin(theta); | 
				
			||||
            double x = mPosX * cos + mPosY * sin; | 
				
			||||
            double y = mPosX * -sin + mPosY * cos; | 
				
			||||
            mPosX = x; | 
				
			||||
            mPosY = y; | 
				
			||||
            double px = mPenumbraX * cos + mPenumbraY * sin; | 
				
			||||
            double py = mPenumbraX * -sin + mPenumbraY * cos; | 
				
			||||
            mPenumbraX = px; | 
				
			||||
            mPenumbraY = py; | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        public void set(Vertex vertex) { | 
				
			||||
            mPosX = vertex.mPosX; | 
				
			||||
            mPosY = vertex.mPosY; | 
				
			||||
            mPosZ = vertex.mPosZ; | 
				
			||||
            mTexX = vertex.mTexX; | 
				
			||||
            mTexY = vertex.mTexY; | 
				
			||||
            mPenumbraX = vertex.mPenumbraX; | 
				
			||||
            mPenumbraY = vertex.mPenumbraY; | 
				
			||||
            mColor = vertex.mColor; | 
				
			||||
            mColorFactor = vertex.mColorFactor; | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        public void translate(double dx, double dy) { | 
				
			||||
            mPosX += dx; | 
				
			||||
            mPosY += dy; | 
				
			||||
        } | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
@ -1,195 +0,0 @@ | 
				
			||||
package io.legado.app.ui.book.read.page.delegate.curl; | 
				
			||||
 | 
				
			||||
import android.graphics.Bitmap; | 
				
			||||
import android.graphics.Canvas; | 
				
			||||
import android.graphics.Color; | 
				
			||||
import android.graphics.RectF; | 
				
			||||
 | 
				
			||||
/** | 
				
			||||
 * Storage class for page textures, blend colors and possibly some other values | 
				
			||||
 * in the future. | 
				
			||||
 * | 
				
			||||
 * @author harism | 
				
			||||
 */ | 
				
			||||
public class CurlPage { | 
				
			||||
 | 
				
			||||
    public static final int SIDE_BACK = 2; | 
				
			||||
    public static final int SIDE_BOTH = 3; | 
				
			||||
    public static final int SIDE_FRONT = 1; | 
				
			||||
 | 
				
			||||
    private int mColorBack; | 
				
			||||
    private int mColorFront; | 
				
			||||
    private Bitmap mTextureBack; | 
				
			||||
    private Bitmap mTextureFront; | 
				
			||||
    private boolean mTexturesChanged; | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * Default constructor. | 
				
			||||
     */ | 
				
			||||
    public CurlPage() { | 
				
			||||
        reset(); | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * Getter for color. | 
				
			||||
     */ | 
				
			||||
    public int getColor(int side) { | 
				
			||||
        switch (side) { | 
				
			||||
            case SIDE_FRONT: | 
				
			||||
                return mColorFront; | 
				
			||||
            default: | 
				
			||||
                return mColorBack; | 
				
			||||
        } | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * Calculates the next highest power of two for a given integer. | 
				
			||||
     */ | 
				
			||||
    private int getNextHighestPO2(int n) { | 
				
			||||
        n -= 1; | 
				
			||||
        n = n | (n >> 1); | 
				
			||||
        n = n | (n >> 2); | 
				
			||||
        n = n | (n >> 4); | 
				
			||||
        n = n | (n >> 8); | 
				
			||||
        n = n | (n >> 16); | 
				
			||||
        n = n | (n >> 32); | 
				
			||||
        return n + 1; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * Generates nearest power of two sized Bitmap for give Bitmap. Returns this | 
				
			||||
     * new Bitmap using default return statement + original texture coordinates | 
				
			||||
     * are stored into RectF. | 
				
			||||
     */ | 
				
			||||
    private Bitmap getTexture(Bitmap bitmap, RectF textureRect) { | 
				
			||||
        // Bitmap original size.
 | 
				
			||||
        int w = bitmap.getWidth(); | 
				
			||||
        int h = bitmap.getHeight(); | 
				
			||||
        // Bitmap size expanded to next power of two. This is done due to
 | 
				
			||||
        // the requirement on many devices, texture width and height should
 | 
				
			||||
        // be power of two.
 | 
				
			||||
        int newW = getNextHighestPO2(w); | 
				
			||||
        int newH = getNextHighestPO2(h); | 
				
			||||
 | 
				
			||||
        // TODO: Is there another way to create a bigger Bitmap and copy
 | 
				
			||||
        // original Bitmap to it more efficiently? Immutable bitmap anyone?
 | 
				
			||||
        Bitmap bitmapTex = Bitmap.createBitmap(newW, newH, bitmap.getConfig()); | 
				
			||||
        Canvas c = new Canvas(bitmapTex); | 
				
			||||
        c.drawBitmap(bitmap, 0, 0, null); | 
				
			||||
 | 
				
			||||
        // Calculate final texture coordinates.
 | 
				
			||||
        float texX = (float) w / newW; | 
				
			||||
        float texY = (float) h / newH; | 
				
			||||
        textureRect.set(0f, 0f, texX, texY); | 
				
			||||
 | 
				
			||||
        return bitmapTex; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * Getter for textures. Creates Bitmap sized to nearest power of two, copies | 
				
			||||
     * original Bitmap into it and returns it. RectF given as parameter is | 
				
			||||
     * filled with actual texture coordinates in this new upscaled texture | 
				
			||||
     * Bitmap. | 
				
			||||
     */ | 
				
			||||
    public Bitmap getTexture(RectF textureRect, int side) { | 
				
			||||
        switch (side) { | 
				
			||||
            case SIDE_FRONT: | 
				
			||||
                return getTexture(mTextureFront, textureRect); | 
				
			||||
            default: | 
				
			||||
                return getTexture(mTextureBack, textureRect); | 
				
			||||
        } | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * Returns true if textures have changed. | 
				
			||||
     */ | 
				
			||||
    public boolean getTexturesChanged() { | 
				
			||||
        return mTexturesChanged; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * Returns true if back siding texture exists and it differs from front | 
				
			||||
     * facing one. | 
				
			||||
     */ | 
				
			||||
    public boolean hasBackTexture() { | 
				
			||||
        return !mTextureFront.equals(mTextureBack); | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * Recycles and frees underlying Bitmaps. | 
				
			||||
     */ | 
				
			||||
    public void recycle() { | 
				
			||||
        if (mTextureFront != null) { | 
				
			||||
            mTextureFront.recycle(); | 
				
			||||
        } | 
				
			||||
        mTextureFront = Bitmap.createBitmap(1, 1, Bitmap.Config.RGB_565); | 
				
			||||
        mTextureFront.eraseColor(mColorFront); | 
				
			||||
        if (mTextureBack != null) { | 
				
			||||
            mTextureBack.recycle(); | 
				
			||||
        } | 
				
			||||
        mTextureBack = Bitmap.createBitmap(1, 1, Bitmap.Config.RGB_565); | 
				
			||||
        mTextureBack.eraseColor(mColorBack); | 
				
			||||
        mTexturesChanged = false; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * Resets this CurlPage into its initial state. | 
				
			||||
     */ | 
				
			||||
    public void reset() { | 
				
			||||
        mColorBack = Color.WHITE; | 
				
			||||
        mColorFront = Color.WHITE; | 
				
			||||
        recycle(); | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * Setter blend color. | 
				
			||||
     */ | 
				
			||||
    public void setColor(int color, int side) { | 
				
			||||
        switch (side) { | 
				
			||||
            case SIDE_FRONT: | 
				
			||||
                mColorFront = color; | 
				
			||||
                break; | 
				
			||||
            case SIDE_BACK: | 
				
			||||
                mColorBack = color; | 
				
			||||
                break; | 
				
			||||
            default: | 
				
			||||
                mColorFront = mColorBack = color; | 
				
			||||
                break; | 
				
			||||
        } | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * Setter for textures. | 
				
			||||
     */ | 
				
			||||
    public void setTexture(Bitmap texture, int side) { | 
				
			||||
        if (texture == null) { | 
				
			||||
            texture = Bitmap.createBitmap(1, 1, Bitmap.Config.RGB_565); | 
				
			||||
            if (side == SIDE_BACK) { | 
				
			||||
                texture.eraseColor(mColorBack); | 
				
			||||
            } else { | 
				
			||||
                texture.eraseColor(mColorFront); | 
				
			||||
            } | 
				
			||||
        } | 
				
			||||
        switch (side) { | 
				
			||||
            case SIDE_FRONT: | 
				
			||||
                if (mTextureFront != null) | 
				
			||||
                    mTextureFront.recycle(); | 
				
			||||
                mTextureFront = texture; | 
				
			||||
                break; | 
				
			||||
            case SIDE_BACK: | 
				
			||||
                if (mTextureBack != null) | 
				
			||||
                    mTextureBack.recycle(); | 
				
			||||
                mTextureBack = texture; | 
				
			||||
                break; | 
				
			||||
            case SIDE_BOTH: | 
				
			||||
                if (mTextureFront != null) | 
				
			||||
                    mTextureFront.recycle(); | 
				
			||||
                if (mTextureBack != null) | 
				
			||||
                    mTextureBack.recycle(); | 
				
			||||
                mTextureFront = mTextureBack = texture; | 
				
			||||
                break; | 
				
			||||
        } | 
				
			||||
        mTexturesChanged = true; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
} | 
				
			||||
					Loading…
					
					
				
		Reference in new issue