2012-08-07 23 views
15

Quiero pintar gráficos en un lienzo de modo que los colores sean aditivos. Por ejemplo, quiero producir esto:
Proper additive colors¿Cómo pintar con alfa?

Pero en cambio, me sale esto:
My results

Tenga en cuenta que la mitad de fondo blanco, mitad negro es intencional, sólo para ver cómo alfa interactúa tanto con antecedentes. Estaré feliz de tener este trabajo con cualquier fondo. Aquí está mi código:

public class VennColorsActivity extends Activity { 
    @Override 
    public void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     class VennView extends View { 
      public VennView(Context context) { 
       super(context); 
      } 

      @Override 
      protected void onDraw(Canvas canvas) { 
       super.onDraw(canvas); 
       int alpha = 60, val = 255; 
       int ar = Color.argb(alpha, val, 0, 0); 
       int ag = Color.argb(alpha, 0, val, 0); 
       int ab = Color.argb(alpha, 0, 0, val); 
       float w = canvas.getWidth(); 
       float h = canvas.getHeight(); 
       float cx = w/2f; 
       float cy = h/2; 
       float r = w/5; 
       float tx = (float) (r * Math.cos(30 * Math.PI/180)); 
       float ty = (float) (r * Math.sin(30 * Math.PI/180)); 
       float expand = 1.5f; 
       Paint paint = new Paint(); 
       paint.setColor(Color.WHITE); 
       canvas.drawRect(new Rect(0, 0, (int) w, (int) (h/2)), paint); 
       PorterDuff.Mode mode = android.graphics.PorterDuff.Mode.ADD; 
       paint = new Paint(Paint.ANTI_ALIAS_FLAG); 
       paint.setColorFilter(new PorterDuffColorFilter(ar, mode)); 
       paint.setColor(ar); 
       canvas.drawCircle(cx, cy - r, expand * r, paint); 
       paint.setColorFilter(new PorterDuffColorFilter(ag, mode)); 
       paint.setColor(ag); 
       canvas.drawCircle(cx - tx, cy + ty, expand * r, paint); 
       paint.setColorFilter(new PorterDuffColorFilter(ab, mode)); 
       paint.setColor(ab); 
       canvas.drawCircle(cx + tx, cy + ty, expand * r, paint); 
      } 
     } 
     this.setContentView(new VennView(this)); 
    } 
} 

Puede alguien por favor me ayude a entender cómo pintar con colores aditivos en los gráficos de Android?

Respuesta

26

Estás en el camino correcto. Hay 3 principales problemas en su código:

  • Es necesario establecer Xfer modo de filtro de color iso
  • mapa de bits de uso temporal para la prestación de su imagen
  • alfa debe ser 0xFF con el fin de obtener resultados que usted está buscando

Esto es lo que tengo al usar el modo xfer. Lo que estoy haciendo es dibujar todo en un mapa de bits temporal y luego renderizar todo el mapa de bits en el lienzo principal.

xfer mode rules

Usted pregunta ¿Por qué necesita de mapa de bits temp? ¡Buena pregunta! Si está dibujando todo en un lienzo principal, sus colores se mezclarán con el color de fondo del lienzo principal, por lo que se desordenarán todos los colores. El mapa de bits de temperatura transparente ayuda a mantener sus colores alejados de otras partes de UI

Asegúrese de no asignar nada en onDraw() - se agotará la memoria muy pronto de esta manera. También asegúrese de haber reciclado su bitmap de temperatura cuando ya no lo necesites

package com.example.stack2; 

import android.app.Activity; 
import android.content.Context; 
import android.graphics.Bitmap; 
import android.graphics.Bitmap.Config; 
import android.graphics.Canvas; 
import android.graphics.Color; 
import android.graphics.Paint; 
import android.graphics.PorterDuff.Mode; 
import android.graphics.PorterDuffXfermode; 
import android.os.Bundle; 
import android.view.View; 

public class YouAreWelcome extends Activity { 

    Bitmap tempBmp = Bitmap.createBitmap(1, 1, Config.ARGB_8888); 
    Canvas c = new Canvas(); 
    Paint paint = new Paint(); 

