Android camera library CameraView source code analysis (3): Filter related class description

1 Introduction

During this period, I am usingnatario1/CameraView to implement filtered 预览, a> authors have never fixed it. , some of has entered the deep water area, and it gradually appears that it cannot meet our needs. However, as the project continues to deepen, the use of the packaging is quite in place, it indeed saved us a lot of time in the early stages of the project. Since function. 拍照, 录像
CameraView
CameraView
GithubissuesBUG

What should we do? The project urgently needs to implement related functions, so we can only bite the bullet and read its source code to solve these problems.
In the last article, we have a general understanding of the process of taking pictures. In this article, we will take a look at the filter-related classes to provide guidance for the following. The source code analysis of filter photography will pave the way.

The following source code analysis is based onCameraView 2.7.2

implementation("com.otaliastudios:cameraview:2.7.2")

In order to better display on the blog, the code posted in this article has been partially streamlined.

Insert image description here

2. How to set filters

InCameraView, set the filter viasetFilter(Filter filter).

//初始化亮度滤镜
val brightnessFilter = BrightnessFilter()
//设置亮度值
brightnessFilter.setBrightness(1.5F)
//设置滤镜
cameraView.setFilter(brightnessFilter)

3. Filter

FilterThis is one contact, fixed获取顶点着色器, 获取片元着色器, 当初始化时, 当销毁时 , 当绘制时, 设置尺寸, 拷贝滤镜

public interface Filter {
    
    

    /**
     * 获取顶点着色器
     */
    String getVertexShader();

    /**
     * 获取片元着色器
     */
    String getFragmentShader();

    /**
     * 初始化时调用
     */
    void onCreate(int programHandle);

    /**
     * 销毁时调用
     * 
     */
    void onDestroy();

    /**
     * 当绘制的时候
     */
    void draw(long timestampUs, float[] transformMatrix);

    /**
     * 设置尺寸
     */
    void setSize(int width, int height);

    /**
     * 复制滤镜
     */
    Filter copy();
}

4. BaseFilter

BaseFilter is an abstract class that implements the Filter interface. BaseFilter implements the default vertex shader and fragment shader. In When onCreate, a that specifically executes OpenGL API is created. When , it will be executed according to and interfaces, copy. GlTextureProgramcopyOneParameterFilterTwoParameterFilterFilter

public abstract class BaseFilter implements Filter {
    
    
	//...省略了具体代码...
}

Let’s look at the specific code of BaseFilter

4.1 Default vertex shader and fragment shader

Implemented default vertex shader and fragment shader

protected final static String DEFAULT_VERTEX_POSITION_NAME = "aPosition";
protected final static String DEFAULT_VERTEX_TEXTURE_COORDINATE_NAME = "aTextureCoord";
protected final static String DEFAULT_VERTEX_MVP_MATRIX_NAME = "uMVPMatrix";
protected final static String DEFAULT_VERTEX_TRANSFORM_MATRIX_NAME = "uTexMatrix";
protected final static String DEFAULT_FRAGMENT_TEXTURE_COORDINATE_NAME = "vTextureCoord";

private static String createDefaultVertexShader(
        @NonNull String vertexPositionName,
        @NonNull String vertexTextureCoordinateName,
        @NonNull String vertexModelViewProjectionMatrixName,
        @NonNull String vertexTransformMatrixName,
        @NonNull String fragmentTextureCoordinateName) {
    
    
    return "uniform mat4 "+vertexModelViewProjectionMatrixName+";\n"
            + "uniform mat4 "+vertexTransformMatrixName+";\n"
            + "attribute vec4 "+vertexPositionName+";\n"
            + "attribute vec4 "+vertexTextureCoordinateName+";\n"
            + "varying vec2 "+fragmentTextureCoordinateName+";\n"
            + "void main() {\n"
            + "    gl_Position = " +vertexModelViewProjectionMatrixName+" * "
            + vertexPositionName+";\n"
            + "    "+fragmentTextureCoordinateName+" = ("+vertexTransformMatrixName+" * "
            + vertexTextureCoordinateName+").xy;\n"
            + "}\n";
}

private static String createDefaultFragmentShader(
        @NonNull String fragmentTextureCoordinateName) {
    
    
    return "#extension GL_OES_EGL_image_external : require\n"
            + "precision mediump float;\n"
            + "varying vec2 "+fragmentTextureCoordinateName+";\n"
            + "uniform samplerExternalOES sTexture;\n"
            + "void main() {\n"
            + "  gl_FragColor = texture2D(sTexture, "+fragmentTextureCoordinateName+");\n"
            + "}\n";
}

4.2 Create GlTextureProgram

GlTextureProgram is the specific implementation of OpenGL texture drawing. 顶点着色器和片元着色器 and so on are passed in here to create GlTextureProgram

