OpenGLES添加水印

版权声明:本文为卫伟学习总结文章,转载请注明出处!
原理:把文字变成图片,用多纹理渲染的方式来添加。其他图片水印直接是多纹理渲染即可。即添加水印=多个纹理绘制在同一个surface上。
理解了添加水印的原理,不管是视频水印还是图片水印都是很简单的了,只是使用的纹理不一样而已。如果是绘制文字水印的话,则需要将文字生成图片,然后将图片使用纹理绘制即可。
那么怎样将多个纹理添加到同一个surface上?
简单示例代码:

public class WlEncodecRender implements WLEGLSurfaceView.WlGLRender {

private Context context;
//顶点坐标
private float[] vertexData = { // in counterclockwise order:
        -1f, -1f, 0.0f, // bottom left
        1f, -1f, 0.0f,// bottom right
        -1f, 1f, 0.0f,// top left
        1f, 1f,0.0f,// top right

        0f, 0f, 0f, // 水印预留位置
        0f, 0f, 0f,
        0f, 0f, 0f,
        0f, 0f, 0f
};

// 纹理坐标 对应顶点坐标  与之映射
private float[] fragmentData = {
        0f, 1f, 0.0f, // bottom left
        1f, 1f, 0.0f, // bottom right
        0f, 0f, 0.0f, // top left
        1f, 0f, 0.0f, // top right
};

// 位置
private FloatBuffer vertexBuffer;
// 纹理
private FloatBuffer fragmentBuffer;

private int program;
private int vPosition;
// 纹理位置
private int fPosition;
// 纹理 默认第0个位置 可以不获取
private int textureid;

// vbo id
private int vboId;

private int fboTextureId;

private Bitmap bitmap;
private int waterTextureId;

private int bitmapTextureid;


public WlEncodecRender(Context context, int textureid) {
    this.context = context;
    this.textureid = textureid;

    initWater(); 

    vertexBuffer = ByteBuffer.allocateDirect(vertexData.length * 4)
            .order(ByteOrder.nativeOrder())
            .asFloatBuffer()
            .put(vertexData);
    vertexBuffer.position(0);

    fragmentBuffer = ByteBuffer.allocateDirect(fragmentData.length * 4)
            .order(ByteOrder.nativeOrder())
            .asFloatBuffer()
            .put(fragmentData);
    fragmentBuffer.position(0);

}

private void initWater() {
    bitmap = WlShaderUtil.createTextImage("我是水印",40,"#fff000","#00000000",0);

    // 设置位置 根据需求自己配置
    float r = 1.0f * bitmap.getWidth() / bitmap.getHeight();
    float w = r * 0.1f;
    vertexData[12] = 0.8f - w;
    vertexData[13] = -0.8f;
    vertexData[14] = 0;

    vertexData[15] = 0.8f;
    vertexData[16] = -0.8f;
    vertexData[17] = 0;

    vertexData[18] = 0.8f - w;
    vertexData[19] = -0.7f;
    vertexData[20] = 0;

    vertexData[21] = 0.8f;
    vertexData[22] = -0.7f;
    vertexData[23] = 0;
}

@Override
public void onSurfaceCreated() {
    // 启用透明
    GLES20.glEnable(GLES20.GL_BLEND);
    GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA,GLES20.GL_ONE_MINUS_SRC_ALPHA);
    String vertexSource = WlShaderUtil.getRawResource(context, R.raw.vertex_shader_screen);
    String fragmentSource = WlShaderUtil.getRawResource(context, R.raw.fragment_shader_screen);

    program = WlShaderUtil.createProgram(vertexSource, fragmentSource);

    vPosition = GLES20.glGetAttribLocation(program, "v_Position");
    fPosition = GLES20.glGetAttribLocation(program, "f_Position");

    // 创建vbo
    createVBO();

    //创建水印纹理
    bitmapTextureid =  createWaterTextureId();
}

/**
 * 创建vbo
 */
private void createVBO() {
    //1. 创建VBO
    int [] vbos = new int[1];
    GLES20.glGenBuffers(1, vbos, 0);
    vboId = vbos[0];
    //2. 绑定VBO
    GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vboId);
    //3. 分配VBO需要的缓存大小
    GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, vertexData.length * 4 + fragmentData.length * 4, null, GLES20. GL_STATIC_DRAW);
    //4. 为VBO设置顶点数据的值
    GLES20.glBufferSubData(GLES20.GL_ARRAY_BUFFER, 0, vertexData.length * 4, vertexBuffer);
    GLES20.glBufferSubData(GLES20.GL_ARRAY_BUFFER, vertexData.length * 4, fragmentData.length * 4, fragmentBuffer);
    //5. 解绑VBO
    GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
}

/**
 * 创建水印纹理
 */