    @Override 
    public void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     class VennView extends View { 
      public VennView(Context context) { 
       super(context); 

      } 

      protected void onDraw(Canvas canvas) { 
       super.onDraw(canvas); 
       if(tempBmp.isRecycled() || tempBmp.getWidth()!=canvas.getWidth() || tempBmp.getHeight()!=canvas.getHeight()) 
       { 
        tempBmp.recycle(); 
        tempBmp = Bitmap.createBitmap(canvas.getWidth(), canvas.getHeight(), Config.ARGB_8888); 
        c.setBitmap(tempBmp); 
       } 

        //clear previous drawings 
       c.drawColor(Color.TRANSPARENT, Mode.CLEAR); 

       int alpha = 255, val = 255; 
       int ar = Color.argb(alpha, val, 0, 0); 
       int ag = Color.argb(alpha, 0, val, 0); 
       int ab = Color.argb(alpha, 0, 0, val); 
       float w = canvas.getWidth(); 
       float h = canvas.getHeight(); 
       float cx = w/2f; 
       float cy = h/2; 
       float r = w/5; 
       float tx = (float) (r * Math.cos(30 * Math.PI/180)); 
       float ty = (float) (r * Math.sin(30 * Math.PI/180)); 
       float expand = 1.5f; 
       paint.setAntiAlias(true); 
       paint.setXfermode(new PorterDuffXfermode(Mode.ADD)); 
       paint.setColor(ar); 
       c.drawCircle(cx, cy - r, expand * r, paint); 
       paint.setColor(ag); 
       c.drawCircle(cx - tx, cy + ty, expand * r, paint); 
       paint.setColor(ab); 
       c.drawCircle(cx + tx, cy + ty, expand * r, paint); 
       canvas.drawBitmap(tempBmp, 0, 0, null); 
      } 
     } 
     this.setContentView(new VennView(this)); 
    } 
} 
+1

Muchas gracias, Pavel; ¡y tan rápido también! –

+3

BTW, ¿Conoces una alternativa a PorterDuff.Mode.ADD que sea compatible con SDK versión 8? La documentación dice que se agregó ADD en la versión 8, pero se encuentra. Realmente se introdujo en la versión 11 y se bloquea en algunos dispositivos más antiguos que no deberían haber permitido instalar mi widget. –

+1

¡Esto es genial!¿Puedes aplicar esta técnica a bitmaps que se superponen entre sí? –

2

Gracias de nuevo Pavel. Eso habría sido muy difícil para mí haber descubierto por mi cuenta. Estoy respondiendo mi propia pregunta para profundizar en los detalles, pero acepté la tuya como la mejor respuesta.

Tienes razón en que prefiero no tener que crear y administrar un mapa de bits (y un lienzo) fuera de la pantalla. Esa es esencialmente la razón por la que mencioné que un fondo negro o blanco estaría bien para hacer que esto funcione.

Nunca me preocupa el rendimiento antes de ver algo que funciona, pero comparto su precaución después de ese punto. Editando su versión para arreglar eso y eliminar esos miembros da la implementación a continuación.

¿Es esto robusto? Tenga en cuenta las dos llamadas a Canvas.drawColor() que sospecho que también podrían combinarse en una sola.

package com.superliminal.android.test.venn; 

import android.app.Activity; 
import android.content.Context; 
import android.graphics.Canvas; 
import android.graphics.Color; 
import android.graphics.Paint; 
import android.graphics.PorterDuff.Mode; 
import android.graphics.PorterDuffXfermode; 
import android.os.Bundle; 
import android.view.View; 

public class VennColorsActivity extends Activity { 
    private Paint paint = new Paint(); 

    @Override 
    public void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     class VennView extends View { 
      public VennView(Context context) { 
       super(context); 
      } 

      @Override 
      protected void onDraw(Canvas canvas) { 
       super.onDraw(canvas); 
       canvas.drawColor(Color.BLACK); 
       canvas.drawColor(Color.TRANSPARENT, Mode.CLEAR); 
       int alpha = 255, val = 255; 
       int ar = Color.argb(alpha, val, 0, 0); 
       int ag = Color.argb(alpha, 0, val, 0); 
       int ab = Color.argb(alpha, 0, 0, val); 
       float w = canvas.getWidth(); 
       float h = canvas.getHeight(); 
       float cx = w/2f; 
       float cy = h/2; 
       float r = w/5; 
       float tx = (float) (r * Math.cos(30 * Math.PI/180)); 
       float ty = (float) (r * Math.sin(30 * Math.PI/180)); 
       float expand = 1.5f; 
       paint.setAntiAlias(true); 
       paint.setXfermode(new PorterDuffXfermode(Mode.ADD)); 
       paint.setColor(ar); 
       canvas.drawCircle(cx, cy - r, expand * r, paint); 
       paint.setColor(ag); 
       canvas.drawCircle(cx - tx, cy + ty, expand * r, paint); 
       paint.setColor(ab); 
       canvas.drawCircle(cx + tx, cy + ty, expand * r, paint); 
      } 
     } 
     setContentView(new VennView(this)); 
    } 
} 
+1

esta solución también es correcta. Sugerí que se utilizara un mapa de bits separado, porque generalmente quieres mezclar solo los colores que deseas y no estropearte con el resto de la IU. El único comentario a su código es que la llamada 'canvas.drawColor (Color.BLACK);' es inútil ya que borra toda el área llamando a 'canvas.drawColor (Color.TRANSPARENT, Mode.CLEAR);'. Entonces, en teoría, puede soltar esta línea –

+0

@Melinda Green, ¿cómo puedo cambiar el color de la pintura en cada botón? Haga clic en mi actividad para que pueda dar colores de fondo differnet – Erum

+0

@Erum Hannan Simplemente almacene un color en el método onClick del botón y configúrelo ese color como fondo con canvas.drawColor con él en onDraw. –

Cuestiones relacionadas