2015-06-07 19 views
7

Popularna gra Words with Friends rysuje płytek z literami na planszy jako jeden podmiot -Jak utworzyć wypukłość wokół mapy bitowej?

Można zobaczyć żółty gradient liniowy stosowane do wszystkich płytek literowych w poniższym zrzucie ekranu, a także wpływ etapie wytłaczania na krawędzi :

app screenshot

W my word game chciałbym mieć podobne efekty:

emboss example

Więc tworzę planszy o wymiarach mBitmap, a następnie wyciągnąć wszystkie płytki do niego i wreszcie narysować bitmapę do my custom view -

Setup:

setLayerType(View.LAYER_TYPE_SOFTWARE, null); 

// create yellow linear gradient 
mGradStart = new Point(3 * mWidth/4, mHeight/3); 
mGradEnd = new Point(mWidth/4, 2 * mHeight/3); 
LinearGradient gradient = new LinearGradient(
     mGradStart.x, 
     mGradStart.y, 
     mGradEnd.x, 
     mGradEnd.y, 
     new int[]{ 0xCCFFCC00, 0xCCFFCC99, 0xCCFFCC00 }, 
     null, 
     TileMode.CLAMP); 

// create the big bitmap holding all tiles 
mBitmap = Bitmap.createBitmap(mWidth, mHeight, Bitmap.Config.ARGB_8888); 
mCanvas = new Canvas(mBitmap); 

mPaintGrad = new Paint(); 
mPaintGrad.setShader(gradient); 

mPaintEmboss = new Paint(); 
mPaintEmboss.setShader(gradient); 

EmbossMaskFilter filter = new EmbossMaskFilter(
    new float[] { 0f, 1f, 0.5f }, 0.8f, 3f, 3f); 
mPaintEmboss.setMaskFilter(filter); 

Rysunek:

@Override 
protected void onDraw(Canvas canvas) { 
    mGameBoard.draw(canvas); 

    // draw all tiles as rectangles into big bitmap 
    // (this code will move to onTouchEvent later) 
    mBitmap.eraseColor(Color.TRANSPARENT); 
    for (SmallTile tile: mTiles) { 
     mCanvas.drawRect(
       tile.left, 
       tile.top, 
       tile.left + tile.width, 
       tile.top + tile.height, 
       mPaintGrad); 
     tile.draw(mCanvas); 
    } 
    canvas.drawBitmap(mBitmap, 0, 0, mPaintEmboss); // emboss NOT displayed 
    canvas.drawText("TEXT WORKS OK", 400, 400, mPaintEmboss); // ebmoss OK 
    canvas.drawRect(300, 600, 800, 1200, mPaintEmboss); // emboss OK 
} 

The EmbossMaskFilter efekt działa OK z drawText() i drawRect() połączeń, ale to nie działa dla drawBitmap():

app screenshot

Moje pytanie: to możliwe, aby korzystać z niektórych kombinacji PorterDuff.Mode narysować (i extractAlpha?) wygładzić wokół mojej wielkiej bitmapy?

UPDATE:

Patrząc na HolographicOutlineHelper.java I udało się dodać zewnętrzną cień:

app screenshot

z następującego kodu w MyView.java -

Setup:

public MyView(Context context, AttributeSet attrs) { 
    super(context, attrs); 

    mScale = getResources().getDisplayMetrics().density; 
    mGradStart = new Point(3 * mWidth/4, mHeight/3); 
    mGradEnd = new Point(mWidth/4, 2 * mHeight/3); 
    LinearGradient gradient = new LinearGradient(
      mGradStart.x, 
      mGradStart.y, 
      mGradEnd.x, 
      mGradEnd.y, 
      new int[]{ 0xCCFFCC00, 0xCCFFCC99, 0xCCFFCC00 }, 
      null, 
      TileMode.CLAMP); 

    mBitmap = Bitmap.createBitmap(mWidth, mHeight, Bitmap.Config.ARGB_8888); 
    mCanvas = new Canvas(mBitmap); 

    mPaintGrad = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG); 
    mPaintGrad.setShader(gradient); 

    mPaintBlur = new Paint(); 
    mPaintBlur.setColor(Color.BLACK); 
    BlurMaskFilter blurFilter = new BlurMaskFilter(mScale * 1, Blur.OUTER); 
    mPaintBlur.setMaskFilter(blurFilter); 
} 