private int createWaterTextureId() {
    int[] textureIds = new int[1];
    // 创建纹理
    GLES20.glGenTextures(1,textureIds,0);
    waterTextureId = textureIds[0];
    // 绑定纹理
    GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, waterTextureId);
    //环绕(超出纹理坐标范围)  (s==x t==y GL_REPEAT 重复)
    GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_REPEAT);
    GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_REPEAT);
    //过滤(纹理像素映射到坐标点)  (缩小、放大:GL_LINEAR线性)
    GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
    GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);

    ByteBuffer bitmapBuffer = ByteBuffer.allocate(bitmap.getHeight() * bitmap.getWidth() * 4);//RGBA
    bitmap.copyPixelsToBuffer(bitmapBuffer);
    //将bitmapBuffer位置移动到初始位置
    bitmapBuffer.flip();

    //设置内存大小绑定内存地址
    GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, bitmap.getWidth(), bitmap.getHeight(),
            0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, bitmapBuffer);

    //解绑纹理
    GLES20.glBindTexture(GLES20.GL_TEXTURE_2D,0);
    return textureIds[0];
}

@Override
public void onSurfaceChanged(int width, int height) {
    GLES20.glViewport(0, 0, width, height);
}

@Override
public void onDrawFrame() {
    // 清空颜色
    GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
    // 设置北京颜色
    GLES20.glClearColor(1f,0f, 0f, 1f);

    //使用程序
    GLES20.glUseProgram(program);

    // 设置纹理
    // 绑定渲染纹理  默认是第0个位置
    GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureid);

    GLES20.glEnableVertexAttribArray(vPosition);
    GLES20.glEnableVertexAttribArray(fPosition);

    // 使用VBO设置纹理和顶点值
    useVboSetVertext();

    GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);


    //bitmap
    GLES20.glBindTexture(GLES20.GL_TEXTURE_2D,bitmapTextureid);

    GLES20.glEnableVertexAttribArray(vPosition);
    GLES20.glVertexAttribPointer(vPosition,2,GLES20.GL_FLOAT,false,12,
            48);
    GLES20.glEnableVertexAttribArray(fPosition);
    GLES20.glVertexAttribPointer(fPosition, 2, GLES20.GL_FLOAT, false, 12,
            vertexData.length * 4);

    GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);

    GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
    GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);

}

/**
 * 使用vbo设置顶点位置
 */
private void useVboSetVertext() {
    //1. 绑定VBO
    GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vboId);
    //2. 设置顶点数据
    GLES20.glVertexAttribPointer(vPosition, 2, GLES20.GL_FLOAT, false, 8,
            0);
    GLES20.glVertexAttribPointer(fPosition, 2, GLES20.GL_FLOAT, false, 8,
            vertexData.length * 4);
    //3. 解绑VBO
    GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
}

ShaderUtil.java

/**
 * 根据文字创建图片
 */
public static Bitmap createTextImage(String text, int textSize, String textColor, String bgColor, int padding) {
    Paint paint = new Paint();
    paint.setColor(Color.parseColor(textColor));
    paint.setTextSize(textSize);
    paint.setStyle(Paint.Style.FILL);
    paint.setAntiAlias(true);

    float width = paint.measureText(text,0,text.length());

    float top = paint.getFontMetrics().top;
    float bottom = paint.getFontMetrics().bottom;

    Bitmap bm = Bitmap.createBitmap((int) (width + padding * 2), (int) ((bottom - top) + padding * 2), Bitmap.Config.ARGB_8888);
    Canvas canvas = new Canvas(bm);

    canvas.drawColor(Color.parseColor(bgColor));
    canvas.drawText(text, padding, - top + padding, paint);
    return bm;
}

public static int loadBitmapTexture(Bitmap bitmap) {
    int[] textureIds = new int[1];
    GLES20.glGenTextures(1,textureIds,0);
    GLES20.glBindTexture(GLES20.GL_TEXTURE_2D,textureIds[0]);

    GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_REPEAT);
    GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_REPEAT);
    GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
    GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);

    ByteBuffer bitmapBuffer = ByteBuffer.allocate(bitmap.getHeight() * bitmap.getWidth() * 4);
    bitmap.copyPixelsToBuffer(bitmapBuffer);
    bitmapBuffer.flip();

    GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, bitmap.getWidth(),
            bitmap.getHeight(), 0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, bitmapBuffer);
    return textureIds[0];
}

在当前GL_TEXTURE_2D纹理绘制之后在glBindTexture绑定水印的纹理绘制即可。需要注意的几个点:

    1. 需要开启透明,不然没有透明效果。
//启用透明
GLES20.glEnable(GLES20.GL_BLEND);
GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, 
GLES20.GL_ONE_MINUS_SRC_ALPHA);
    1. 如果是使用fbo来离屏渲染OES纹理添加水印,需要在开个fbo来使用2D绘制OES的纹理和添加水印,然后另外用一个Render来绘制fbo纹理,即:fbo里面OES和2d不能混用,不然不会起作用。
    1. 使用VBO需要注意点的位置。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,723评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,485评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,998评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,323评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,355评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,079评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,389评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,019评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,519评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,971评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,100评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,738评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,293评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,289评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,517评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,547评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,834评论 2 345

推荐阅读更多精彩内容