Как создать тиснение вокруг растрового изображения?
Популярная игра слова с друзьями рисует буквы плитки на игровом поле как единое целое -
Вы можете увидеть желтый линейный градиент , примененный ко всем буквенным плиткам на следующем снимке экрана, а также эффект тиснения на краю:
В моей игре в слова я хотел бы иметь аналогичные эффекты:
Поэтому я создаю игровую доску размером mBitmap
, затем рисую все плитки в нее и, наконец, нарисуйте растровое изображение в мой пользовательский вид -
Настройка:
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);
Рисунок:
@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
}
Эффект EmbossMaskFilter
хорошо работает с вызовами drawText()
и drawRect()
, но он не работает для drawBitmap()
:
Мой вопрос: можно ли использовать некоторые комбинации PorterDuff.Режим (и extractAlpha
?) нарисовать тиснение вокруг моего большого растрового изображения?
Обновление:
Глядя на голографический Каутлинхелпер.java я смог добавить внешнюю тень:
Со следующим кодом в MyView.java -
Настройка:
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);
}
Рисунок:
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);
}
Но, к сожалению, приложение теперь работает медленно , и я до сих пор не знаю, как добавить эффект тиснения вокруг растрового изображения.2 ответа:
Я не уверен, что получил именно то, что вам нужно, но если вы просто хотите применить EmbossMaskFilter вокруг какой-то буквы png с альфа-каналом, вы можете в значительной степени сделать этот трюк с
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);
Вам никогда не понадобится весь этот код в 1 onDraw, потому что он создает много объектов в памяти. И
src.extractAlpha();
создает новый растровый рисунок каждый раз. (Кстати, я всегда получаю из памяти ошибку от вашего проекта git . ДобавленоmAlphaBitmap.recycle();
, и он может, по крайней мере, загрузиться. Но он все равно отстает, как черт)Итак, я играл с ваш Git репозиторий и получил некоторые результаты. Вот демонстрационный образ и git repo первого коммита :
Но потом я понял, что вам не нужен фильтр Embossmask вокруг букв, они нужны вокруг прямоугольников. И это можно сделать примерно так же. Вот как я это сделал:
- создайте новый вспомогательный статический растровый рисунок и холст для тиснения фона, как и mAlphaBitmap
- на каждом prepareBitmaps () рисует rects на вспомогательном растровом изображении. Чистый цвет без Альфы.
- извлеките Альфа-код из созданного растрового изображения следующим образом
Bitmap alpha = helperCanvas.extractAlpha();
- нарисуйте извлеченный Альфа-растровый рисунок на хелпере с помощью краски с фильтром тиснения
helperCanvas.drawBitmap(alpha, 0, 0, paintEmboss);
- в onDraw выведите helperBitmap с некоторым Альфа перед основным растровым изображением.
Вот скриншот без Альфы(потому что так гораздо проще видеть фигуры)
Вот git демо этой версии: https://github.com/varren/AndroidEmbossMaskFilterForPng/blob/1d692d576e78bd434252a8a6c6ad2ee9f4c6dbd8/app/src/main/java/de/afarber/mytiles2/MyView.java
И вот существенная часть кода, которую я изменил в вашем проекте:
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); // ... }
И последний 3-d шаг, который я сделал, - это переместить все из onDraw в
prepareBitmaps()
, и преформанс теперь в порядке, но у нас есть текстовое разрушение при изменении размера. Итак, вот исходный код для этого шага.И здесь вроде как отлично работает окончательное решение . Перемещение всех красок с фильтрами решило проблемы преформанса, но я думаю, что есть еще лучшие варианты для реализации этого. Как я уже сказал erlier, я не знаю, что вам нужно, но этот код в значительной степени создает тиснение вокруг Bitmap
ЗЫ: прикольно, когда эффект разделения и добавления ячеек
PS2:
new EmbossMaskFilter(new float[] { 0f, 1f, 0.5f }, 0.8f, 3f, 3f);
это не будет выглядеть одинаково на разных устройствах с разным разрешением экрана
Вот предложение с использованием пользовательского макета.
Вам понадобится свой собственный макет для доски scrabble. Поскольку это сетка, это должно быть довольно легко кодировать.
Основная идея состоит в том, чтобы иметь набор теневых изображений PNG, по одному для каждого типа комбинации соседних ячеек. В вашем макете onDraw () сначала нарисуйте тени, а затем нарисуйте плитку в onLayout().В onDraw () выполните итерацию по массиву заполнителей плиток. Если у вас есть плитка, то для каждого края осмотрите соседние клетки. В зависимости от того, что находится рядом, выберите правильное теневое изображение и нарисуйте его.
Вы можете существенно уменьшить количество теневых изображений, имея теневое изображение, которое точно соответствует ширине плитки, а затем специализируя угловую область: один для 270 градусов, один для прямого выравнивания, один для 90 градусов.
Я не знаю, Может ли использование porter-duff помочь, так как вам все еще нужно определить все эти "крайние" случаи (без каламбура).