Dessin sur toile-PorterDuff.Mode.CLAIRE tirages noir! Pourquoi?

j'essaie de créer une vue personnalisée qui fonctionne simplement: il y a un Bitmap qui est révélé par arc path - de 0deg à 360deg. Les degrés changent avec certains MF.

donc j'ai fait une vue personnalisée avec overridden onDraw() méthode:

@Override
protected void onDraw(Canvas canvas) {

    canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
    arcPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
    canvas.drawArc(arcRectF, -90, currentAngleSweep, true, arcPaint);
    arcPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
    canvas.drawBitmap(bitmap, circleSourceRect, circleDestRect, arcPaint);
}

arcPaint est initialisé comme suit:

arcPaint = new Paint();
arcPaint.setAntiAlias(true);
arcPaint.setColor(Color.RED); // Color doesn't matter

maintenant, tout dessine bien, mais... le fond est noir dans la vue entière.

Si j'ai mis canvas.drawColor(..., PorterDuff.Mode.DST) sans canvas.drawBitmap() - l'arc est dessiné correctement sur arrière-plan transparent.

Ma question est - comment définir PorterDuff modes pour le faire fonctionner avec la transparence?

bien sûr bitmap est PNG 32 bits avec canal alpha.

18
demandé sur cadavre 2013-08-22 22:08:52

6 réponses

PorterDuff.Mode.CLEAR ne fonctionne pas avec l'accélération matérielle. Just set

view.setLayerType(View.LAYER_TYPE_SOFTWARE,null); 

Fonctionne parfaitement pour moi.

16
répondu Nitesh Tarani 2017-06-17 18:23:17

Utilisez cette instruction lors de l'initialisation de la vue

setLayerType(LAYER_TYPE_HARDWARE, null);
5
répondu Mukesh Kumar 2017-07-21 04:09:42

tout est ok dans votre code sauf une chose: vous obtenez le fond noir parce que votre fenêtre est opaque. Pour obtenir un résultat transparent, vous devez dessiner sur un autre bitmap. Dans votre méthode onDraw s'il vous plaît un Créer nouveau bitmap et faire toute la portée sur elle. Ensuite, dessinez cette image sur votre toile.

pour plus de détails et un exemple de code, veuillez lire c'est ma réponse:

2
répondu Robert 2017-05-23 12:24:59

pour résoudre indésirables PorterDuff effet

utilisez la méthode la plus simple au début, comme le problème de L'OP, a Path.arcTo(*, *, *, *, false) -- note arcTo, pas addArc et false signifie no forceMoveTo avant d'ajouter arc -- il n'y a pas besoin de PorterDuff.

Path arcPath = new Path();
@Override
protected void onDraw(Canvas canvas) {
    arcPath.rewind();
    arcPath.moveTo(arcRectF.centerX, arcRectF.centerY);
    arcPath.arcTo(arcRectF, -90, currentAngleSweep, false);
    arcPath.close();
    canvas.clipPath(arcPath, Region.Op.DIFFERENCE);
    canvas.drawBitmap(bitmap, circleSourceRect, circleDestRect, arcPaint);
}

si vous avez vraiment besoin de PorterDuff, principalement pour le modelage complexe des couleurs, comme le mélange des gradients, ne dessinez pas la couleur ou la forme ou bitmap avec L'effet de filtrage PorterDuff directement sur la toile par défaut fournie en onDraw(Canvas), utilisez un tampon / lest bitmap [s] avec le canal alpha -- et setHasAlpha(true) -- pour stocker le résultat du filtrage de PorterDuff, dessinez enfin le bitmap sur la toile par défaut sans appliquer de filtrage sauf changement de matrice.

voici un exemple de travail pour créer une image ronde floue:

import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.RadialGradient;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Shader;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.widget.ImageView;

/**
* Created by zdave on 6/22/17.
*/

