# Android Canvas 绘制贝塞尔曲线

Android 自定义 View 时，我们知道 Canvas 类有专门的 API 可以很方便地绘制贝塞尔曲线，但是通常性能较差，更不方便与图像一起处理，因为本文的目的是利用贝塞尔曲线处理图像。

``````  path.reset();
path.moveTo(p0x, p0y);//设置起点
path.moveTo(p0x, p0y);//设置终止点
path.close();

canvas.drawPath(path, paint);
``````

# OpenGL ES 绘制贝塞尔曲线

OpenGL ES 的基本绘制单位是点、线和三角形，既然可以绘制点，只需要基于上述公式计算出点，然后将其绘制出来，即可得到我们想要的贝塞尔曲线。

``````vec2 bezier_3order(in vec2 p0, in vec2 p1, in vec2 p2, in vec2 p3, in float t){
float tt = (1.0 - t) * (1.0 -t);
return tt * (1.0 -t) *p0 + 3.0 * t * tt * p1 + 3.0 * t *t *(1.0 -t) *p2 + t *t *t *p3;
}
``````

``````vec2 bezier_3order_mix(in vec2 p0, in vec2 p1, in vec2 p2, in vec2 p3, in float t)
{
vec2 q0 = mix(p0, p1, t);
vec2 q1 = mix(p1, p2, t);
vec2 q2 = mix(p2, p3, t);

vec2 r0 = mix(q0, q1, t);
vec2 r1 = mix(q1, q2, t);

return mix(r0, r1, t);
}
``````

``````#define POINTS_NUM           256 //取 256 个点
#define POINTS_PRE_TRIANGLES 3

int tDataSize = POINTS_NUM * POINTS_PRE_TRIANGLES;
float *p_tData = new float[tDataSize];

for (int i = 0; i < tDataSize; i += POINTS_PRE_TRIANGLES) {

float t0 = (float) i / tDataSize;
float t1 = (float) (i + 1) / tDataSize;
float t2 = (float) (i + 2) / tDataSize;

p_tData[i] = t0;
p_tData[i + 1] = t1;
p_tData[i + 2] = t2;
}
``````

``````//顶点着色器
#version 300 es
layout(location = 0) in float a_tData;//t 取值数组
uniform vec4 u_StartEndData;//起始点和终止点
uniform vec4 u_ControlData;//控制点
uniform mat4 u_MVPMatrix;
uniform float u_Offset;//y轴方向做一个动态偏移

vec2 bezier_3order_mix(in vec2 p0, in vec2 p1, in vec2 p2, in vec2 p3, in float t)
{
vec2 q0 = mix(p0, p1, t);
vec2 q1 = mix(p1, p2, t);
vec2 q2 = mix(p2, p3, t);

vec2 r0 = mix(q0, q1, t);
vec2 r1 = mix(q1, q2, t);

return mix(r0, r1, t);
}

void main() {

vec4 pos;
pos.w = 1.0;

vec2 p0 = u_StartEndData.xy;
vec2 p3 = u_StartEndData.zw;

vec2 p1 = u_ControlData.xy;
vec2 p2 = u_ControlData.zw;

p0.y *= u_Offset;
p1.y *= u_Offset;
p2.y *= u_Offset;
p3.y *= u_Offset;

float t = a_tData;

vec2 point = fun2(p0, p1, p2, p3, t);

if (t < 0.0) //用于绘制三角形的时候起作用，类似于绘制扇形
{
pos.xy = vec2(0.0, 0.0);
}
else
{
pos.xy = point;
}

gl_PointSize = 4.0f;//设置点的大小
gl_Position = u_MVPMatrix * pos;
}

//片段着色器
#version 300 es
precision mediump float;
layout(location = 0) out vec4 outColor;
uniform vec4 u_Color;//设置绘制三角形或者点的颜色
void main()
{
outColor = u_Color;
}

``````

``````GLUtils::setMat4(m_ProgramObj, "u_MVPMatrix", m_MVPMatrix);
GLUtils::setVec4(m_ProgramObj, "u_StartEndData", glm::vec4(-1, 0,
1, 0));
GLUtils::setVec4(m_ProgramObj, "u_ControlData", glm::vec4(-0.04f, 0.99f,
0.0f, 0.99f));
GLUtils::setVec4(m_ProgramObj, "u_Color", glm::vec4(1.0f, 0.3f, 0.0f, 1.0f));
float offset = (m_FrameIndex % 100) * 1.0f / 100;
offset = (m_FrameIndex / 100) % 2 == 1 ? (1 - offset) : offset;
GLUtils::setFloat(m_ProgramObj, "u_Offset", offset);
glDrawArrays(GL_POINTS, 0, POINTS_NUM * TRIANGLES_PER_POINT);

//旋转 180 度后再绘制一条
UpdateMVPMatrix(m_MVPMatrix, 180, m_AngleY, (float) screenW / screenH);
GLUtils::setMat4(m_ProgramObj, "u_MVPMatrix", m_MVPMatrix);
glDrawArrays(GL_POINTS, 0, POINTS_NUM * TRIANGLES_PER_POINT);
``````

``````//绘制三角形，要重新输入 t 的取值数组，使得每输出 3 个点包含一个原点，前面着色器中 t<0 时输出原点。
int tDataSize = POINTS_NUM * POINTS_PRE_TRIANGLES;
float *p_tData = new float[tDataSize];

for (int i = 0; i < tDataSize; i += POINTS_PRE_TRIANGLES) {
float t = (float) i / tDataSize;
float t1 = (float) (i + 3) / tDataSize;
p_tData[i] = t;
p_tData[i + 1] = t1;
p_tData[i + 2] = -1;
}
``````

``````glDrawArrays(GL_TRIANGLES, 0, POINTS_NUM * POINTS_PRE_TRIANGLES);
``````

``````glEnable(GL_BLEND);
glBlendFuncSeparate(GL_ONE, GL_ONE_MINUS_SRC_COLOR, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); // Screen blend mode
``````

Android_OpenGLES_3_0