gpt4 book ai didi

java - 在Android中使用ColorMatrixFilter减去混合模式?

转载 作者:行者123 更新时间:2023-12-05 00:58:39 26 4
gpt4 key购买 nike

我有以下ColorMatrixFilter。但我想将其用作减混模式的蒙版,而不是直接使用它。我该如何实现这一目标?

ColorMatrix:

colorMatrix[
0.393, 0.7689999, 0.18899999, 0, 0,
0.349, 0.6859999, 0.16799999, 0, 0,
0.272, 0.5339999, 0.13099999, 0, 0,
0, 0, 0, 1, 0
];

最佳答案

长话短说

Android中没有开箱即用的减法混合功能。但是,您可以使用OpenGL实现所需的颜色混合。 Here是要点,您可以这样使用:

BlendingFilterUtil.subtractMatrixColorFilter(bitmap, new float[]{
0.393f, 0.7689999f, 0.18899999f, 0, 0,
0.349f, 0.6859999f, 0.16799999f, 0, 0,
0.272f, 0.5339999f, 0.13099999f, 0, 0,
0, 0, 0, 1, 0
}, activity, callback);

理论

坦白地说,这个问题令我有些困惑。为了弄清事情,让我们定义两个不同的功能集:Android中的 颜色混合颜色过滤

色彩融合

色彩混合在设计师和使用图形的人中是众所周知的事情。如其标题所示,它使用其 channel 值(称为红色,绿色,蓝色和Alpha)和混合功能来混合两种颜色。这些功能称为混合模式。其中一种模式称为 减去。减法混合模式使用以下公式获取输出颜色:

Subtract blend mode

其中Cout是结果颜色,Cdst是“当前”颜色,Csrc是用于更改原始颜色的颜色值。如果任何 channel 差为负,则应用0值。
就像人们可能猜到的那样,“减法”混合的结果往往比原始图像更暗,因为 channel 变得更接近于零。我发现 this page中的示例很清楚地演示了减法效果:

目标

Destination

来源

Source

减去输出

Output

色彩过滤

