diff --git a/app/src/main/java/cn/nekocode/camerafilter/CameraRenderer.java b/app/src/main/java/cn/nekocode/camerafilter/CameraRenderer.java index 1aa7481..9bd375d 100644 --- a/app/src/main/java/cn/nekocode/camerafilter/CameraRenderer.java +++ b/app/src/main/java/cn/nekocode/camerafilter/CameraRenderer.java @@ -35,6 +35,7 @@ import cn.nekocode.camerafilter.filter.AsciiArtFilter; import cn.nekocode.camerafilter.filter.BasicDeformFilter; +import cn.nekocode.camerafilter.filter.BeautyFilter; import cn.nekocode.camerafilter.filter.BlueorangeFilter; import cn.nekocode.camerafilter.filter.CameraFilter; import cn.nekocode.camerafilter.filter.ChromaticAberrationFilter; @@ -163,6 +164,7 @@ public void run() { cameraFilterMap.append(R.id.filter18, new CrackedFilter(context)); cameraFilterMap.append(R.id.filter19, new PolygonizationFilter(context)); cameraFilterMap.append(R.id.filter20, new JFAVoronoiFilter(context)); + cameraFilterMap.append(R.id.filter21, new BeautyFilter(context)); setSelectedFilter(selectedFilterId); // Create texture for camera preview @@ -274,7 +276,7 @@ private Pair getBackCamera() { for (int i = 0; i < numberOfCameras; ++i) { Camera.getCameraInfo(i, cameraInfo); - if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_BACK) { + if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) { return new Pair<>(cameraInfo, i); } } diff --git a/app/src/main/java/cn/nekocode/camerafilter/RenderBuffer.java b/app/src/main/java/cn/nekocode/camerafilter/RenderBuffer.java index dc2ed39..82aa8cd 100644 --- a/app/src/main/java/cn/nekocode/camerafilter/RenderBuffer.java +++ b/app/src/main/java/cn/nekocode/camerafilter/RenderBuffer.java @@ -43,9 +43,7 @@ public RenderBuffer(int width, int height, int activeTexUnit) { // Generate and bind 2d texture GLES20.glActiveTexture(activeTexUnit); texId = MyGLUtils.genTexture(); - IntBuffer texBuffer = - ByteBuffer.allocateDirect(width * height * 4).order(ByteOrder.nativeOrder()).asIntBuffer(); - GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, width, height, 0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, texBuffer); + GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, width, height, 0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, null); GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR); GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR); diff --git a/app/src/main/java/cn/nekocode/camerafilter/filter/BeautyFilter.java b/app/src/main/java/cn/nekocode/camerafilter/filter/BeautyFilter.java new file mode 100644 index 0000000..c6b1908 --- /dev/null +++ b/app/src/main/java/cn/nekocode/camerafilter/filter/BeautyFilter.java @@ -0,0 +1,45 @@ +/* + * Copyright 2016 nekocode + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package cn.nekocode.camerafilter.filter; + +import android.content.Context; +import android.opengl.GLES20; + +import cn.nekocode.camerafilter.MyGLUtils; +import cn.nekocode.camerafilter.R; + +/** + * @author nekocode (nekocode.cn@gmail.com) + */ +public class BeautyFilter extends CameraFilter { + private int program; + + public BeautyFilter(Context context) { + super(context); + + // Build shaders + program = MyGLUtils.buildProgram(context, R.raw.vertext, R.raw.beauty); + } + + @Override + public void onDraw(int cameraTexId, int canvasWidth, int canvasHeight) { + setupShaderInputs(program, + new int[]{canvasWidth, canvasHeight}, + new int[]{cameraTexId}, + new int[][]{}); + GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); + } +} \ No newline at end of file diff --git a/app/src/main/java/cn/nekocode/camerafilter/filter/CameraFilter.java b/app/src/main/java/cn/nekocode/camerafilter/filter/CameraFilter.java index e1c7975..6927e40 100644 --- a/app/src/main/java/cn/nekocode/camerafilter/filter/CameraFilter.java +++ b/app/src/main/java/cn/nekocode/camerafilter/filter/CameraFilter.java @@ -176,6 +176,10 @@ void setupShaderInputs(int program, FloatBuffer vertex, FloatBuffer textureCoord int iChannelResolutionLocation = GLES20.glGetUniformLocation(program, "iChannelResolution"); GLES20.glUniform3fv(iChannelResolutionLocation, _iChannelResolutions.length, FloatBuffer.wrap(_iChannelResolutions)); + + // For beauty + int singleStepOffsetLocation = GLES20.glGetUniformLocation(program, "singleStepOffset"); + GLES20.glUniform2fv(singleStepOffsetLocation, 1, FloatBuffer.wrap(new float[] { 2.0f / 480.0f, 2.0f / 640.0f})); } public static void release() { diff --git a/app/src/main/res/menu/filter.xml b/app/src/main/res/menu/filter.xml index cfc9a71..19445a2 100644 --- a/app/src/main/res/menu/filter.xml +++ b/app/src/main/res/menu/filter.xml @@ -134,4 +134,10 @@ android:title="JFAVoronoi" app:showAsAction="never" /> + + diff --git a/app/src/main/res/raw/beauty.fsh b/app/src/main/res/raw/beauty.fsh new file mode 100644 index 0000000..5cf9256 --- /dev/null +++ b/app/src/main/res/raw/beauty.fsh @@ -0,0 +1,107 @@ +precision highp float; + +uniform sampler2D iChannel0; +uniform vec2 singleStepOffset; + +varying vec2 texCoord; + +const vec4 params = vec4(0.33, 0.63, 0.4, 0.35); +const highp vec3 W = vec3(0.299,0.587,0.114); +const mat3 saturateMatrix = mat3( + 1.1102,-0.0598,-0.061, + -0.0774,1.0826,-0.1186, + -0.0228,-0.0228,1.1772); + +vec2 blurCoordinates[24]; + +float hardLight(float color) { + if(color <= 0.5) { + color = color * color * 2.0; + } else { + color = 1.0 - ((1.0 - color)*(1.0 - color) * 2.0); + } + return color; +} + +void main() { + vec3 centralColor = texture2D(iChannel0, texCoord).rgb; + + blurCoordinates[0] = texCoord.xy + singleStepOffset * vec2(0.0, -10.0); + blurCoordinates[1] = texCoord.xy + singleStepOffset * vec2(0.0, 10.0); + blurCoordinates[2] = texCoord.xy + singleStepOffset * vec2(-10.0, 0.0); + blurCoordinates[3] = texCoord.xy + singleStepOffset * vec2(10.0, 0.0); + blurCoordinates[4] = texCoord.xy + singleStepOffset * vec2(5.0, -8.0); + blurCoordinates[5] = texCoord.xy + singleStepOffset * vec2(5.0, 8.0); + blurCoordinates[6] = texCoord.xy + singleStepOffset * vec2(-5.0, 8.0); + blurCoordinates[7] = texCoord.xy + singleStepOffset * vec2(-5.0, -8.0); + blurCoordinates[8] = texCoord.xy + singleStepOffset * vec2(8.0, -5.0); + blurCoordinates[9] = texCoord.xy + singleStepOffset * vec2(8.0, 5.0); + blurCoordinates[10] = texCoord.xy + singleStepOffset * vec2(-8.0, 5.0); + blurCoordinates[11] = texCoord.xy + singleStepOffset * vec2(-8.0, -5.0); + blurCoordinates[12] = texCoord.xy + singleStepOffset * vec2(0.0, -6.0); + blurCoordinates[13] = texCoord.xy + singleStepOffset * vec2(0.0, 6.0); + blurCoordinates[14] = texCoord.xy + singleStepOffset * vec2(6.0, 0.0); + blurCoordinates[15] = texCoord.xy + singleStepOffset * vec2(-6.0, 0.0); + blurCoordinates[16] = texCoord.xy + singleStepOffset * vec2(-4.0, -4.0); + blurCoordinates[17] = texCoord.xy + singleStepOffset * vec2(-4.0, 4.0); + blurCoordinates[18] = texCoord.xy + singleStepOffset * vec2(4.0, -4.0); + blurCoordinates[19] = texCoord.xy + singleStepOffset * vec2(4.0, 4.0); + blurCoordinates[20] = texCoord.xy + singleStepOffset * vec2(-2.0, -2.0); + blurCoordinates[21] = texCoord.xy + singleStepOffset * vec2(-2.0, 2.0); + blurCoordinates[22] = texCoord.xy + singleStepOffset * vec2(2.0, -2.0); + blurCoordinates[23] = texCoord.xy + singleStepOffset * vec2(2.0, 2.0); + + float sampleColor = centralColor.g * 22.0; + sampleColor += texture2D(iChannel0, blurCoordinates[0]).g; + sampleColor += texture2D(iChannel0, blurCoordinates[1]).g; + sampleColor += texture2D(iChannel0, blurCoordinates[2]).g; + sampleColor += texture2D(iChannel0, blurCoordinates[3]).g; + sampleColor += texture2D(iChannel0, blurCoordinates[4]).g; + sampleColor += texture2D(iChannel0, blurCoordinates[5]).g; + sampleColor += texture2D(iChannel0, blurCoordinates[6]).g; + sampleColor += texture2D(iChannel0, blurCoordinates[7]).g; + sampleColor += texture2D(iChannel0, blurCoordinates[8]).g; + sampleColor += texture2D(iChannel0, blurCoordinates[9]).g; + sampleColor += texture2D(iChannel0, blurCoordinates[10]).g; + sampleColor += texture2D(iChannel0, blurCoordinates[11]).g; + sampleColor += texture2D(iChannel0, blurCoordinates[12]).g * 2.0; + sampleColor += texture2D(iChannel0, blurCoordinates[13]).g * 2.0; + sampleColor += texture2D(iChannel0, blurCoordinates[14]).g * 2.0; + sampleColor += texture2D(iChannel0, blurCoordinates[15]).g * 2.0; + sampleColor += texture2D(iChannel0, blurCoordinates[16]).g * 2.0; + sampleColor += texture2D(iChannel0, blurCoordinates[17]).g * 2.0; + sampleColor += texture2D(iChannel0, blurCoordinates[18]).g * 2.0; + sampleColor += texture2D(iChannel0, blurCoordinates[19]).g * 2.0; + sampleColor += texture2D(iChannel0, blurCoordinates[20]).g * 3.0; + sampleColor += texture2D(iChannel0, blurCoordinates[21]).g * 3.0; + sampleColor += texture2D(iChannel0, blurCoordinates[22]).g * 3.0; + sampleColor += texture2D(iChannel0, blurCoordinates[23]).g * 3.0; + sampleColor = sampleColor / 62.0; + + float highPass = centralColor.g - sampleColor + 0.5; + + for(int i = 0; i < 5;i++) + { + highPass = hardLight(highPass); + } + float luminance = dot(centralColor, W); + float alpha = pow(luminance, params.r); + + vec3 smoothColor = centralColor + (centralColor-vec3(highPass))*alpha*0.1; + + smoothColor.r = clamp(pow(smoothColor.r, params.g),0.0,1.0); + smoothColor.g = clamp(pow(smoothColor.g, params.g),0.0,1.0); + smoothColor.b = clamp(pow(smoothColor.b, params.g),0.0,1.0); + + vec3 screen = vec3(1.0) - (vec3(1.0)-smoothColor) * (vec3(1.0)-centralColor); + vec3 lighten = max(smoothColor, centralColor); + vec3 softLight = 2.0 * centralColor*smoothColor + centralColor*centralColor + - 2.0 * centralColor*centralColor * smoothColor; + + gl_FragColor = vec4(mix(centralColor, screen, alpha), 1.0); + gl_FragColor.rgb = mix(gl_FragColor.rgb, lighten, alpha); + gl_FragColor.rgb = mix(gl_FragColor.rgb, softLight, params.b); + + vec3 satColor = gl_FragColor.rgb * saturateMatrix; + gl_FragColor.rgb = mix(gl_FragColor.rgb, satColor, params.a); +} \ No newline at end of file