1. 程式人生 > 其它 >最通俗的安卓OpenGL教學09——新增水印

最通俗的安卓OpenGL教學09——新增水印

技術標籤:OpenGL ES

新增水印的原理主要是利用OpenGL ES繪製多次,把不同的紋理如圖片、文字等繪製到紋理或視窗上,達到新增水印的效果,需要注意的地方就是要確定好需要在螢幕上繪製的位置座標。

新增水印的原理:

利用OpenGL ES繪製多次,把不同的紋理如圖片、文字等繪製到紋理或視窗上,達到新增水印的效果。

  • 圖片水印,直接繫結bitmap到紋理;
  • 文字水印,先將文字轉為bitmap,再繫結到紋理。

先看效果:

新增水印的步驟:

  1. 首先確認水印新增的位置
  2. 設定水印的大小
  3. 根據水印圖片長寬比計算水印的頂點座標
  4. 接下來就是跟繪製圖片紋理沒區別了

1.在頂點座標數組裡預留水印的座標點

    //頂點座標
    float[] vertexData = {
            -1f, -1f,
            1f, -1f,
            -1f, 1f,
            1f, 1f,
            //用來 加一個 圖片水印 到左上角
            -1f, 0.5f,
            0f, 0.5f,
            -1f, 1f,
            0f, 1f,

            //用來 加一個文字水印 到右下角
            0f, -1f,
            1f, -1f
, 0f, -0.8f, 1f, -0.8f };

2.獲取到水印的紋理ID

這裡直接給出個工具類:

package com.york.media.opengl.egl;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.
Paint; import android.opengl.GLES20; import android.opengl.GLUtils; import java.nio.ByteBuffer; /** * author : York * date : 2020/12/20 20:13 * desc : 紋理工具 */ public class TextureUtils { /** * 載入圖片為 2D紋理 * * @param context 上下文 * @param rawID 資源ID * @return 生成的紋理 */ public static int createImageTexture(Context context, int rawID) { //生產一個紋理 int[] textureIds = new int[1]; GLES20.glGenTextures(1, textureIds, 0); //繫結為 2D紋理 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); Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), rawID); //繫結 bitmap到 textureIds[0] 這個2D紋理上 GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0); //退出 紋理的設定,進入下一環節 GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0); return textureIds[0]; } /** * 設定文字水印 * * @param text 文字內容 * @param textSize 文字大小 * @param textColor 文字顏色 * @param bgColor 文字背景顏色 #00000000 * @param padding 文字與邊距距離 * @return 文字水印的 bitmap */ 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; } /** * 載入 bitmap為 2D紋理 * * @param bitmap 圖片 bitmap * @return 紋理 */ 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]; } }

3.改變從頂點陣列取點的位置

vertexBuffer.position(index);
GLES20.glVertexAttribPointer(vPosition, 2, GLES20.GL_FLOAT, false, 8,index);

index:記憶體中起始位置(位元組)

也可以從VBO裡取點的位置

  		//圖片水印
        //從VBO中獲取圖片水印的座標,並使能
        GLES20.glEnableVertexAttribArray(vPosition);
        GLES20.glVertexAttribPointer(vPosition, 2, GLES20.GL_FLOAT, false, 8,32);
        GLES20.glEnableVertexAttribArray(fPosition);
        GLES20.glVertexAttribPointer(fPosition, 2, GLES20.GL_FLOAT, false, 8,vertexData.length * 4);

4.繫結紋理進行繪製

 		GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, bitmapTextureId);
        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);

5.完整程式碼

package com.york.media.opengl.demo.bitmap;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.opengl.GLES20;
import android.opengl.GLUtils;

import com.york.media.opengl.R;
import com.york.media.opengl.egl.TextureUtils;
import com.york.media.opengl.egl.YGLSurfaceView;
import com.york.media.opengl.egl.YShaderUtil;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;

/**
 * author : York
 * date   : 2020/12/21 4:16
 * desc   :
 */
public class YWaterMarkRender implements YGLSurfaceView.YGLRender {
    private final Context mContext;
    private final FloatBuffer vertexBuffer;
    private final FloatBuffer fragmentBuffer;
    private int program;
    private int vPosition;
    private int fPosition;
    private int[] textureIds;
    private int bitmapTextureId;
    private int textTextureId;
    private int vboId;

