Cara menggunakan Android Media Effects dengan OpenGL ES
Indonesian (Bahasa Indonesia) translation by Sandi Muamar (you can also view the original English article)
Android Media Effects framework memungkinkan developer untuk dengan mudah menerapkan banyak impresif untuk foto dan video. Rangka menggunakan GPU untuk melakukan semua gambar pengolahan operasi, itu hanya dapat menerima tekstur OpenGL sebagai input. Dalam tutorial ini, Anda akan belajar bagaimana menggunakan OpenGL ES 2.0 untuk mengubah resource drawable menjadi sebuah tekstur dan kemudian menggunakan framework untuk menerapkan berbagai efek untuk itu.
Prasyarat
Untuk mengikuti tutorial ini, Anda harus mempunyai:
- IDE yang mendukung pengembangan aplikasi Android. Jika Anda tidak memiliki satu, mendapatkan versi terbaru dari Android Studio dari Android pengembang situs.
- sebuah perangkat yang menjalankan Android 4.0 + dan GPU yang mendukung OpenGL ES 2.0.
- pemahaman dasar tentang OpenGL.
1. menyiapkan lingkungan OpenGL ES
Langkah 1: Buat GLSurfaceView
Untuk menampilkan grafis OpenGL dalam aplikasi Anda, Anda harus menggunakan GLSurfaceView
objek. Seperti View
lain, Anda dapat menambahkannya ke Activity
atau Fragment
oleh mendefinisikan dalam tata letak file XML atau membuat contoh kode.
Dalam tutorial ini, Anda akan memiliki sebuah objek GLSurfaceView
sebagai satu-satunya View
dalam Activity
Anda. Oleh karena itu, membuat kode lebih sederhana. Setelah dibuat, passing ke metode setContentView
sehingga ini mengisi seluruh layar. Metode onCreate
Activity's
Anda akan terlihat seperti ini:
1 |
protected void onCreate(Bundle savedInstanceState) { |
2 |
super.onCreate(savedInstanceState); |
3 |
|
4 |
GLSurfaceView view = new GLSurfaceView(this); |
5 |
setContentView(view); |
6 |
}
|
Karena kMedia Effects framework hanya mendukung OpenGL ES 2.0 atau lebih tinggi, melewati nilai 2
metode setEGLContextClientVersion
.
1 |
view.setEGLContextClientVersion(2); |
Untuk memastikan bahwa GLSurfaceView
menuliskan isinya hanya bila diperlukan, melewati nilai RENDERMODE_WHEN_DIRTY
setRenderMode
metode.
1 |
view.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY); |
Langkah 2: Buat sebuah Renderer
GLSurfaceView.Renderer
bertanggung jawab untuk drawing isi GLSurfaceView
.
Membuat sebuah class baru yang mengimplementasikan interface GLSurfaceView.Renderer.
Aku akan memanggil kelas ini EffectsRenderer
. Setelah menambahkan konstruktor dan mengganti semua metode antarmuka, kelas akan terlihat seperti ini:
1 |
public class EffectsRenderer implements GLSurfaceView.Renderer { |
2 |
|
3 |
public EffectsRenderer(Context context){ |
4 |
super(); |
5 |
}
|
6 |
|
7 |
@Override
|
8 |
public void onSurfaceCreated(GL10 gl, EGLConfig config) { |
9 |
}
|
10 |
|
11 |
@Override
|
12 |
public void onSurfaceChanged(GL10 gl, int width, int height) { |
13 |
}
|
14 |
|
15 |
@Override
|
16 |
public void onDrawFrame(GL10 gl) { |
17 |
}
|
18 |
}
|
Kembali ke Activity
Anda dan memanggil metode setRenderer
sehingga GLSurfaceView
menggunakan renderer kustom.
1 |
view.setRenderer(new EffectsRenderer(this)); |
Langkah 3: Mengedit Manifest
Jika Anda berencana untuk menerbitkan aplikasi Anda di Google Play, tambahkan baris berikut ke AndroidManifest.xml:
1 |
<uses-feature android:glEsVersion="0x00020000" android:required="true" /> |
Hal ini membuat yakin bahwa aplikasi Anda hanya dapat diinstal pada perangkat yang mendukung OpenGL ES 2.0. Lingkungan OpenGL kini siap.
2. menciptakan sebuah OpenGL Pesawat
Langkah 1: Menentukan simpul
GLSurfaceView
tidak dapat menampilkan foto langsung. Foto telah dikonversi menjadi sebuah tekstur dan diterapkan ke bentuk OpenGL yang pertama. Dalam tutorial ini, kita akan menciptakan sebuah pesawat 2D yang memiliki empat simpul. Demi kesederhanaan, mari kita membuat sebuah persegi. Membuat baru kelas, Square
, untuk mewakili alun-alun.
1 |
public class Square { |
2 |
|
3 |
}
|
Sistem koordinat OpenGL default berawal di pusat. Sebagai akibatnya, koordinat empat sudut alun-alun kami, yang sisi-sisinya adalah dua unit panjang, akan:
- sudut kiri bawah di (-1, -1)
- sudut kanan di bawah (1, -1)
- pojok kanan atas di (1, 1)
- sudut kiri atas pada (-1, 1)
Semua objek yang kita buat menggunakan OpenGL harus terdiri dari segitiga. Menggambar square, kita membutuhkan dua segitiga dengan tepi yang umum. Ini berarti bahwa koordinat segitiga akan:
segitiga 1: (-1, -1), (1, -1) dan (-1, 1)
segitiga 2: (1, -1), (-1, 1), dan (1, 1)
Membuat float
array yang mewakili simpul tersebut.
1 |
private float vertices[] = { |
2 |
-1f, -1f, |
3 |
1f, -1f, |
4 |
-1f, 1f, |
5 |
1f, 1f, |
6 |
};
|
Untuk peta tekstur ke alun-alun, Anda perlu menentukan koordinat Vertex tekstur. Tekstur mengikuti sistem koordinat di mana nilai yang meningkat koordinat y sebagai Anda pergi lebih tinggi. Membuat lain array yang mewakili Vertex tekstur.
1 |
private float textureVertices[] = { |
2 |
0f,1f, |
3 |
1f,1f, |
4 |
0f,0f, |
5 |
1f,0f |
6 |
};
|
Langkah 2: Buat objek Buffer
Array koordinat perlu dikonversi menjadi byte buffer sebelum OpenGL dapat menggunakannya. Mari kita menyatakan buffer ini pertama.
1 |
private FloatBuffer verticesBuffer; |
2 |
private FloatBuffer textureBuffer; |
Menulis kode untuk menginisialisasi buffer ini dalam metode baru yang disebut initializeBuffers
. Menggunakan ByteBuffer.allocateDirect
metode untuk menciptakan buffer. Karena float
menggunakan 4 byte, Anda perlu mengalikan ukuran array dengan nilai 4.
Selanjutnya, gunakan ByteBuffer.nativeOrder
untuk menentukan urutan byte mendasarinya platform native, dan mengatur urutan buffer untuk nilai. Menggunakan metode asFloatBuffer
untuk mengkonversi contoh ByteBuffer
ke FloatBuffer
. Setelah FloatBuffer
dibuat, menggunakan put
method untuk memuat array ke buffer. Akhirnya, menggunakan metode position
untuk memastikan bahwa buffer membaca dari awal.
Isi dari metode initializeBuffers
yang akan terlihat seperti ini:
1 |
private void initializeBuffers(){ |
2 |
ByteBuffer buff = ByteBuffer.allocateDirect(vertices.length * 4); |
3 |
buff.order(ByteOrder.nativeOrder()); |
4 |
verticesBuffer = buff.asFloatBuffer(); |
5 |
verticesBuffer.put(vertices); |
6 |
verticesBuffer.position(0); |
7 |
|
8 |
buff = ByteBuffer.allocateDirect(textureVertices.length * 4); |
9 |
buff.order(ByteOrder.nativeOrder()); |
10 |
textureBuffer = buff.asFloatBuffer(); |
11 |
textureBuffer.put(textureVertices); |
12 |
textureBuffer.position(0); |
13 |
}
|
Step 3: Membuat Shaders
Saatnya untuk menulis shaders Anda sendiri. Shaders hanyalah program C sederhana yang dijalankan oleh GPU untuk memproses vertex setiap individu. Untuk tutorial ini, Anda harus membuat dua shaders, vertex shader dan shader fragmen.
C kode untuk vertex shader adalah:
1 |
attribute vec4 aPosition; |
2 |
attribute vec2 aTexPosition; |
3 |
varying vec2 vTexPosition; |
4 |
void main() { |
5 |
gl_Position = aPosition; |
6 |
vTexPosition = aTexPosition; |
7 |
};
|
C kode untuk shader fragment adalah:
1 |
precision mediump float; |
2 |
uniform sampler2D uTexture; |
3 |
varying vec2 vTexPosition; |
4 |
void main() { |
5 |
gl_FragColor = texture2D(uTexture, vTexPosition); |
6 |
};
|
Jika Anda sudah tahu OpenGL, kode ini harus akrab bagi Anda karena itu umum di semua platform. Jika Anda tidak, untuk memahami program ini Anda harus merujuk ke dokumentasi OpenGL. Berikut adalah penjelasan singkat untuk Anda mulai:
- Vertex shader bertanggung jawab untuk menarik Vertex individu.
aPosition
adalah variabel yang akan terikat untukFloatBuffer
yang berisi koordinat Vertex. Demikian pula,aTexPosition
adalah variabel yang akan terikat untukFloatBuffer
yang berisi koordinat tekstur.gl_Position
adalah variabel OpenGL built-in dan mewakili posisi vertex masing-masing.vTexPosition
adalah sebuah variabel yangvarying
, yang nilainya hanya diteruskan ke shader fragment. - Dalam tutorial ini, shader fragment bertanggung jawab untuk mewarnai square. Ini mengambil warna dari tekstur menggunakan metode
texture2D
dan menetapkan mereka fragment menggunakan variabel built-in yang bernamagl_FragColor
.
Kode shader perlu diwakili sebagai objek String
di kelas.
1 |
private final String vertexShaderCode = |
2 |
"attribute vec4 aPosition;" + |
3 |
"attribute vec2 aTexPosition;" + |
4 |
"varying vec2 vTexPosition;" + |
5 |
"void main() {" + |
6 |
" gl_Position = aPosition;" + |
7 |
" vTexPosition = aTexPosition;" + |
8 |
"}"; |
9 |
|
10 |
private final String fragmentShaderCode = |
11 |
"precision mediump float;" + |
12 |
"uniform sampler2D uTexture;" + |
13 |
"varying vec2 vTexPosition;" + |
14 |
"void main() {" + |
15 |
" gl_FragColor = texture2D(uTexture, vTexPosition);" + |
16 |
"}"; |
Step 4: Membuat Program
Menciptakan metode baru yang disebut initializeProgram
untuk membuat OpenGL program setelah kompilasi dan menghubungkan shaders.
Gunakan glCreateShader
untuk membuat objek shader dan mengembalikan referensi ke itu dalam form int
. Untuk membuat vertex shader, melewati nilai GL_VERTEX_SHADER
untuk itu. Demikian pula, untuk membuat shader fragment, melewati nilai GL_FRAGMENT_SHADER
untuk itu. Selanjutnya menggunakan glShaderSource
untuk mengasosiasikan kode shader sesuai dengan shader. Gunakan glCompileShader
untuk mengkompilasi kode shader.
Setelah kompilasi shaders kedua, membuat sebuah program baru yang menggunakan glCreateProgram
. Seperti glCreateShader
, ini juga mengembalikan int
sebagai referensi untuk program. Memanggil glAttachShader
untuk melampirkan shaders ke program. Akhirnya, hubungi glLinkProgram
untuk menghubungkan program.
Metode Anda dan variabel yang terkait akan terlihat seperti ini:
1 |
private int vertexShader; |
2 |
private int fragmentShader; |
3 |
private int program; |
4 |
|
5 |
private void initializeProgram(){ |
6 |
vertexShader = GLES20.glCreateShader(GLES20.GL_VERTEX_SHADER); |
7 |
GLES20.glShaderSource(vertexShader, vertexShaderCode); |
8 |
GLES20.glCompileShader(vertexShader); |
9 |
|
10 |
fragmentShader = GLES20.glCreateShader(GLES20.GL_FRAGMENT_SHADER); |
11 |
GLES20.glShaderSource(fragmentShader, fragmentShaderCode); |
12 |
GLES20.glCompileShader(fragmentShader); |
13 |
|
14 |
program = GLES20.glCreateProgram(); |
15 |
GLES20.glAttachShader(program, vertexShader); |
16 |
GLES20.glAttachShader(program, fragmentShader); |
17 |
|
18 |
GLES20.glLinkProgram(program); |
19 |
}
|
Anda mungkin telah memperhatikan bahwa OpenGL metode (metode diawali dengan gl
) milik kelas GLES20
. Hal ini karena kami menggunakan OpenGL ES 2.0. Jika Anda ingin menggunakan versi yang lebih tinggi, maka Anda akan harus menggunakan kelas-kelas GLES30
atau GLES31
.
Langkah 5: Menggambar persegi
Buat metode baru yang disebut draw
untuk benar-benar menarik alun-alun yang menggunakan dan Vertex shaders kita didefinisikan sebelumnya.
Berikut adalah apa yang perlu Anda lakukan dalam metode ini:
- Gunakan
glBindFramebuffer
untuk membuat objek penyangga bernama bingkai (sering disebut FBO).
- Gunakan
glUseProgram
untuk mulai menggunakan program kami hanya dihubungkan. - Melewati nilai
GL_BLEND
glDisable
untuk menonaktifkan pencampuran warna sementara render.
- Gunakan
glGetAttribLocation
untuk mendapatkan pegangan ke variabelaPosition
danaTexPosition
yang disebutkan dalam kode shader vertex.
- Gunakan
glGetUniformLocation
untuk mendapatkan pegangan untukuTexture
konstan yang disebutkan dalam kode shader fragmen.
- Gunakan
glVertexAttribPointer
untuk mengasosiasikan peganganaPosition
danaTexPosition
denganverticesBuffer
dantextureBuffer
masing-masing.
- Gunakan
glBindTexture
untuk mengikat tekstur (melewati sebagai argumen untuk metodedraw
) shader fragment.
- Hapus Isi
GLSurfaceView
yang menggunakanglClear
.
- Akhirnya, menggunakan metode
glDrawArrays
untuk benar-benar menggambar dua segitiga (dan dengan demikian alun-alun).
Kode untuk metode draw
akan terlihat seperti ini:
1 |
public void draw(int texture){ |
2 |
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0); |
3 |
GLES20.glUseProgram(program); |
4 |
GLES20.glDisable(GLES20.GL_BLEND); |
5 |
|
6 |
int positionHandle = GLES20.glGetAttribLocation(program, "aPosition"); |
7 |
int textureHandle = GLES20.glGetUniformLocation(program, "uTexture"); |
8 |
int texturePositionHandle = GLES20.glGetAttribLocation(program, "aTexPosition"); |
9 |
|
10 |
GLES20.glVertexAttribPointer(texturePositionHandle, 2, GLES20.GL_FLOAT, false, 0, textureBuffer); |
11 |
GLES20.glEnableVertexAttribArray(texturePositionHandle); |
12 |
|
13 |
GLES20.glActiveTexture(GLES20.GL_TEXTURE0); |
14 |
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texture); |
15 |
GLES20.glUniform1i(textureHandle, 0); |
16 |
|
17 |
GLES20.glVertexAttribPointer(positionHandle, 2, GLES20.GL_FLOAT, false, 0, verticesBuffer); |
18 |
GLES20.glEnableVertexAttribArray(positionHandle); |
19 |
|
20 |
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); |
21 |
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); |
22 |
}
|
Tambahkan konstruktor kelas untuk menginisialisasi buffer dan program pada saat pembuatan objek.
1 |
public Square(){ |
2 |
initializeBuffers(); |
3 |
initializeProgram(); |
4 |
}
|
3. render OpenGL pesawat dan tekstur
Saat ini, kami renderer tidak apa-apa. Kita perlu mengubah hal itu sehingga ia dapat membuat pesawat yang kita buat di langkah sebelumnya.
Tapi pertama-tama, mari kita membuat Bitmap
. Tambahkan foto ke folder res/drawable proyek Anda. Saya menggunakan file yang disebut forest.jpg. Gunakan BitmapFactory
untuk mengkonversi foto ke sebuah objek Bitmap
. Juga, menyimpan dimensi objek Bitmap
variabel terpisah.
Mengubah konstruktor kelas EffectsRenderer
sehingga memiliki isi sebagai berikut:
1 |
private Bitmap photo; |
2 |
private int photoWidth, photoHeight; |
3 |
public EffectsRenderer(Context context){ |
4 |
super(); |
5 |
photo = BitmapFactory.decodeResource(context.getResources(), R.drawable.forest); |
6 |
photoWidth = photo.getWidth(); |
7 |
photoHeight = photo.getHeight(); |
8 |
}
|
Buar metode baru yang disebut generateSquare
untuk mengkonversi bitmap menjadi sebuah tekstur dan menginisialisasi objek Square
. Anda juga akan memerlukan serangkaian bilangan bulat untuk menahan referensi untuk tekstur OpenGL. Gunakan glGenTextures
untuk menginisialisasi array dan glBindTexture
untuk mengaktifkan tekstur di indeks 0
.
Selanjutnya, gunakan glTexParameteri
untuk mengatur berbagai properti yang memutuskan bagaimana tekstur dituliskan:
- Set
GL_TEXTURE_MIN_FILTER
(fungsi minifying) danGL_TEXTURE_MAG_FILTER
(pembesar fungsi) untukGL_LINEAR
untuk memastikan bahwa tekstur tampak halus, bahkan ketika itu telah menggeliat atau menyusut.
- Mengatur
GL_TEXTURE_WRAP_S
danGL_TEXTURE_WRAP_T
untukGL_CLAMP_TO_EDGE
sehingga tekstur tidak pernah terulang lagi.
Akhirnya, menggunakan texImage2D
metode untuk memetakan Bitmap
untuk tekstur. Pelaksanaan metode generateSquare
yang akan terlihat seperti ini:
1 |
private int textures[] = new int[2]; |
2 |
private Square square; |
3 |
|
4 |
private void generateSquare(){ |
5 |
GLES20.glGenTextures(2, textures, 0); |
6 |
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textures[0]); |
7 |
|
8 |
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR); |
9 |
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR); |
10 |
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE); |
11 |
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE); |
12 |
|
13 |
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, photo, 0); |
14 |
square = new Square(); |
15 |
}
|
Setiap kali perubahan dimensi GLSurfaceView
, metode onSurfaceChanged
Renderer
dipanggil. Berikut ini adalah dimana Anda harus memanggil glViewPort
untuk menentukan dimensi baru viewport. Juga, panggil glClearColor
untuk mewarnai menjadi hitam GLSurfaceView.
Selanjutnya, hubungi generateSquare
untuk reinitialize tekstur dan pesawat.
1 |
@Override
|
2 |
public void onSurfaceChanged(GL10 gl, int width, int height) { |
3 |
GLES20.glViewport(0,0,width, height); |
4 |
GLES20.glClearColor(0,0,0,1); |
5 |
generateSquare(); |
6 |
}
|
Akhirnya, memanggil metode draw
objek Square
di dalam metode onDrawFrame
Renderer
.
1 |
@Override
|
2 |
public void onDrawFrame(GL10 gl) { |
3 |
square.draw(textures[0]); |
4 |
}
|
Anda sekarang dapat menjalankan aplikasi Anda dan lihat foto Anda telah memilih yang diterjemahkan sebagai tekstur OpenGL di pesawat.