@Override
public void onCreate(int programHandle) {
    
    
    program = new GlTextureProgram(programHandle,
            vertexPositionName,
            vertexModelViewProjectionMatrixName,
            vertexTextureCoordinateName,
            vertexTransformMatrixName);
    programDrawable = new GlRect();
}

4.3 Set dimensions and draw

Set the size and draw at the appropriate opportunity. There are three methods for drawing: onPreDraw, onDraw, onPostDraw, internal are all called GlTextureProgram corresponding to onPreDraw, onDraw, onPostDraw, and GlTextureProgramInside, we only need to know the OpenGL API specific method now.

@Override
public void setSize(int width, int height) {
    
    
    size = new Size(width, height);
}

@Override
public void draw(long timestampUs, @NonNull float[] transformMatrix) {
    
    
    onPreDraw(timestampUs, transformMatrix);
    onDraw(timestampUs);
    onPostDraw(timestampUs);
}

protected void onPreDraw(long timestampUs, @NonNull float[] transformMatrix) {
    
    
    program.setTextureTransform(transformMatrix);
    program.onPreDraw(programDrawable, programDrawable.getModelMatrix());
}

protected void onDraw(long timestampUs) {
    
    
    program.onDraw(programDrawable);
}

protected void onPostDraw(long timestampUs) {
    
    
    program.onPostDraw(programDrawable);
}

4.4 Copy filter

copy method, internally calls getClass().newInstance() to reflect to get a new BaseFilter, and assigns Size, if implemented If the OneParameterFilter or TwoParameterFilter interface is installed, related parameters will also be set.

For example, the brightness value of the brightness filter needs to implement the OneParameterFilter or TwoParameterFilter interface, so that the set brightness value can be assigned to the new one a>BaseFilter

@NonNull
@Override
public final BaseFilter copy() {
    
    
    BaseFilter copy = onCopy();
    if (size != null) {
    
    
        copy.setSize(size.getWidth(), size.getHeight());
    }
    if (this instanceof OneParameterFilter) {
    
    
        ((OneParameterFilter) copy).setParameter1(((OneParameterFilter) this).getParameter1());
    }
    if (this instanceof TwoParameterFilter) {
    
    
        ((TwoParameterFilter) copy).setParameter2(((TwoParameterFilter) this).getParameter2());
    }
    return copy;
}

@NonNull
protected BaseFilter onCopy() {
    
    
    try {
    
    
        return getClass().newInstance();
    } catch (IllegalAccessException e) {
    
    
        throw new RuntimeException("Filters should have a public no-arguments constructor.", e);
    } catch (InstantiationException e) {
    
    
        throw new RuntimeException("Filters should have a public no-arguments constructor.", e);
    }
}

Then we will have questions,copy Under what circumstances will the method be used?
According to the source code, you can see that with filters In the SnapshotGlPictureRecorder classes related to taking pictures, the copy method will be used.

protected void onRendererFilterChanged(@NonNull Filter filter) {
    
    
    mTextureDrawer.setFilter(filter.copy());
}

It is used for previewing and taking picturesBaseFilterIn fact, it is not the same oneFitler, but one will be made firstcopy. Go take pictures again.
Because in order to preview smoothly, previewing and taking photos are actually not the same Surface (will be discussed later), the original Fitler has It is used by the preview, so you needCopy a copy before taking pictures.

5. Preset filters

CameraViewSome common filters are preset and can be used directly.

5.1 Collection of preset filters

The preset filters include the following
Insert image description here

5.2 Brightness filter

For exampleBrightnessFilter is a filter that adjusts brightness. The code is as follows
You can see that the relevant . GLSLonPreDraw

public class BrightnessFilter extends BaseFilter implements OneParameterFilter {
    
    

    private final static String FRAGMENT_SHADER = "#extension GL_OES_EGL_image_external : require\n"
            + "precision mediump float;\n"
            + "uniform samplerExternalOES sTexture;\n"
            + "uniform float brightness;\n"
            + "varying vec2 "+DEFAULT_FRAGMENT_TEXTURE_COORDINATE_NAME+";\n"
            + "void main() {\n"
            + "  vec4 color = texture2D(sTexture, "+DEFAULT_FRAGMENT_TEXTURE_COORDINATE_NAME+");\n"
            + "  gl_FragColor = brightness * color;\n"
            + "}\n";

    private float brightness = 2.0f; // 1.0F...2.0F
    private int brightnessLocation = -1;


    public BrightnessFilter() {
    
     }

    /**
     * Sets the brightness adjustment.
     * 1.0: normal brightness.
     * 2.0: high brightness.
     *
     * @param brightness brightness.
     */
    @SuppressWarnings({
    
    "WeakerAccess", "unused"})
    public void setBrightness(float brightness) {
    
    
        if (brightness < 1.0f) brightness = 1.0f;
        if (brightness > 2.0f) brightness = 2.0f;
        this.brightness = brightness;
    }