    //頂點座標
    float[] vertexData = {
            -1f, -1f,
            1f, -1f,
            -1f, 1f,
            1f, 1f,
            //用來 加一個 圖片水印 到左上角
            -1f, 0.5f,
            0f, 0.5f,
            -1f, 1f,
            0f, 1f,

            //用來 加一個文字水印 到右下角
            0f, -1f,
            1f, -1f,
            0f, -0.8f,
            1f, -0.8f
    };
    //紋理座標
    float[] fragmentData = {
            0f, 1f,
            1f, 1f,
            0f, 0f,
            1f, 0f
    };

    public YWaterMarkRender(Context context) {
        this.mContext = context;
        //讀取頂點座標
        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);


    }

    @Override
    public void onSurfaceCreated() {
        //載入頂點著色器 shader
        String vertexSource = YShaderUtil.getRawResource(mContext, R.raw.screen_vert);
        //載入片元著色器 shader
        String fragmentSource = YShaderUtil.getRawResource(mContext, R.raw.screen_frag);
        //獲取源程式
        program = YShaderUtil.createProgram(vertexSource, fragmentSource);
        //從渲染程式中得到著頂點色器中的屬性
        vPosition = GLES20.glGetAttribLocation(program, "vPosition");
        //從渲染程式中得到片元著色器中的屬性
        fPosition = GLES20.glGetAttribLocation(program, "fPosition");

        //設定文字支援透明
        GLES20.glEnable(GLES20.GL_BLEND);
        GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA);

        int [] vbo_s = new int[1];
        GLES20.glGenBuffers(1, vbo_s, 0);
        vboId = vbo_s[0];

        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vboId);
        GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, vertexData.length * 4 + fragmentData.length * 4, null, GLES20. GL_STATIC_DRAW);
        GLES20.glBufferSubData(GLES20.GL_ARRAY_BUFFER, 0, vertexData.length * 4, vertexBuffer);
        GLES20.glBufferSubData(GLES20.GL_ARRAY_BUFFER, vertexData.length * 4, fragmentData.length * 4, fragmentBuffer);
        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);


        //建立 1個紋理,放入到 int [] textureIds, 一共有 30多個 紋理
        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);
        //解綁紋理 指的是離開對 紋理的配置,進入下一個狀態
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);

        bitmapTextureId = TextureUtils.createImageTexture(mContext, R.drawable.bjwd);

        Bitmap txtBitmap = TextureUtils.createTextImage("York的IT旅途", 36, "#ff0000", "#00000000", 0);
        textTextureId = TextureUtils.loadBitmapTexture(txtBitmap);
        txtBitmap.recycle();
    }

    @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);

        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vboId);

        GLES20.glEnableVertexAttribArray(vPosition);
        GLES20.glVertexAttribPointer(vPosition, 2, GLES20.GL_FLOAT, false, 8,0);

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

        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureIds[0]);
        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);

        //繫結 textureIds[0] 到已啟用的 2D紋理 GL_TEXTURE0上
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureIds[0]);
        //獲取圖片的 bitmap
        Bitmap bitmap = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.byg);
        //繫結 bitmap 到textureIds[0]紋理
        GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
        bitmap.recycle();//用完及時回收
        //繪製原圖片
        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
        //解綁 2D紋理
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);

        //圖片水印
        //從VBO中獲取圖片水印的座標,並使能
        GLES20.glEnableVertexAttribArray(vPosition);
        GLES20.glVertexAttribPointer(vPosition, 2, GLES20.GL_FLOAT, false, 8,32);
        GLES20.glEnableVertexAttribArray(fPosition);
        GLES20.glVertexAttribPointer(fPosition, 2, GLES20.GL_FLOAT, false, 8,vertexData.length * 4);

        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, bitmapTextureId);
        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);

        //文字水印
        //從VBO中獲取圖片水印的座標,並使能
        GLES20.glEnableVertexAttribArray(vPosition);
        GLES20.glVertexAttribPointer(vPosition, 2, GLES20.GL_FLOAT, false, 8,64);
        GLES20.glEnableVertexAttribArray(fPosition);
        GLES20.glVertexAttribPointer(fPosition, 2, GLES20.GL_FLOAT, false, 8,vertexData.length * 4);

        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textTextureId);
        //繪製水印
        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
        //解綁紋理
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);


        //解綁 VBO
        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
    }
}

看: