2013-02-10 11 views
29

Więc próbowałem kodu z tutaj: Creating an ImageView with a mask. Używam następujących obrazów jak oryginalne i maski:Android jak zastosować maskę na ImageView?

enter image description hereenter image description here

Jednak wynik otrzymuję to:

enter image description here

Zauważ, że okno nie jest czarne tło, ale światło holo (które w nexusie galaktyki wygląda na bardzo jasnoszare, nie całkowicie białe). Drugi obraz to wynik, który pojawia się, gdy element jest wybrany w widoku listy.

Jeśli zamiast tworzyć nową bitmapę za pomocą tego samego algorytmu, a następnie przekazać je do widoku obrazu zamiast przesłanianie OnDraw(), to rysuje poprawnie:

Canvas canvas = new Canvas(); 
Bitmap mainImage = //get original image 
Bitmap maskImage = //get mask image 
Bitmap result = Bitmap.createBitmap(mainImage.getWidth(), mainImage.getHeight(), Bitmap.Config.ARGB_8888); 

canvas.setBitmap(result); 
Paint paint = new Paint(); 
paint.setFilterBitmap(false); 

canvas.drawBitmap(mainImage, 0, 0, paint); 
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN)); 
canvas.drawBitmap(maskImage, 0, 0, paint); 
paint.setXfermode(null); 

imageView.setImageBitmap(result); 

uzyskać oczekiwany wynik:

enter image description here

Pamiętaj, że zanikanie zostało poprawnie zastosowane. Jest to bardziej oczywiste, gdy dokonuje się selekcji.

Co się dzieje w metodzie onDraw ImageView, aby stworzyć to czarne tło zamiast przepuszczać tło okna? Co ciekawe jest to, że jeśli sam obraz oryginału ma pewną przejrzystość, że przejrzystość jest przestrzegana, na przykład:

enter image description here

nie mogę zrozumieć to sam. Wolałbym móc to zrobić na onDraw zamiast wstępnego tworzenia bitmapy, ponieważ działa ona tylko dla bitmap jako źródło i maska. Chcę móc to zrobić z innymi rysunkami, takimi jak gradienty i jednolite kolory, ale w takich przypadkach szerokość i wysokość nie są ustawione.

+1

Oto aktualizacja. Po przeczytaniu http://stackoverflow.com/questions/3467334/erase-bitmap-parts-using-porterduff-mode mógłbym odtworzyć to samo czarne zachowanie na działającym przykładzie, jeśli ustawię konfigurację Bitmapy na RGB_565 zamiast ARGB_8888. Mogłem również zawęzić problem do płótna przekazywanego w metodzie onDraw. Jeśli użyję płótna onDraw, pojawi się czarna ramka.Ponieważ płótno przekazywane do onDraw nie ma powiązanej bitmapy, może natywnie nie obsługuje przezroczystości? Inną rzeczą, którą odkryłem, jest to, że czarna ramka znika, gdy przewija się widok listy. – AngraX

+1

Ok. Po przeczytaniu tego http://stackoverflow.com/questions/5231260/android-shader-behave-different-in-ondrawcanvas-and-new-canvasbitmap wygląda na to, że błąd polega na tym, że robię piksele z przezroczystego okna. jeśli zastosuję to na kanwie przekazanej do onDraw(). A za oknem jest czarne tło. Wygląda na to, że nie mam innego wyjścia niż użycie tymczasowej bitmapy i renderowanie jej, chyba że znajdę inny tryb DST, który działa. – AngraX

+0

Próbuję zaimplementować podobną funkcjonalność, w której mam jeden obraz z obrazem, a drugą z czarną maską, po dotknięciu powinien pokazywać za obrazem w kręgu jako twój pokaz, czy możesz mi pomóc tutaj? – ViVekH

Odpowiedz

25

Znalazłem idealną kombinację do tworzenia maskowania bez czarnego obramowania po przejrzeniu wszystkich stackoverflow postów. Bardzo dobrze pasuje do mojego celu.

Obecnie tworzę widok przeciągalny przy użyciu jednego normalnego obrazu i obrazu maskującego (png z przezroczystością), więc będę musiał zastąpić funkcję onDraw.

private Bitmap mImage = ...; 
private Bitmap mMask = ...; // png mask with transparency 
private int mPosX = 0; 
private int mPosY = 0; 

private final Paint maskPaint; 
private final Paint imagePaint; 

public CustomView (final Context context) { 
    maskPaint = new Paint(); 
    maskPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); 

    imagePaint = new Paint(); 
    imagePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OVER)); 
} 

/* TODO 
if you have more constructors, make sure you initialize maskPaint and imagePaint 
Declaring these as final means that all your constructors have to initialize them. 
Failure to do so = your code won't compile. 
*/ 

@Override 
public void onDraw(Canvas canvas) { 
    super.onDraw(canvas); 

    canvas.save();  
    canvas.drawBitmap(mMask, 0, 0, maskPaint); 
    canvas.drawBitmap(mImage, mPosX, mPosY, imagePaint); 
    canvas.restore(); 
} 
+1

Hej. Właśnie sprawdziłem logikę po przeczytaniu kilku artykułów na temat trybów Xfer i wierzę, że twoje rozwiązanie działa całkiem nieźle. Przyjmowanie go jako oficjalnej odpowiedzi. Aby uzyskać więcej informacji na temat trybów xfer, zobacz: http://www.piwai.info/transparent-jpegs-done-right/ i http://ssp.impulsetrain.com/2013-03-17_Porter_Duff_Compositing_and_Blend_Modes.html – AngraX

+0

W przypadku algorytmu powyżej nie działało, zmień ustawienie setXfermode maskPaint na PorterDuff.Mode.DST_OUT. – morph85

+0

Działało w obu trybach: CLEAR i DST_OUT. Interesujące przez, z dokumentacji, którą czytałem o trybach Xfer, CLEAR powinien wyjaśnić całą sprawę, ale na Androidzie działa dokładnie tak jak DST_OUT zamiast. BTW, używam teraz shaderów zamiast trybów xfer, więc wykonuję 1 mniej karnetu, co powoduje, że mój overdraw spada z czerwonych do zielonych :). – AngraX

2

Odpowiadając na moje własne pytanie. Xfermode działał zgodnie z przeznaczeniem. Farba sprawiała, że ​​powstały płótna były przezroczyste (co było płótnem używanym przez działanie okna). Ponieważ płótno było przezroczyste, okno pokazywało, co za nim stoi: czarne tło.

Aby zrobić to poprawnie, należy utworzyć nową bitmapę, aby zachować wynik maski alfa. Zaktualizowałem kod, aby uwzględnić rysunki wszystkich typów.

0

niniejszego Kodeksu:

mask_over = BitmapFactory.decodeResource(
      getResources(), mask_over1[0]); 
    icon = Bitmap.createScaledBitmap(icon, screenwidth, screenwidth, false); 
    mask_over = Bitmap.createScaledBitmap(mask_over, screenwidth, screenwidth, false); 
      back_img=createBitmap_ScriptIntrinsicBlur(Bitmap.createScaledBitmap(cropview.croppedImage, screenwidth, screenwidth, false),25.0f); 
    LinearLayout.LayoutParams layoutParams111 = new LinearLayout.LayoutParams(screenwidth, screenwidth); 
Powiązane problemy