public class BlurredCircleImageViewShader extends ImageView {
private Canvas mCanvas;
private Paint mPaint;
private Matrix matrix;
private static final float GRADIENT_RADIUS = 600f;  //any value you like, but should be big enough for better resolution.
private Shader gradientShader;
private Bitmap bitmapGradient;
private Bitmap bitmapDest;
private Canvas canvasDest;
public BlurredCircleImageViewShader(Context context) {
    this(context, null);
}

public BlurredCircleImageViewShader(Context context, @Nullable AttributeSet attrs) {
    this(context, attrs, 0);
}

public BlurredCircleImageViewShader(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    matrix = new Matrix();
    int[] colors = new int[]{Color.BLACK, Color.BLACK, Color.TRANSPARENT};
    float[] colorStops = new float[]{0f, 0.5f, 1f};
    gradientShader = new RadialGradient(GRADIENT_RADIUS, GRADIENT_RADIUS, GRADIENT_RADIUS, colors, colorStops, Shader.TileMode.CLAMP);
    mPaint.setShader(gradientShader);

    bitmapGradient = Bitmap.createBitmap((int)(GRADIENT_RADIUS * 2), (int)(GRADIENT_RADIUS * 2), Bitmap.Config.ARGB_8888);
    bitmapDest = bitmapGradient.copy(Bitmap.Config.ARGB_8888, true);

    Canvas canvas = new Canvas(bitmapGradient);
    canvas.drawRect(0, 0, GRADIENT_RADIUS * 2, GRADIENT_RADIUS * 2, mPaint);

    canvasDest = new Canvas(bitmapDest);
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    int width = getMeasuredWidth();
    setMeasuredDimension(width, width);
}

@Override
protected void onDraw(Canvas canvas){
    /*uncomment each of them to show the effect, the first and the third one worked, the second show the same problem as OP's*/
    //drawWithLayers(canvas);  //unrecommended.
    //drawWithBitmap(canvas);  //this shows transparent as black
    drawWithBitmapS(canvas);   //recommended.
}
@SuppressLint("WrongCall")
private void drawWithLayers(Canvas canvas){
    mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
    float width = canvas.getWidth();
    float hWidth = width / 2;
    //both saveLayerAlpha saveLayer worked here, and if without either of them,
    //the transparent area will be black.
    //int count = canvas.saveLayerAlpha(0, 0, getWidth(), getHeight(), 255, Canvas.ALL_SAVE_FLAG);
    int count = canvas.saveLayer(0, 0, getWidth(), getHeight(), null, Canvas.ALL_SAVE_FLAG);
    super.onDraw(canvas);
    float scale = hWidth/GRADIENT_RADIUS;
    matrix.setTranslate(hWidth - GRADIENT_RADIUS, hWidth - GRADIENT_RADIUS);
    matrix.postScale(scale, scale, hWidth, hWidth);
    gradientShader.setLocalMatrix(matrix);

    canvas.drawRect(0, 0, width, width, mPaint);

    canvas.restoreToCount(count);
}
@SuppressLint("WrongCall")
private void drawWithBitmap(Canvas canvas){
    super.onDraw(canvas);
    float scale = canvas.getWidth() / (GRADIENT_RADIUS * 2);
    matrix.setScale(scale, scale);
    mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
    canvas.drawBitmap(bitmapGradient, matrix, mPaint);  //transparent area is still black.
}
@SuppressLint("WrongCall")
private void drawWithBitmapS(Canvas canvas){
    float scale = canvas.getWidth() / (GRADIENT_RADIUS * 2);
    int count = canvasDest.save();
    canvasDest.scale(1/scale, 1/scale); //tell super to draw in 1/scale.
    super.onDraw(canvasDest);
    canvasDest.restoreToCount(count);

    mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
    canvasDest.drawBitmap(bitmapGradient, 0, 0, mPaint);

    matrix.setScale(scale, scale);  //to scale bitmapDest to canvas.
    canvas.drawBitmap(bitmapDest, matrix, null);
    }
 }

quelques notes: 1, de ce point de vue s'étend ImageViewView, il y a quelques différences.

2, pourquoi drawWithLayers--saveLayer ou saveLayerAlpha-- est non recommandé: a, Ils sont incertains, parfois ça ne marche pas bien (montrer transparent comme noir), surtout pour View whoes onDraw(Canvas) est vide, alors que ImageView.onDraw(Canvas) utilisé un Drawable pour en dessiner; b, ils sont chers, ils distribuent off-screen bitmap pour stocker le résultat du tirage temporaire, et il n'y a pas d'indices clairs d'un quelconque mécanisme de recyclage des ressources.

3, en utilisant votre propre bitmap[ s], est meilleur pour le recyclage personnalisé des ressources.

certains ont dit, c'est impossible d'utiliser PorterDuff sans allocation bitmap [s] chaque dessin, parce que la largeur du bitmap, la hauteur ne peut pas être déterminée avant le dessin/layout/measure.

vous pouvez utiliser le tampon bitmap [s] pour le dessin de PorterDuff:

au début, allouer un peu assez grand bitmap [s].

puis, dessinez sur le bitmap[s]avec une matrice.

et le, dessinez le bitmap [s] dans un bitmap dest avec une matrice.

enfin, dessinez le bitmap dest dans toile avec de la matrice.



certaines personnes recommandent setLayerType (View.LAYER_TYPE_SOFTWARE, null), qui n'est pas une option pour moi, car il va provoquer onDraw(Canvas) à être appelé dans une boucle.

image résultat image source

1
répondu zdave 2017-06-22 13:58:03

si vous avez le fond de la couleur solide tout ce que vous devez faire est de mettre la couleur de la peinture à votre couleur de fond. Par exemple, si vous avez un fond blanc, vous pouvez faire:

paint.setColor(Color.WHITE);

cependant, si vous devez effacer une ligne avec un fond transparent vous essayez ceci: Afin de dessiner avec une couleur transparente, vous devez utiliser Paint setXfermode qui ne fonctionnera que si vous mettez un bitmap sur votre toile. Si vous suivez les étapes ci-dessous, vous devriez obtenir le résultat souhaité.

Créer un canvas et placer son bitmap.

mCanvas = new Canvas();
mBitmap= Bitmap.createBitmap(scrw, scrh, Config.ARGB_8888);
mCanvas.setBitmap(mBitmap);
When you want to erase something you just need to use setXfermode.

public void onClickEraser() 
{ 
   if (isEraserOn)
      mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
   else
      mPaint.setXfermode(null);
}

Maintenant vous devriez pouvoir dessiner avec une couleur transparente en utilisant:

mCanvas.drawPath(path, mPaint);
0
répondu Ishan 2015-12-08 10:00:23

il suffit de sauvegarder la toile et de restaurer après

canvas.saveLayer(clipContainer, null, Canvas.ALL_SAVE_FLAG);
canvas.drawRoundRect(rectTopGrey, roundCorners, roundCorners, greyPaint);
canvas.drawRoundRect(rectTopGreyClip, roundCorners, roundCorners, clipPaint);
canvas.restore();

dans ce cas, le premier rectangle servira de destination pour https://developer.android.com/reference/android/graphics/PorterDuff.Mode et le second rectangle comme source

0
répondu Mykola Tychyna 2018-10-02 05:48:35