/**
* Copyright (c) 2016 UPTech
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package org.thoughtcrime.securesms.scribbles.widget.entity;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PointF;
import android.support.annotation.IntRange;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.Layout;
import android.text.StaticLayout;
import android.text.TextPaint;
import org.thoughtcrime.securesms.scribbles.viewmodel.TextLayer;
public class TextEntity extends MotionEntity {
private final TextPaint textPaint;
@Nullable
private Bitmap bitmap;
public TextEntity(@NonNull TextLayer textLayer,
@IntRange(from = 1) int canvasWidth,
@IntRange(from = 1) int canvasHeight)
{
super(textLayer, canvasWidth, canvasHeight);
this.textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
updateEntity(false);
}
private void updateEntity(boolean moveToPreviousCenter) {
// save previous center
PointF oldCenter = absoluteCenter();
Bitmap newBmp = createBitmap(getLayer(), bitmap);
// recycle previous bitmap (if not reused) as soon as possible
if (bitmap != null && bitmap != newBmp && !bitmap.isRecycled()) {
bitmap.recycle();
}
this.bitmap = newBmp;
float width = bitmap.getWidth();
float height = bitmap.getHeight();
@SuppressWarnings("UnnecessaryLocalVariable")
float widthAspect = 1.0F * canvasWidth / width;
// for text we always match text width with parent width
this.holyScale = widthAspect;
// initial position of the entity
srcPoints[0] = 0;
srcPoints[1] = 0;
srcPoints[2] = width;
srcPoints[3] = 0;
srcPoints[4] = width;
srcPoints[5] = height;
srcPoints[6] = 0;
srcPoints[7] = height;
srcPoints[8] = 0;
srcPoints[8] = 0;
if (moveToPreviousCenter) {
// move to previous center
moveCenterTo(oldCenter);
}
}
/**
* If reuseBmp is not null, and size of the new bitmap matches the size of the reuseBmp,
* new bitmap won't be created, reuseBmp it will be reused instead
*
* @param textLayer text to draw
* @param reuseBmp the bitmap that will be reused
* @return bitmap with the text
*/
@NonNull
private Bitmap createBitmap(@NonNull TextLayer textLayer, @Nullable Bitmap reuseBmp) {
int boundsWidth = canvasWidth;
// init params - size, color, typeface
textPaint.setStyle(Paint.Style.FILL);
textPaint.setTextSize(textLayer.getFont().getSize() * canvasWidth);
textPaint.setColor(textLayer.getFont().getColor());
// textPaint.setTypeface(fontProvider.getTypeface(textLayer.getFont().getTypeface()));
// drawing text guide : http://ivankocijan.xyz/android-drawing-multiline-text-on-canvas/
// Static layout which will be drawn on canvas
StaticLayout sl = new StaticLayout(
textLayer.getText(), // - text which will be drawn
textPaint,
boundsWidth, // - width of the layout
Layout.Alignment.ALIGN_CENTER, // - layout alignment
1, // 1 - text spacing multiply
1, // 1 - text spacing add
true); // true - include padding
// calculate height for the entity, min - Limits.MIN_BITMAP_HEIGHT
int boundsHeight = sl.getHeight();
// create bitmap not smaller than TextLayer.Limits.MIN_BITMAP_HEIGHT
int bmpHeight = (int) (canvasHeight * Math.max(TextLayer.Limits.MIN_BITMAP_HEIGHT,
1.0F * boundsHeight / canvasHeight));
// create bitmap where text will be drawn
Bitmap bmp;
if (reuseBmp != null && reuseBmp.getWidth() == boundsWidth
&& reuseBmp.getHeight() == bmpHeight) {
// if previous bitmap exists, and it's width/height is the same - reuse it
bmp = reuseBmp;
bmp.eraseColor(Color.TRANSPARENT); // erase color when reusing
} else {
bmp = Bitmap.createBitmap(boundsWidth, bmpHeight, Bitmap.Config.ARGB_8888);
}
Canvas canvas = new Canvas(bmp);
canvas.save();
// move text to center if bitmap is bigger that text
if (boundsHeight < bmpHeight) {
//calculate Y coordinate - In this case we want to draw the text in the
//center of the canvas so we move Y coordinate to center.
float textYCoordinate = (bmpHeight - boundsHeight) / 2;
canvas.translate(0, textYCoordinate);
}
//draws static layout on canvas
sl.draw(canvas);
canvas.restore();
return bmp;
}
@Override
@NonNull
public TextLayer getLayer() {
return (TextLayer) layer;
}
@Override
protected void drawContent(@NonNull Canvas canvas, @Nullable Paint drawingPaint) {
if (bitmap != null) {
canvas.drawBitmap(bitmap, matrix, drawingPaint);
}
}
@Override
public int getWidth() {
return bitmap != null ? bitmap.getWidth() : 0;
}
@Override
public int getHeight() {
return bitmap != null ? bitmap.getHeight() : 0;
}
@Override
public void updateEntity() {
updateEntity(true);
}
}