4. menggunakan Media efek Framework
Kode yang kompleks yang kami menulis sampai sekarang adalah hanya sebuah prasyarat untuk menggunakan Media Effects framework.. Sekarang saatnya untuk mulai menggunakan framework itu sendiri. Menambahkan kolom berikut kelas Renderer
Anda.
1 |
private EffectContext effectContext; |
2 |
private Effect effect; |
Menginisialisasi bidang effectContext
dengan menggunakan EffectContext.createWithCurrentGlContext
. Hal ini bertanggung jawab untuk mengelola informasi tentang efek visual dalam konteks OpenGL. Untuk mengoptimalkan kinerja, ini harus disebut hanya sekali. Tambahkan kode berikut pada permulaan metode onDrawFrame
Anda.
1 |
if(effectContext==null) { |
2 |
effectContext = EffectContext.createWithCurrentGlContext(); |
3 |
}
|
Membuat efek sangat sederhana. Gunakan effectContext
untuk membuat EffectFactory
dan menggunakan EffectFactory
untuk membuat objek Effect
. Setelah Effect
tersedia, Anda dapat memanggil apply
dan pass referensi untuk tekstur aslinya, dalam kasus kami ini adalah textures[0]
, bersama dengan referensi ke objek kosong tekstur, dalam kasus kami ini adalah textures[1]
. Setelah apply
metode ini disebut, textures[1]
akan berisi hasil dari Effect
.
Misalnya, untuk membuat dan menerapkan efek grayscale, di sini 's kode Anda harus menulis:
1 |
private void grayScaleEffect(){ |
2 |
EffectFactory factory = effectContext.getFactory(); |
3 |
effect = factory.createEffect(EffectFactory.EFFECT_GRAYSCALE); |
4 |
effect.apply(textures[0], photoWidth, photoHeight, textures[1]); |
5 |
}
|
Memanggil metode onDrawFrame
dan pass textures[1]
metode draw
objek Square
. Metode onDrawFrame
Anda harus memiliki kode berikut:
1 |
@Override
|
2 |
public void onDrawFrame(GL10 gl) { |
3 |
if(effectContext==null) { |
4 |
effectContext = EffectContext.createWithCurrentGlContext(); |
5 |
}
|
6 |
if(effect!=null){ |
7 |
effect.release(); |
8 |
}
|
9 |
grayScaleEffect(); |
10 |
square.draw(textures[1]); |
11 |
}
|
release
metode digunakan untuk membebaskan semua resource yang diselenggarakan oleh Effect
. Ketika Anda menjalankan aplikasi, Anda harus melihat hasil sebagai berikut:



