I'm trying to draw VectorDrawable resource on Canvas. The way I'm doing it now, is a bit complicated, so step-by-step proccess looks like:
Load VectorDrawable from assets, set desired size, convert it to bitmap:
fun getBitmapFromVectorDrawable(context: Context, drawableId: Int, width:
Int, height: Int): Bitmap {
var drawable = ContextCompat.getDrawable(context, drawableId)
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP)
drawable = DrawableCompat.wrap(drawable!!).mutate()
val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
val canvas = Canvas(bitmap)
drawable.setBounds(0, 0, width, height)
drawable.isFilterBitmap = true
drawable.draw(canvas)
return bitmap
}
Manually inflate this View:
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ImageView
android:id="#id/img1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:adjustViewBounds="true"
android:scaleType="fitCenter"/>
...
///ACTUALLY, THIS VIEW IS MORE COMPLICATED WITH TextView AND SOME OTHER VIEWS
</RelativeLayout>
Layout it with desired size:
val widthSpec = View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.AT_MOST)
val heightSpec = View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.AT_MOST)
view.measure(widthSpec, heightSpec)
view.layout(0, 0, view.measuredWidth, view.measuredHeight)
Then, I`m setting bitmap to this image View:
imageView.setImageBitmap(getBitmapFromVectorDrawable(view.context, drawableId, width, height))
Then I`m rendering this View into Bitmap, using this method:
fun loadBitmapFromView(v: View, width: Int, height: Int): Bitmap {
v.isDrawingCacheEnabled = true
v.buildDrawingCache();
if (v.drawingCache != null)
return v.drawingCache
val b = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
val c = Canvas(b)
v.draw(c)
return b
}
And finally, draw it on canvas:
bitmap = loadBitmapFromView(view, width, height)
matrix.reset()
matrix.postTranslate(translation.left, translation.top)
canvas.drawBitmap(bitmap, matrix, null)
My test image is a simple circle. If I'm trying to draw it small (25px:25px), there are a lot of pixel artifacts on edges.
Width and height variables from code above is taken from drawingRect, that I'm trying to draw on canvas, so, at the first step, I`m trying to load resource with target size, that no additional scaling required.
Also, I understand, that there are a better way to draw bitmap on canvas, without using view hierarchy, but I need it to get some aligning benefits from it (align text at center of image, or using constraint layout, autosize text etc...)
EDIT 1
Here is an example:
Related
I have a imported an SVG into my project as Vector Drawable.
I have my custom view
I know how to display a vector drawable using an ImageView by code (http://www.androidhive.info/2017/02/android-working-svg-vector-drawables/)
, which is described in several articles, but:
How can I draw my vectors using my custom view and by code with canvas ?
Is this possible to do. And if so, can someone give me a hint.
To do this you need to convert the vector to a bitmap as follows:
private fun getVectorBitmap(context: Context, drawableId: Int): Bitmap? {
var bitmap: Bitmap? = null
when (val drawable = ContextCompat.getDrawable(context, drawableId)) {
is BitmapDrawable -> {
bitmap = drawable.bitmap
}
is VectorDrawable -> {
bitmap = Bitmap.createBitmap(
drawable.intrinsicWidth,
drawable.intrinsicHeight, Bitmap.Config.ARGB_8888
)
val canvas = Canvas(bitmap)
drawable.setBounds(0, 0, canvas.width, canvas.height)
drawable.draw(canvas)
}
}
return bitmap
}
Then you can use the vector as a bitmap normally with your canvas:
canvas?.drawBitmap(getVectorBitmap(context, R.drawable.ic_icon), 500f, 500f, canvasPaint)
I'm going to turn #pskink's comment into an answer as I'm not so advanced in android as to quickly realize what he meant. I had to read #Krishna's answer to understand. So credits to both of them.
Here it goes:
A vector drawable inherits Drawable that can be drawn directly into a canvas. First, get the drawable from the resources using an android context:
val drawable = yourContext.getDrawable(drawableId)
Then simply draw into the canvas with it. The next line tells the drawable to draw on the top left corner, with a size of 32x32 pixels:
drawable.setBounds(0, 0, 32, 32)
Finally draw it into the canvas:
drawable.draw(canvas)
I have an imageView and several textViews
My app allows user to drag textViews on evey coordinates of imageView (imageView is not full screen) that user wants .
In other words this app allows user to add several captions to user image
and convert that image and captions to a single image and store it on user device.
According to one of stackOverFlow responses I can just convert one textView text to a bitamp
id there any way to screenshot from final image which user have created with its captions in kotlin??
This is my code:
#Throws(IOException::class)
fun foo(text: String) {
val textPaint = object : Paint() {
init {
setColor(Color.WHITE)
setTextAlign(Align.CENTER)
setTextSize(20f)
setAntiAlias(true)
}
}
val bounds = Rect()
textPaint.getTextBounds(text, 0, text.length, bounds)
val bmp = Bitmap.createBitmap(mImgBanner.getWidth(), mImgBanner.getHeight(), Bitmap.Config.RGB_565) //use ARGB_8888 for better quality
val canvas = Canvas(bmp)
canvas.drawText(text, 0, 20f, textPaint)
val path = Environment.getExternalStorageDirectory().getAbsolutePath() + "/image.png"
val stream = FileOutputStream(path)
bmp.compress(Bitmap.CompressFormat.PNG, 100, stream)
bmp.recycle()
stream.close()
}
Add desired views in xml layout inflate it and take screenshot of parent layout that is containing your views.
Code for taking screenshoot:
fun takeScreenshotOfView(view: View, height: Int, width: Int): Bitmap {
val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
val canvas = Canvas(bitmap)
val bgDrawable = view.background
if (bgDrawable != null) {
bgDrawable.draw(canvas)
} else {
canvas.drawColor(Color.WHITE)
}
view.draw(canvas)
return bitmap
}
you can also use the extension View.drawToBitmap(). It will return a Bitmap
/**
* Return a [Bitmap] representation of this [View].
*
* The resulting bitmap will be the same width and height as this view's current layout
* dimensions. This does not take into account any transformations such as scale or translation.
*
* Note, this will use the software rendering pipeline to draw the view to the bitmap. This may
* result with different drawing to what is rendered on a hardware accelerated canvas (such as
* the device screen).
*
* If this view has not been laid out this method will throw a [IllegalStateException].
*
* #param config Bitmap config of the desired bitmap. Defaults to [Bitmap.Config.ARGB_8888].
*/
fun View.drawToBitmap(config: Bitmap.Config = Bitmap.Config.ARGB_8888): Bitmap {
if (!ViewCompat.isLaidOut(this)) {
throw IllegalStateException("View needs to be laid out before calling drawToBitmap()")
}
return Bitmap.createBitmap(width, height, config).applyCanvas {
translate(-scrollX.toFloat(), -scrollY.toFloat())
draw(this)
}
}
I have a imported an SVG into my project as Vector Drawable.
I have my custom view
I know how to display a vector drawable using an ImageView by code (http://www.androidhive.info/2017/02/android-working-svg-vector-drawables/)
, which is described in several articles, but:
How can I draw my vectors using my custom view and by code with canvas ?
Is this possible to do. And if so, can someone give me a hint.
To do this you need to convert the vector to a bitmap as follows:
private fun getVectorBitmap(context: Context, drawableId: Int): Bitmap? {
var bitmap: Bitmap? = null
when (val drawable = ContextCompat.getDrawable(context, drawableId)) {
is BitmapDrawable -> {
bitmap = drawable.bitmap
}
is VectorDrawable -> {
bitmap = Bitmap.createBitmap(
drawable.intrinsicWidth,
drawable.intrinsicHeight, Bitmap.Config.ARGB_8888
)
val canvas = Canvas(bitmap)
drawable.setBounds(0, 0, canvas.width, canvas.height)
drawable.draw(canvas)
}
}
return bitmap
}
Then you can use the vector as a bitmap normally with your canvas:
canvas?.drawBitmap(getVectorBitmap(context, R.drawable.ic_icon), 500f, 500f, canvasPaint)
I'm going to turn #pskink's comment into an answer as I'm not so advanced in android as to quickly realize what he meant. I had to read #Krishna's answer to understand. So credits to both of them.
Here it goes:
A vector drawable inherits Drawable that can be drawn directly into a canvas. First, get the drawable from the resources using an android context:
val drawable = yourContext.getDrawable(drawableId)
Then simply draw into the canvas with it. The next line tells the drawable to draw on the top left corner, with a size of 32x32 pixels:
drawable.setBounds(0, 0, 32, 32)
Finally draw it into the canvas:
drawable.draw(canvas)
I have bitmap that need to draw to canvas. The image is of a fixed size, but the canvas will change according to the user's screen size and density (bitmap coule be larger/smaller than the canvas).
I need to draw the bitmap to canvas scaling all the way into the canvas size (without distorting the image), I have done the code as below but the bitmap still filling only a portion of the screen.
Rect dest = new Rect(0, 0, drawCanvas.getWidth(), drawCanvas.getHeight());
Paint paint = new Paint();
paint.setFilterBitmap(true);
drawCanvas.drawBitmap(canvasBitmap, null, dest, paint);
May I know if anybody can shed light on a good solution? Thanks.
This example is in javascript but it should still help you out scale an image
jsFiddle : https://jsfiddle.net/CanvasCode/7oghuwe2/3/
javascript
var canvas1 = document.getElementById('canvas1');
var context1 = canvas1.getContext('2d')
var canvas2 = document.getElementById('canvas2');
var context2 = canvas2.getContext('2d');
var image1 = new Image();
image1.src = "http://media.giphy.com/media/iNk83OBPzlA8o/giphy.gif";
image1.onload = function () {
context1.fillStyle = "#F00";
context1.fillRect(0, 0, canvas1.width, canvas1.height);
context2.fillStyle = "#00F";
context2.fillRect(0, 0, canvas2.width, canvas2.height);
ratio(context1, canvas1, image1);
ratio(context2, canvas2, image1);
}
function ratio(context1, canvas1, image1) {
var imageRatio = image1.width / image1.height;
var newHeight = canvas1.width / imageRatio;
var newWidth = canvas1.height * imageRatio;
var heightDiff = newHeight - canvas1.height;
var widthDiff = newWidth - canvas1.width;
if (widthDiff >= heightDiff) {
context1.drawImage(image1, 0, 0, canvas1.width, canvas1.width / imageRatio);
} else {
context1.drawImage(image1, 0, 0, canvas1.height * imageRatio, canvas1.height);
}
}
Basically you need to calculate what the width would be if you scaled the image by the canvas height and what the height would be if you scale the image by the canvas width, and which ever is smaller, then you scale by that dimension.
The reason why it might not work for you might be because the function drawBitmap() ignores the density of the bitmap. The following is from the documentation.
public void drawBitmap (Bitmap bitmap, Rect src, Rect dst, Paint
paint)
This function ignores the density associated with the bitmap. This is
because the source and destination rectangle coordinate spaces are in
their respective densities, so must already have the appropriate
scaling factor applied.
What you could do is use public void drawBitmap (Bitmap bitmap, Matrix matrix, Paint paint) instead. First you need to map the source Matrix with the desination Matrix. You do this via Matrix.setRectToRect() or Matrix.setPolyToPoly(). This will give you an accurate mapping. Just make sure you map them correctly, otherwise things will be distorted.
For more info refer here: What code should I use with 'drawMatrix.setPolyToPoly' to manipulate and draw just a rectangular part of a bitmap, rather than the entire bitmap?
I have a view I need to flip vertically, or mirror. There's plenty of information out there about mirroring a single bitmap by scaling it by -1 and translating for the offset, as described here, but there doesn't seem to be any information on how to draw all the contents of a View - specifically, all of it's subviews - upside down.
I have multiple subviews in this container - text, images - and I was hoping for a way that would let me just add them to a single View and draw that View upside down/sideways, rather than having them all perform custom drawing code to draw them upside down and have the container reposition them all appropriately. Any ideas?
You could simply create a Canvas out of a Bitmap and then invoke your root view's View.draw(Canvas) method. This will give you a snapshot of the view hierarchy in a Bitmap. You then apply the aforementioned transformations to mirror the image.
Transfer View into Bitmap, then flip it using method below:
private static Bitmap getBitmapFromView(View view,int width,int height) {
int widthSpec = View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY);
int heightSpec = View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY);
view.measure(widthSpec, heightSpec);
view.layout(0, 0, width, height);
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
view.draw(canvas);
return bitmap;
}
private static Bitmap flipBitmap(Bitmap src)
{
Matrix matrix = new Matrix();
matrix.preScale(-1, 1);
Bitmap dst = Bitmap.createBitmap(src, 0, 0, src.getWidth(), src.getHeight(), matrix, false);
dst.setDensity(DisplayMetrics.DENSITY_DEFAULT);
return dst;
}