Rysunek:

private void prepareBitmaps() { 
    mBitmap.eraseColor(Color.TRANSPARENT); 
    for (SmallTile tile: mTiles) { 
     mCanvas.drawRect(
       tile.left, 
       tile.top, 
       tile.left + tile.width, 
       tile.top + tile.height, 
       mPaintGrad); 
     tile.draw(mCanvas); 
    } 

    mAlphaBitmap = mBitmap.extractAlpha(mPaintBlur, mOffset); 
} 

@Override 
protected void onDraw(Canvas canvas) { 
    mGameBoard.draw(canvas); 
    canvas.drawBitmap(mAlphaBitmap, mOffset[0], mOffset[1], mPaintBlur); 
    canvas.drawBitmap(mBitmap, 0, 0, mPaintGrad); 
} 

ale niestety aplikacja działa powoli teraz - a ja nadal nie wiem jak dodać etapie wytłaczania efekt wokół bitmapy.

Odpowiedz

5

Nie jestem pewien, mam exacly co trzeba, ale jeśli chcesz się po prostu zastosować EmbossMaskFilter wokół jakiegoś listu png z kanałem alfa, można bardzo dużo zrobić ten trick z

EmbossMaskFilter filter = new EmbossMaskFilter(new float[]{1, 1, 1}, 0.5f, 0.6f, 2f); 

Paint paintEmboss = new Paint(); 
paintEmboss.setMaskFilter(embossMaskFilter); 

Bitmap helperBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); 
Canvas helperCanvas = new Canvas(helperBitmap); 

Bitmap alpha = src.extractAlpha(); 
helperCanvas.drawBitmap(alpha, 0, 0, paintEmboss); 
alpha.recycle(); 

... 
canvas.drawBitmap(helperBitmap, 0, 0, anyPaint); 

nigdy nie będzie chcesz cały ten kod w 1 onDraw, ponieważ tworzy wiele obiektów w pamięci. Za każdym razem src.extractAlpha(); tworzy nową bitmapę. (Btw zawsze wydostać się z błędu pamięci z git projektu.Dodano mAlphaBitmap.recycle(); i może co najmniej rozruchu, ale nadal opóźnia się jak cholera)

Tak, grałem z repozytorium git i dostałem kilka wyników. Oto demo obraz i git repo of first commit:

enter image description here

Ale potem zdałem sobie sprawę, że nie trzeba EmbossMaskFilter wokół liter, trzeba je wokół prostokątów. I można to zrobić w podobny sposób. Oto jak to zrobić:

  1. Tworzenie nowego pomocnika statycznego Bitmap i płótna do wytłoczenia tle, podobnie jak mAlphaBitmap
  2. na każdym prepareBitmaps() rects farby na pomocnika bitmapy. Jednolity kolor bez alfa.
  3. Wyciąg alfa z utworzonej mapy bitowej jak ten Bitmap alpha = helperCanvas.extractAlpha();
  4. Draw ekstrakcji alfa bitmapy na pomocnika z farbą z wytłoczenia filtrem helperCanvas.drawBitmap(alpha, 0, 0, paintEmboss);
  5. W OnDraw druku helperBitmap z jakimś alfa przed głównym bitmapy.

Oto zrzut ekranu bez alfa (ponieważ jest o wiele łatwiej zobaczyć kształty ten sposób) enter image description here

Oto git demo tej wersji: https://github.com/varren/AndroidEmbossMaskFilterForPng/blob/1d692d576e78bd434252a8a6c6ad2ee9f4c6dbd8/app/src/main/java/de/afarber/mytiles2/MyView.java

I tu jest istotnym elementem kodu, który zmieniłem w Twoim projekcie:

private static final EmbossMaskFilter filter = 
       new EmbossMaskFilter(new float[]{1, 1, 1}, 0.5f, 0.6f, 2f); 
private static Canvas helperCanvas; 
private static Paint paintEmboss; 

