最通俗的安卓OpenGL教學09——新增水印
阿新 • • 發佈:2020-12-22
技術標籤:OpenGL ES
新增水印的原理主要是利用OpenGL ES繪製多次,把不同的紋理如圖片、文字等繪製到紋理或視窗上,達到新增水印的效果,需要注意的地方就是要確定好需要在螢幕上繪製的位置座標。
新增水印的原理:
利用OpenGL ES繪製多次,把不同的紋理如圖片、文字等繪製到紋理或視窗上,達到新增水印的效果。
- 圖片水印,直接繫結bitmap到紋理;
- 文字水印,先將文字轉為bitmap,再繫結到紋理。
先看效果:
新增水印的步驟:
- 首先確認水印新增的位置
- 設定水印的大小
- 根據水印圖片長寬比計算水印的頂點座標
- 接下來就是跟繪製圖片紋理沒區別了
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);
}
}
看: