Маскирование Drawable / Bitmap на Android

17

В настоящее время я ищу способ использования черно-белого растрового изображения для маскирования альфа-канала другого растрового изображения или Drawable на Android. Мне любопытно, что лучший способ сделать это. У меня, конечно, есть несколько идей, как это сделать, но они не оптимальны.

Мне нужно иметь возможность применять новую маску к изображению так часто (черно-белое растровое изображение будет меняться каждые несколько секунд).

Любая обратная связь о том, как достичь этого, будет с благодарностью.

    
задан Joshua Rodgers 12.04.2010 в 19:08
источник
  • Я пытаюсь использовать черные части растрового изображения маски, чтобы установить альфа-канал соответствующего пикселя на другом растровом / Drawable на 0. –  Joshua Rodgers 12.04.2010 в 21:52

5 ответов

3

У меня это работает, так что это что-то вроде этого

    // we first same the layer, rectF is the area of interest we plan on drawing
    // this will create an offscreen bitmap
    canvas.saveLayer(rectF, null, Canvas.MATRIX_SAVE_FLAG
            | Canvas.CLIP_SAVE_FLAG | Canvas.HAS_ALPHA_LAYER_SAVE_FLAG
            | Canvas.FULL_COLOR_LAYER_SAVE_FLAG
            | Canvas.CLIP_TO_LAYER_SAVE_FLAG);

    // draw our unmasked stuff
    super.draw(canvas);
    // We same a layer again but this time we pass a paint object to mask
    // the above layer
    maskPaint = new Paint()
    maskPaint.setColor(0xFFFFFFFF);
    maskPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY));

    canvas.saveLayer(rectF, maskPaint,
            Canvas.MATRIX_SAVE_FLAG | Canvas.CLIP_SAVE_FLAG
                    | Canvas.HAS_ALPHA_LAYER_SAVE_FLAG
                    | Canvas.FULL_COLOR_LAYER_SAVE_FLAG
                    | Canvas.CLIP_TO_LAYER_SAVE_FLAG);
    // we draw the mask which is black and white. In my case
    // I have a path, and I use a blurPaint which blurs the mask slightly
    // You can do anti aliasing or whatever you which. Just black & white
    canvas.drawPath(path, blurPaint);
    // We restore twice, this merges the results upward
    // as each saveLayer() allocates a new drawing bitmap
    canvas.restore();
    canvas.restore();
    
ответ дан over_optimistic 12.10.2012 в 22:19
  • Если переопределить метод onDraw, вместо super.draw (canvas) должен быть super.onDraw (canvas). В противном случае вы попадаете в бесконечный цикл с ошибкой StackOverflow. –  Speedy 05.04.2013 в 10:21
  • Я сделал то же самое, что и выше, в onDraw своего пользовательского представления, но кажется, что saveLayer дорог, и вместо этого я устанавливаю аппаратный уровень для своего представления (View.setLayerType (LAYER_TYPE_HARDWARE, краска)). Дополнительная информация: stackoverflow.com/a/33483016/4747587 –  Henry 02.11.2015 в 18:12
5

Мое решение близко к решению @ over_optimistic, за исключением одного вызова saveLayer (). Я использую маска Drawable вместо пути, в моем случае это был диск.

Я объявлял эти переменные как поля (рекомендуется распределять память за пределами метода onDraw):

private Paint maskingPaint = new Paint();
private Drawable mask = <insert your drawable here>

Затем, где-то вне onDraw (), настройте объекты:

// Xfermode won't work if hardware accelerated
setLayerType(View.LAYER_TYPE_SOFTWARE, null);

// Using destination shape as a mask
// For a good explanation of PorterDuff transfer modes : http://ssp.impulsetrain.com/porterduff.html
maskingPaint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
maskingPaint.setAntiAlias(true);

// Position the mask
mask.setBounds(<insert your mask bounds here>);

Затем, наконец, метод onDraw () применяет маску:

@Override
protected synchronized void onDraw(Canvas canvas)
{
    // Draw the mask first, making it the PorterDuff destination
    mask.draw(canvas);

    // Save the layer with the masking paint, that will be applied on restore()
    // Using CLIP_TO_LAYER_SAVE_FLAG to avoid any overflow of the masked image outside the mask bounds.
    Rect bounds = mask.getBounds();
    canvas.saveLayer(bounds.left, bounds.top, bounds.right, bounds.bottom, maskingPaint, 
            Canvas.CLIP_TO_LAYER_SAVE_FLAG);

    // Draw the shape offscreen, making it the PorterDuff source
    super.onDraw(canvas);

    // Apply the source to the destination, using SRC_IN transfer mode
    canvas.restore();
}

Для лучшего понимания режимов передачи я упомянул Ссылка . Эта страница довольно интересна для чтения. После этого с таким же кодом вы сможете получить гораздо больше, чем просто маска!

    
ответ дан JM Lord 30.07.2014 в 18:04
  • Идеальное решение. Другие слишком долго. –  Jona 17.02.2015 в 01:46
  • IMO лучше, чем принятый ответ, я предпочитаю рисовать по пути –  patrick-iv 03.03.2017 в 09:36
3

Я сделал маскаратный макет. Это framelayout, где вы можете указать xorderduffmode и маску. Вы можете найти его здесь: Ссылка

    
ответ дан Christophe Smet 26.07.2014 в 13:40
2

Я не совсем понимаю, что вы собираетесь делать, но я считаю, что комбинация BitmapDrawable и LayerDrawable может работать. BitmapDrawable позволит вам использовать ваши растровые изображения в качестве Drawables, а затем вы можете использовать LayerDrawable для выравнивания маски поверх другого Drawable.

    
ответ дан Daniel Lew 12.04.2010 в 20:24
  • Я проверю это, спасибо. –  Joshua Rodgers 12.04.2010 в 21:49
1

Используя пример Xfermodes в API Demo, я смог использовать PorterDuffXfermode, примененный к объекту Paint, чтобы объединить два растровых изображения на холсте. Это работает именно так, как мне это нужно.

    
ответ дан Joshua Rodgers 17.04.2010 в 19:08
  • Не могли бы вы добавить более подробную информацию? –  Casebash 29.04.2010 в 04:25