public Canvas getHelperCanvas(int width, int height){ 
    if (mAlphaBitmap == null) { 
     mAlphaBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); 
     helperCanvas = new Canvas(mAlphaBitmap); 
     paintEmboss = new Paint(); 
     paintEmboss.setColor(Color.BLACK); 
    } 
    return helperCanvas; 
} 

private void prepareBitmaps() { 
    mCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); 
    helperCanvas = getHelperCanvas(mBitmap.getWidth(),mBitmap.getHeight()); 
    helperCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); 
    paintEmboss.setMaskFilter(null); 
    paintEmboss.setAlpha(255); 

    for (SmallTile tile: mTiles) { 
     if (!tile.visible) continue; 
     helperCanvas.drawRect(tile.left,tile.top,tile.left + tile.width, 
       tile.top + tile.height,paintEmboss); 
     mCanvas.drawRect(tile.left, tile.top,tile.left + tile.width, 
       tile.top + tile.height, mPaintGrad); 
     tile.draw(mCanvas); 
    } 

    paintEmboss.setMaskFilter(filter); 
    Bitmap alpha = mAlphaBitmap.extractAlpha(); 
    helperCanvas.drawBitmap(alpha, 0, 0, paintEmboss); 
} 

protected void onDraw(Canvas canvas) { 
    // ... 
    paintEmboss.setAlpha(255); //todo change alpha here 
    if(mAlphaBitmap!= null)canvas.drawBitmap(mAlphaBitmap, 0,0, paintEmboss); 
    if(mBitmap!= null)canvas.drawBitmap(mBitmap, 0, 0, mPaintGrad); 
    // ... 
} 

Ostatni krok 3-d Wykonane jest przeniesienie wszystkiego z onDraw do prepareBitmaps(), a wstępne ustawienie jest już w porządku, ale mamy zmienione zniekształcenie tekstu. więc tutaj jest source code dla tego kroku.

A tutaj jest dobrze działa final solution. Przeniesienie wszystkich farb za pomocą filtrów rozwiązało problemy związane z preformancją, ale sądzę, że wciąż istnieją lepsze opcje do ich wdrożenia.Jak powiedziałem erlier nie wiem, jest to, co trzeba, ale to dość dużo kodu tworzy Płaskorzeźba wokół Bitmap

PS: trochę fajny efekt, gdy podział i komórki zsumowanie

PS2: new EmbossMaskFilter(new float[] { 0f, 1f, 0.5f }, 0.8f, 3f, 3f); to nie będzie wyglądać to samo na różnych urządzeniach o różnej rozdzielczości ekranu:

+0

Dzięki ... Możesz sprawić, aby wygląd wyglądał tak samo poprzez pomnożenie przez 'mScale = getResources(). GetDisplayMetrics(). Density;' –

+1

Tak, chciałbym tylko podkreślić, że może to być problem w kodzie demo, a efekt emboss może być trudny zobaczyć z powodu rozdzielczości ekranu. – varren

2

Oto sugestia za pomocą niestandardowego układu.

Będziesz potrzebował własnego układu dla planszy scrabble. Ponieważ jest to siatka, kodowanie powinno być dość łatwe.

Podstawową ideą jest posiadanie zestawu obrazów w cieniu PNG, po jednym dla każdego typu kombinacji sąsiadujących komórek. W swoim layoucie onDraw() najpierw narysuj cienie, a następnie narysuj kafelek onLayout().

W onDraw(), iteruj po tablicy symboli zastępczych. Jeśli masz kafelek, a następnie dla każdej krawędzi, sprawdź sąsiednie komórki. W zależności od tego, co sąsiaduje, wybierz odpowiedni obraz cienia i narysuj go.

Możesz znacznie zmniejszyć liczbę obrazów cienia, mając obraz cienia, który jest dokładnie szerokości płytki, a następnie specjalizując obszar narożny: jeden dla 270 stopni, drugi dla wyrównania prostego, jeden dla 90 stopni.

Nie wiem, czy korzystanie z portera-duffa może pomóc, ponieważ nadal trzeba ustalić wszystkie te "skrajne" przypadki (gra słów nie jest przeznaczona).

+0

Tak, tak działa program Word with Friends (patrząc na ich liczne zasoby graficzne) ... ale chciałbym znaleźć programowy sposób narysowania wytłoczenia wokół wszystkich płytek z literami . –

Powiązane problemy