package org.telegram.ui.Components.Paint; import android.graphics.Matrix; import android.graphics.PointF; import android.graphics.RectF; import android.opengl.GLES20; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.FloatBuffer; public class Render { public static RectF RenderPath(Path path, RenderState state) { state.baseWeight = path.getBaseWeight(); state.spacing = path.getBrush().getSpacing(); state.alpha = path.getBrush().getAlpha(); state.angle = path.getBrush().getAngle(); state.scale = path.getBrush().getScale(); int length = path.getLength(); if (length == 0) { return null; } if (length == 1) { PaintStamp(path.getPoints()[0], state); } else { Point[] points = path.getPoints(); state.prepare(); for (int i = 0; i < points.length - 1; i++) { PaintSegment(points[i], points[i + 1], state); } } path.remainder = state.remainder; return Draw(state); } private static void PaintSegment(Point lastPoint, Point point, RenderState state) { double distance = lastPoint.getDistanceTo(point); Point vector = point.substract(lastPoint); Point unitVector = new Point(1.0f, 1.0f, 0.0f); float vectorAngle = Math.abs(state.angle) > 0.0f ? state.angle : (float) Math.atan2(vector.y, vector.x); float brushWeight = state.baseWeight * state.scale; double step = Math.max(1.0f, state.spacing * brushWeight); if (distance > 0.0) { unitVector = vector.multiplyByScalar(1.0 / distance); } float boldenedAlpha = Math.min(1.0f, state.alpha * 1.15f); boolean boldenHead = lastPoint.edge; boolean boldenTail = point.edge; int count = (int) Math.ceil((distance - state.remainder) / step); int currentCount = state.getCount(); state.appendValuesCount(count); state.setPosition(currentCount); Point start = lastPoint.add(unitVector.multiplyByScalar(state.remainder)); boolean succeed = true; double f = state.remainder; for (; f <= distance; f += step) { float alpha = boldenHead ? boldenedAlpha : state.alpha; succeed = state.addPoint(start.toPointF(), brushWeight, vectorAngle, alpha, -1); if (!succeed) { break; } start = start.add(unitVector.multiplyByScalar(step)); boldenHead = false; } if (succeed && boldenTail) { state.appendValuesCount(1); state.addPoint(point.toPointF(), brushWeight, vectorAngle, boldenedAlpha, -1); } state.remainder = f - distance; } private static void PaintStamp(Point point, RenderState state) { float brushWeight = state.baseWeight * state.scale; PointF start = point.toPointF(); float angle = Math.abs(state.angle) > 0.0f ? state.angle : 0.0f; float alpha = state.alpha; state.prepare(); state.appendValuesCount(1); state.addPoint(start, brushWeight, angle, alpha, 0); } private static RectF Draw(RenderState state) { RectF dataBounds = new RectF(0, 0, 0, 0); int count = state.getCount(); if (count == 0) { return dataBounds; } int vertexDataSize = 5 * Float.SIZE / 8; int capacity = vertexDataSize * (count * 4 + (count - 1) * 2); ByteBuffer bb = ByteBuffer.allocateDirect(capacity); bb.order(ByteOrder.nativeOrder()); FloatBuffer vertexData = bb.asFloatBuffer(); vertexData.position(0); state.setPosition(0); int n = 0; for (int i = 0; i < count; i++) { float x = state.read(); float y = state.read(); float size = state.read(); float angle = state.read(); float alpha = state.read(); RectF rect = new RectF(x - size, y - size, x + size, y + size); float[] points = new float[]{ rect.left, rect.top, rect.right, rect.top, rect.left, rect.bottom, rect.right, rect.bottom }; float centerX = rect.centerX(); float centerY = rect.centerY(); Matrix t = new Matrix(); t.setRotate((float) Math.toDegrees(angle), centerX, centerY); t.mapPoints(points); t.mapRect(rect); Utils.RectFIntegral(rect); dataBounds.union(rect); if (n != 0) { vertexData.put(points[0]); vertexData.put(points[1]); vertexData.put(0); vertexData.put(0); vertexData.put(alpha); n++; } vertexData.put(points[0]); vertexData.put(points[1]); vertexData.put(0); vertexData.put(0); vertexData.put(alpha); n++; vertexData.put(points[2]); vertexData.put(points[3]); vertexData.put(1); vertexData.put(0); vertexData.put(alpha); n++; vertexData.put(points[4]); vertexData.put(points[5]); vertexData.put(0); vertexData.put(1); vertexData.put(alpha); n++; vertexData.put(points[6]); vertexData.put(points[7]); vertexData.put(1); vertexData.put(1); vertexData.put(alpha); n++; if (i != count - 1) { vertexData.put(points[6]); vertexData.put(points[7]); vertexData.put(1); vertexData.put(1); vertexData.put(alpha); n++; } } vertexData.position(0); FloatBuffer coordData = vertexData.slice(); GLES20.glVertexAttribPointer(0, 2, GLES20.GL_FLOAT, false, vertexDataSize, coordData); GLES20.glEnableVertexAttribArray(0); vertexData.position(2); FloatBuffer texData = vertexData.slice(); GLES20.glVertexAttribPointer(1, 2, GLES20.GL_FLOAT, true, vertexDataSize, texData); GLES20.glEnableVertexAttribArray(1); vertexData.position(4); FloatBuffer alphaData = vertexData.slice(); GLES20.glVertexAttribPointer(2, 1, GLES20.GL_FLOAT, true, vertexDataSize, alphaData); GLES20.glEnableVertexAttribArray(2); GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, n); return dataBounds; } }