    /**
     * Returns the current brightness.
     *
     * @see #setBrightness(float)
     * @return brightness
     */
    @SuppressWarnings({
    
    "unused", "WeakerAccess"})
    public float getBrightness() {
    
    
        return brightness;
    }

    @Override
    public void setParameter1(float value) {
    
    
        // parameter is 0...1, brightness is 1...2.
        setBrightness(value + 1);
    }

    @Override
    public float getParameter1() {
    
    
        // parameter is 0...1, brightness is 1...2.
        return getBrightness() - 1F;
    }

    @NonNull
    @Override
    public String getFragmentShader() {
    
    
        return FRAGMENT_SHADER;
    }

    @Override
    public void onCreate(int programHandle) {
    
    
        super.onCreate(programHandle);
        brightnessLocation = GLES20.glGetUniformLocation(programHandle, "brightness");
        Egloo.checkGlProgramLocation(brightnessLocation, "brightness");
    }

    @Override
    public void onDestroy() {
    
    
        super.onDestroy();
        brightnessLocation = -1;
    }

    @Override
    protected void onPreDraw(long timestampUs, @NonNull float[] transformMatrix) {
    
    
        super.onPreDraw(timestampUs, transformMatrix);
        GLES20.glUniform1f(brightnessLocation, brightness);
        Egloo.checkGlError("glUniform1f");
    }
}

6. MultiFilter

To call a single filter, you can directly call a certain filter, but if multiple filters are superimposed, you need to use MultiFilter, through addFilter() to stack multiple filters.

public class MultiFilter implements Filter, OneParameterFilter, TwoParameterFilter {
    
    
	//...省略了具体代码...
}

6.1 Add filters

Store added filtersfilterslist

final List<Filter> filters = new ArrayList<>();

public void addFilter(@NonNull Filter filter) {
    
    
    if (filter instanceof MultiFilter) {
    
    
        MultiFilter multiFilter = (MultiFilter) filter;
        for (Filter multiChild : multiFilter.filters) {
    
    
            addFilter(multiChild);
        }
        return;
    }
    synchronized (lock) {
    
    
        if (!filters.contains(filter)) {
    
    
            filters.add(filter);
            states.put(filter, new State());
        }
    }
}

6.2 Drawing filters

traverses the filters list and calls a series of OpenGL methods to draw filters one by one. After the previous filter is drawn, the next filter Draw on the basis of the previous filter to finally achieve the effect of filter overlay.

@Override
public void draw(long timestampUs, @NonNull float[] transformMatrix) {
    
    
    synchronized (lock) {
    
    
        for (int i = 0; i < filters.size(); i++) {
    
    
            boolean isFirst = i == 0;
            boolean isLast = i == filters.size() - 1;
            Filter filter = filters.get(i);
            State state = states.get(filter);

            maybeSetSize(filter);
            maybeCreateProgram(filter, isFirst, isLast);
            maybeCreateFramebuffer(filter, isFirst, isLast);

            //noinspection ConstantConditions
            GLES20.glUseProgram(state.programHandle);

            // Define the output framebuffer.
            // Each filter outputs into its own framebuffer object, except the
            // last filter, which outputs into the default framebuffer.
            if (!isLast) {
    
    
                state.outputFramebuffer.bind();
                GLES20.glClearColor(0, 0, 0, 0);
            } else {
    
    
                GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
            }

            // Perform the actual drawing.
            // The first filter should apply all the transformations. Then,
            // since they are applied, we should use a no-op matrix.
            if (isFirst) {
    
    
                filter.draw(timestampUs, transformMatrix);
            } else {
    
    
                filter.draw(timestampUs, Egloo.IDENTITY_MATRIX);
            }

            // Set the input for the next cycle:
            // It is the framebuffer texture from this cycle. If this is the last
            // filter, reset this value just to cleanup.
            if (!isLast) {
    
    
                state.outputTexture.bind();
            } else {
    
    
                GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
                GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
            }

            GLES20.glUseProgram(0);
        }
    }
}

7. Others

7.1 CameraView source code analysis series

Android Camera Library CameraView Source Code Analysis (1): Preview-CSDN Blog
Android Camera Library CameraView Source Code Analysis (2): Taking Photos-CSDN Blog
Android camera library CameraView source code analysis (3): Filter related class description-CSDN Blog
Android camera library CameraView source code analysis (4): Taking photos with filters-CSDN Blog< a i=4>Android camera library CameraView source code analysis (5): Saving filter effects-CSDN Blog

Guess you like

Origin blog.csdn.net/EthanCo/article/details/134517249