Anda dapat menggunakan kode yang sama untuk menerapkan efek lainnya. Sebagai contoh, berikut adalah kode untuk menerapkan efek dokumenter:
1 |
private void documentaryEffect(){ |
2 |
EffectFactory factory = effectContext.getFactory(); |
3 |
effect = factory.createEffect(EffectFactory.EFFECT_DOCUMENTARY); |
4 |
effect.apply(textures[0], photoWidth, photoHeight, textures[1]); |
5 |
}
|
Hasil terlihat seperti ini:



Beberapa efek mengambil parameter. Sebagai contoh, efek penyesuaian kecerahan memiliki parameter brightness
yang mengambil nilai float
. Anda dapat menggunakan setParameter
untuk mengubah nilai parameter apapun. Kode berikut memperlihatkan bagaimana menggunakannya:
1 |
private void brightnessEffect(){ |
2 |
EffectFactory factory = effectContext.getFactory(); |
3 |
effect = factory.createEffect(EffectFactory.EFFECT_BRIGHTNESS); |
4 |
effect.setParameter("brightness", 2f); |
5 |
effect.apply(textures[0], photoWidth, photoHeight, textures[1]); |
6 |
}
|
Efek akan membuat aplikasi Anda membuat hasil sebagai berikut:



Kesimpulan
Dalam tutorial ini, Anda telah belajar bagaimana untuk menggunakan Media Effects Framework untuk menerapkan berbagai efek ke foto Anda. Meskipun demikian, Anda juga belajar cara menggambar pesawat menggunakan OpenGL ES 2.0 dan berlaku berbagai tekstur untuk itu.
Kerangka dapat diterapkan untuk foto dan video. Dalam video, Anda hanya perlu menerapkan efek ke frame individu video dalam metode onDrawFrame
.
Anda telah melihat tiga efek dalam tutorial ini dan framework memiliki belasan lagi bagi Anda untuk bereksperimen dengan. Untuk mengetahui lebih banyak tentang mereka, merujuk pada website pengembang Android.