对于Android,与色彩混合相比,色彩过滤是一种 super 操作集。有关它们的完整列表,可以引用 ColorFilter 子类描述。正如您从文档中看到的, ColorFilter有三种可用的实现:
  • PorterDuffColorFilter 本质上是讨论的混合模式
    以上;
  • LightingColorFilter 非常简单。它由两个参数组成,其中一个用作因子,另一个用作红色,绿色和蓝色 channel 的附加值。 Alpha channel 保持不变。因此,您可以使某些图像看起来更亮(如果因数在0到1之间,或者加法为负数,则可以看起来更暗)。
  • ColorMatrixColorFilter 是一个更花哨的东西。此过滤器是由 ColorMatrix 构造的。某种程度上ColorMatrixColorFilterLightingColorFilter相似,它还对原始颜色执行一些数学运算并构成用于其中的参数,但功能更强大。让我们引用ColorMatrix文档以了解有关其实际工作方式的更多信息:

    4x5 matrix for transforming the color and alpha components of a Bitmap. The matrix can be passed as single array, and is treated as follows:

    [ a, b, c, d, e,
    f, g, h, i, j,
    k, l, m, n, o,
    p, q, r, s, t ]

    When applied to a color [R, G, B, A], the resulting color is computed as:

    R’ = a*R + b*G + c*B + d*A + e;
    G’ = f*R + g*G + h*B + i*A + j;
    B’ = k*R + l*G + m*B + n*A + o;
    A’ = p*R + q*G + r*B + s*A + t;

  • 这是在OP帖子中指定的过滤器的示例图片的外观:

    Filtered Image

    目标

    现在我们到了需要定义我们实际目标的地步。我想OP在他的问题中正好在讲 ColorMatrixColorFilter(因为没有其他方法可以利用此矩阵)。从上面的描述中可以看到,“减法混合模式”采用两种颜色,而“颜色矩阵”滤色器采用一种颜色和一种可以更改该颜色的矩阵。这是两个不同的函数,它们采用不同类型的参数。我想到它们如何组合的唯一方法是采用原始颜色(Cdst),首先对其应用 ColorMatrix(过滤器函数),然后从原始颜色中减去此操作的结果,因此我们应得出以下公式:

    Subtract filter formula

    问题

    上面的任务并不是那么困难,我们可以使用 ColorMatrixColorFilter,然后将后续的 PorterDuffColorFilter与减法模式一起使用,并使用过滤后的结果作为源图像。但是,如果您仔细阅读 PorterDuff.Mode 引用,您会发现 Android系统中没有减法混合模式。 Android OS使用下面的 Google's Skia库进行 Canvas 绘制,由于某种原因,它实际上是 lacks Subtract mode,因此我们将不得不以另一种方式进行减法。
    这样的事情在Open GL中相对来说比较简单,主要的挑战是建立一个Open GL环境,以便它允许我们以所需的方式绘制所需的内容。

    解决方案

    我不想让我们自己做所有的努力。 Android已经有了 GLSurfaceView ,它在后台设置了Open GL上下文并提供了我们所有需要的功能,但是直到我们将该 View 添加到View层次结构中,它才起作用,所以我的计划是实例化 GLSurfaceView,并将其附加到我们的应用程序中窗口,给它一个位图,我们要对其应用效果并在那里执行所有花哨的东西。由于它与问题不直接相关,因此我不会过多介绍OpenGL本身,但是,如果您需要澄清任何内容,请随时提出评论。

    添加 GLSurfaceView
    首先,让我们创建一个 GLSurfaceView实例,并设置目标参数所需的所有参数:
    GLSurfaceView hostView = new GLSurfaceView(activityContext);
    hostView.setEGLContextClientVersion(2);
    hostView.setEGLConfigChooser(8, 8, 8, 8, 0, 0);

    然后,需要在“ View ”层次结构上添加此 View ,以使其运行其绘制周期:
    // View should be of bitmap size
    final WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(width, height, TYPE_APPLICATION, 0, PixelFormat.OPAQUE);
    view.setLayoutParams(layoutParams);
    final WindowManager windowManager = (WindowManager) view.getContext().getSystemService(Context.WINDOW_SERVICE);
    Objects.requireNonNull(windowManager).addView(view, layoutParams);

    我在根窗口上添加了此GL View ,因此可以从应用程序中的任何 Activity 中调用它。布局的 widthheight参数应与我们要处理的位图的 widthheight相匹配。

    添加渲染器
    GLSurfaceView本身不会绘制任何内容。这项工作由 Renderer 类完成。让我们定义一个包含几个字段的类:
    class BlendingFilterRenderer implements GLSurfaceView.Renderer {
    private final Bitmap mBitmap;
    private final WeakReference<GLSurfaceView> mHostViewReference;
    private final float[] mColorFilter;
    private final BlendingFilterUtil.Callback mCallback;
    private boolean mFinished = false;

    BlendingFilterRenderer(@NonNull GLSurfaceView hostView, @NonNull Bitmap bitmap,
    @NonNull float[] colorFilter,
    @NonNull BlendingFilterUtil.Callback callback)
    throws IllegalArgumentException {
    if (colorFilter.length != 4 * 5) {
    throw new IllegalArgumentException("Color filter should be a 4 x 5 matrix");
    }
    mBitmap = bitmap;
    mHostViewReference = new WeakReference<>(hostView);
    mColorFilter = colorFilter;
    mCallback = callback;
    }

    // ========================================== //
    // GLSurfaceView.Renderer
    // ========================================== //

    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {}

    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {}

    @Override
    public void onDrawFrame(GL10 gl) {}
    }

    渲染器应保留将要更改的 Bitmap 。我们将使用普通的 ColorMatrix java数组,而不是实际的 float[]实例,因为最终我们将不会使用Android工具来应用此效果,也不需要此类。我们还需要保留对 GLSurfaceView的引用,以便在工作完成后将其从应用程序窗口中删除。最后但并非最不重要的是回调。 GLSurfaceView中的所有绘图都发生在单独的线程中,因此我们无法同步执行此工作,因此需要回调以返回结果。我定义了回调接口(interface),如下所示:
    interface Callback {
    void onSuccess(@NonNull Bitmap blendedImage);
    void onFailure(@Nullable Exception error);
    }

    因此,它要么返回成功结果,要么返回可选错误。在发布结果时,最后将需要 mFinished标志,以防止任何进一步的操作。定义渲染器后,返回 GLSurfaceView设置并设置渲染器实例。我还建议将渲染模式设置为 RENDERMODE_WHEN_DIRTY 以防止每秒绘制60次:
    hostView.setRenderer(new BlendingFilterRenderer(hostView, image, filterValues, callback));
    hostView.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);

    绘制网格

    我们现在还不能在OpenGL曲面上绘制位图。首先,我们需要绘制作为纹理表面的网格。为此,我们必须定义着色器-在GPU上执行的小程序,一个程序定义网格的形状和位置(顶点着色器),另一个程序确定输出颜色(片段着色器)。编译两个着色器时,必须将它们链接到程序中。好了,足够的理论了。首先在renderer类中定义以下方法,我们将使用它来创建着色器程序:
    private int loadShader(int type, String shaderCode) throws GLException {
    int reference = GLES20.glCreateShader(type);
    GLES20.glShaderSource(reference, shaderCode);
    GLES20.glCompileShader(reference);
    int[] compileStatus = new int[1];
    GLES20.glGetShaderiv(reference, GLES20.GL_COMPILE_STATUS, compileStatus, 0);
    if (compileStatus[0] != GLES20.GL_TRUE) {
    GLES20.glDeleteShader(reference);
    final String message = GLES20.glGetShaderInfoLog(reference);
    throw new GLException(compileStatus[0], message);
    }

    return reference;
    }

    此方法中的第一个属性定义着色器类型(“顶点”或“片段”),第二个属性定义实际的代码。我们的顶点着色器如下所示:
    attribute vec2 aPosition;
    void main() {
    gl_Position = vec4(aPosition.x, aPosition.y, 0.0, 1.0);
    }
    aPosition属性将在规范化坐标系中获取x和y坐标(x和y坐标从-1到1),并将它们传递给全局 gl_Position变量。

    这是我们的片段着色器:
    precision mediump float;
    void main() {
    gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
    }

    在OpenGL版本2中,我们必须明确指定浮点精度,否则该程序将无法编译。该着色器还写入全局变量 gl_FragColor,该变量定义输出颜色(这是实际的魔术发生的地方)。
    现在我们需要编译这些着色器并链接到程序中:
    private int loadProgram() {
    int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, "precision mediump float;" +
    "void main() {" +
    " gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);" +
    "}");
    int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, "attribute vec2 aPosition;" +
    "void main() {" +
    " gl_Position = vec4(aPosition.x, aPosition.y, 0.0, 1.0);" +
    "}");
    int programReference = GLES20.glCreateProgram();
    GLES20.glAttachShader(programReference, vertexShader);
    GLES20.glAttachShader(programReference, fragmentShader);
    GLES20.glLinkProgram(programReference);
    return programReference;
    }

    现在,该程序已准备好接受我们的顶点。为了传递它们,我们将使用以下帮助器方法:
    private void enableVertexAttribute(int program, String attributeName, int size, int stride, int offset) {
    final int attributeLocation = GLES20.glGetAttribLocation(program, attributeName);
    GLES20.glVertexAttribPointer(attributeLocation, size, GLES20.GL_FLOAT, false, stride, offset);
    GLES20.glEnableVertexAttribArray(attributeLocation);
    }

    我们需要用网格覆盖所有表面,因此它与 GLSurfaceSize相匹配,在归一化设备坐标系(NDCS)中,它非常简单,对于x和y坐标,整个表面坐标都可以用-1到1的范围来表示。 ,因此这是我们的坐标:
    new float[] {
    -1, 1,
    -1, -1,
    1, 1,
    1, -1,
    }

    不幸的是,由于OpenGL中仅存在三种类型的图元,因此不可能仅画一个框:三角形,线和点。几个直角三角形就足以构成一个覆盖整个表面的矩形。让我们先将顶点加载到数组缓冲区中,以便着色器可以访问它们:
    private FloatBuffer convertToBuffer(float[] array) {
    final ByteBuffer buffer = ByteBuffer.allocateDirect(array.length * PrimitiveSizes.FLOAT);
    FloatBuffer output = buffer.order(ByteOrder.nativeOrder()).asFloatBuffer();
    output.put(array);
    output.position(0);
    return output;
    }

    private void initVertices(int programReference) {
    final float[] verticesData = new float[] {
    -1, 1,
    -1, -1,
    1, 1,
    1, -1,
    }
    int buffers[] = new int[1];
    GLES20.glGenBuffers(1, buffers, 0);
    GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, buffers[0]);
    GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, verticesData.length * 4, convertToBuffer(verticesData), GLES20.GL_STREAM_DRAW);
    enableVertexAttribute(programReference, "aPosition", 2, 0, 0);
    }

    让我们将所有内容放到Renderer界面函数中:
    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {}

    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
    GLES20.glViewport(0, 0, width, height);
    final int program = loadProgram();
    GLES20.glUseProgram(program);
    initVertices(program);
    }

    @Override
    public void onDrawFrame(GL10 gl) {
    GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
    GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
    }

    如果现在运行该程序,则应该看到白色的表面而不是黑色的表面。我们现在快到一半了。

    绘制位图

    现在,我们需要将其传递到着色器程序中,并绘制网格(三角形)。除了纹理(在本例中为位图)本身之外,我们还需要传递纹理坐标,以便可以在整个表面上插值纹理。这是我们的新顶点着色器:
    attribute vec2 aPosition;
    attribute vec2 aTextureCoord;
    varying vec2 vTextureCoord;

    void main() {
    gl_Position = vec4(aPosition.x, aPosition.y, 0.0, 1.0);
    vTextureCoord = aTextureCoord;
    }

    好消息是,此着色器将不再更改。顶点着色器现在处于最后阶段。让我们看一下片段着色器:
    precision mediump float;
    uniform sampler2D uSampler;
    varying vec2 vTextureCoord;

    void main() {
    gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
    gl_FragColor = texture2D(uSampler, vTextureCoord);
    }

    那么,这里发生了什么?粗略地说,我们将纹理的坐标传递到顶点(传递到 aTextureCoord属性中),然后顶点着色器将这些坐标传递到类型不同的特殊变量 vTextureCoord中,该变量在顶点之间进行插值,并将内插值传递给片段着色器。片段着色器通过 uSampler统一参数获取纹理,并从 texture2D函数获取所需像素的颜色以及从顶点着色器传递的纹理坐标。
    除了顶点位置,我们现在还需要传递纹理坐标。 x和y的纹理坐标在0.0到1.0之间变化,起始点(0.0,0.0)在左下角。对于习惯于Android坐标系(0,0始终位于左上角)的用户来说,这听起来并不常见。幸运的是,我们不必太在意它,我们只需在OpenGL中垂直翻转纹理即可,最后,我们将能够获得正确定位的图像。将 initVertices更改为如下所示:
    private void initVertices(int programReference) {
    final float[] verticesData = new float[] {
    //NDCS coords //UV map
    -1, 1, 0, 1,
    -1, -1, 0, 0,
    1, 1, 1, 1,
    1, -1, 1, 0

    }
    int buffers[] = new int[1];
    GLES20.glGenBuffers(1, buffers, 0);
    GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, buffers[0]);
    GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, verticesData.length * 4, convertToBuffer(verticesData), GLES20.GL_STREAM_DRAW);
    final int stride = 4 * 4;
    enableVertexAttribute(programReference, "aPosition", 2, stride, 0);
    enableVertexAttribute(programReference, "aTextureCoord", 2, stride, 2 * 4);

    }

    现在,让我们将实际的位图传递给片段着色器。这是为我们做的方法:
    private void attachTexture(int programReference) {
    final int[] textures = new int[1];
    GLES20.glGenTextures(1, textures, 0);
    final int textureId = textures[0];
    GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId);
    GLES20.glPixelStorei(GLES20.GL_UNPACK_ALIGNMENT, 1);
    GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
    GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST);
    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);
    GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, mBitmap, 0);
    GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
    GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId);
    final int samplerLocation = GLES20.glGetUniformLocation(programReference, "uSampler");
    GLES20.glUniform1i(samplerLocation, 0);
    }

    不要忘记在 onSurfaceChanged方法中调用此方法:
    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
    GLES20.glViewport(0, 0, width, height);
    final int program = loadProgram();
    GLES20.glUseProgram(program);
    initVertices(program);
    attachTexture(program);
    }

    套用彩色滤光片

    现在我们已经准备好应用彩色滤光片。再次让我们从着色器开始。对于顶点着色器,什么都没有改变,只有片段缓冲区对颜色计算感兴趣。彩色滤光片是4x5矩阵,问题是OpenGL在行或列中最多只有4个矩阵。为了解决这个问题,我们将定义一个新结构,该结构将由一个4x4矩阵和一个4x vector 组成。通过滤色器后,我们将拥有执行颜色转换和混合所需的所有内容。您已经知道该公式,因此不再赘述,这是我们几乎最终的片段着色器:
    precision mediump float;
    struct ColorFilter {
    mat4 factor;
    vec4 shift;
    };

    uniform sampler2D uSampler;
    uniform ColorFilter uColorFilter;
    varying vec2 vTextureCoord;
    void main() {
    gl_FragColor = texture2D(uSampler, vTextureCoord);
    vec4 originalColor = texture2D(uSampler, vTextureCoord);
    vec4 filteredColor = (originalColor * uColorFilter.factor) + uColorFilter.shift;
    gl_FragColor = originalColor - filteredColor;

    }

    这是我们将滤色器传递给着色器的方法:
    private void attachColorFilter(int program) {
    final float[] colorFilterFactor = new float[4 * 4];
    final float[] colorFilterShift = new float[4];
    for (int i = 0; i < mColorFilter.length; i++) {
    final float value = mColorFilter[i];
    final int calculateIndex = i + 1;
    if (calculateIndex % 5 == 0) {
    colorFilterShift[calculateIndex / 5 - 1] = value / 255;
    } else {
    colorFilterFactor[i - calculateIndex / 5] = value;
    }
    }
    final int colorFactorLocation = GLES20.glGetUniformLocation(program, "uColorFilter.factor");
    GLES20.glUniformMatrix4fv(colorFactorLocation, 1, false, colorFilterFactor, 0);
    final int colorShiftLocation = GLES20.glGetUniformLocation(program, "uColorFilter.shift");
    GLES20.glUniform4fv(colorShiftLocation, 1, colorFilterShift, 0);
    }

    您还需要在 onSurfaceChanged方法中调用此方法:
    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
    GLES20.glViewport(0, 0, width, height);
    final int program = loadProgram();
    GLES20.glUseProgram(program);
    initVertices(program);
    attachTexture(program);
    attachColorFilter(program);
    }

    Alpha channel 融合

    在最开始设置此参数: hostView.setEGLConfigChooser(8, 8, 8, 8, 0, 0);时,我们实际上在OpenGL上下文中为Alpha channel 添加了缓冲区。否则,我们将始终为输出图像获取一些背景(考虑到png图像对于某些像素往往具有不同的alpha channel ,这是不正确的)。坏消息是它破坏了alpha混合机制,对于某些特殊情况,您将获得意想不到的颜色。好消息-我们可以轻松修复它。首先,我们需要在片段着色器中应用alpha混合:
    precision mediump float;
    struct ColorFilter {
    mat4 factor;
    vec4 shift;
    };
    uniform sampler2D uSampler;
    uniform ColorFilter uColorFilter;
    varying vec2 vTextureCoord;
    void main() {
    vec4 originalColor = texture2D(uSampler, vTextureCoord);
    originalColor.rgb *= originalColor.a;
    vec4 filteredColor = (originalColor * uColorFilter.factor) + uColorFilter.shift;
    filteredColor.rgb *= filteredColor.a;
    gl_FragColor = originalColor - filteredColor
    gl_FragColor = vec4(originalColor.rgb - filteredColor.rgb, originalColor.a);

    }

    我还建议将blend函数设置为以下内容,以便我们的输出不受颜色缓冲区中当前存在的任何影响,并且行为更接近Android的 ImageView 。但是,我们没有将颜色设置为清晰的颜色,并且似乎没有任何改变:
    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
    GLES20.glEnable(GLES20.GL_BLEND);
    GLES20.glBlendFunc(GLES20.GL_ONE, GLES20.GL_ZERO);
    }

    发布结果

    我们几乎做到了。剩下的唯一事情就是将结果返回给调用方。首先,让我们从 GLSurfaceView获取位图,我从 another stackoverflow answer借出了一个出色的解决方案:
    private Bitmap retrieveBitmapFromGl(int width, int height) {
    final ByteBuffer pixelBuffer = ByteBuffer.allocateDirect(width * height * PrimitiveSizes.FLOAT);
    pixelBuffer.order(ByteOrder.LITTLE_ENDIAN);
    GLES20.glReadPixels(0,0, width, height, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, pixelBuffer);
    final Bitmap image = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
    image.copyPixelsFromBuffer(pixelBuffer);
    return image;
    }

    现在只需获取位图,检查错误并返回结果:
    private GLException getGlError() {
    int errorValue = GLES20.glGetError();
    switch (errorValue) {
    case GLES20.GL_NO_ERROR:
    return null;
    default:
    return new GLException(errorValue);
    }
    }

    private void postResult() {
    if (mFinished) {
    return;
    }
    final GLSurfaceView hostView = mHostViewReference.get();
    if (hostView == null) {
    return;
    }
    GLException glError = getGlError();
    if (glError != null) {
    hostView.post(() -> {
    mCallback.onFailure(glError);
    removeHostView(hostView);
    });
    } else {
    final Bitmap result = retrieveBitmapFromGl(mBitmap.getWidth(), mBitmap.getHeight());
    hostView.post(() -> {
    mCallback.onSuccess(result);
    removeHostView(hostView);
    });
    }
    mFinished = true;
    }

    private void removeHostView(@NonNull GLSurfaceView hostView) {
    if (hostView.getParent() == null) {
    return;
    }
    final WindowManager windowManager = (WindowManager) hostView.getContext().getSystemService(Context.WINDOW_SERVICE);
    Objects.requireNonNull(windowManager).removeView(hostView);
    }

    并从 onDrawFrame方法中调用它:
    @Override
    public void onDrawFrame(GL10 gl) {
    GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
    GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
    postResult();
    }

    结果

    现在,让我们来看看刚刚制作的实用程序。让我们从0滤镜开始,这样它就不会在任何 channel 上影响我们的原始图像:

    代码
    BlendingFilterUtil.subtractMatrixColorFilter(bitmap, new float[]{
    0, 0, 0, 0, 0,
    0, 0, 0, 0, 0,
    0, 0, 0, 0, 0,
    0, 0, 0, 0, 0
    }, activity, callback);

    输出

    Original image

    原始图像在左侧,减去滤镜的图像在右侧。正如预期的那样,它们是相同的。现在,让我们做一些更令人兴奋的事情,例如完全删除红色和绿色 channel :

    代码
    BlendingFilterUtil.subtractMatrixColorFilter(bitmap, new float[]{
    1, 0, 0, 0, 0,
    0, 1, 0, 0, 0,
    0, 0, 0, 0, 0,
    0, 0, 0, 1, 0
    }, activity, callback);

    输出

    Blue image

    现在,输出中只有蓝色 channel ,两个余数被完全减去。让我们尝试一下OP在他的问题中提供的过滤器:

    代码
    BlendingFilterUtil.subtractMatrixColorFilter(bitmap, new float[]{
    0.393f, 0.7689999f, 0.18899999f, 0, 0,
    0.349f, 0.6859999f, 0.16799999f, 0, 0,
    0.272f, 0.5339999f, 0.13099999f, 0, 0,
    0, 0, 0, 1, 0
    }, activity, callback);

    输出

    Subtracted fiter image

    要旨

    如果您在任何步骤上都遇到困难,请随时使用上述实用程序的完整代码引用 the gist

    希望你们对这个漫长的帖子不会太无聊。我只是试图简要地解释它是如何工作的,所以可能有些含糊。让我知道是否出现问题或不一致。

    关于java - 在Android中使用ColorMatrixFilter减去混合模式?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33358030/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com