I have a screen with a ScrollView. Then, I save the image of a view when I tap a button at the bottom of the scroll view. The problem I have is that I cannot save the upper part of the scrollView(which is off-screen) as an image. I am using PixelCopy with the following code:
fun takeScreenshot(view: View, activity: Activity){
val loc = IntArray(2)
view.getLocationInWindow(loc)
val bitmap = Bitmap.createBitmap(view.width, view.height, Bitmap.Config.ARGB_8888)
PixelCopy.request(
activity.window,
Rect(loc[0], loc[1], loc[0] + view.width, loc[1] + view.height),
bitmap,
{if(it = PixelCopy.SUCCESS) //convert to png and save},
Handler(looper.getMainLooper())
)
I want to achieve the same image like what
val canvas = Canvas(bitmap)
view.draw(canvas)
can achieve where I can just save a screenshot of the entire view even if the other part of the view is not visible on the screen. Is there a way to achieve this using PixelCopy?
Related
I am making sharing feature like youtube music shares image to instagram story. Everything works fine, but I can't make the curved edge.
The transparent part at the end of the edge changes to black like the below picture.
I tried
add bitmap.setHasAlpha(true)
custom shading with BitmapShader
set AntiAlias true
My code is like this
fun onCreate() {
val changedBitmap = cardViewToBitmap(binding.clCardview)
shareImage(changedBitmap)
}
fun cardViewToBitmap(cardView: View): Bitmap {
val bitmap = Bitmap.createBitmap(cardView.width, cardView.height, Bitmap.Config.ARGB_8888)
bitmap.eraseColor(Color.TRANSPARENT)
bitmap.setHasAlpha(true)
val canvas = Canvas(bitmap)
val radius = 20f
val path = Path()
path.addRoundRect(0f, 0f, cardView.width.toFloat(), cardView.height.toFloat(), radius, radius, Path.Direction.CW)
canvas.clipPath(path)
cardView.draw(canvas)
return bitmap
}
I found that instagram doesn't support transparent background. How can I do it?
val bitmapHeight: Int =
if (webview.measuredHeight < webview.contentHeight) view.contentHeight else view.measuredHeight
val bitmap = Bitmap.createBitmap(webview.measuredWidth, bitmapHeight, Bitmap.Config.ARGB_8888)
val canvas = Canvas(bitmap)
view.draw(canvas)
val scrollOffset =
if (scrollTo + view.getMeasuredHeight() > bitmap.height) bitmap.height else scrollTo
val resized = Bitmap.createBitmap(bitmap, 0, scrollOffset, bitmap.width, bitmap.contentHeight)
I want to let the user select the range of the screenshot to be taken of webview by scrolling.
How can I programmatically take a screenshot of a webview, capturing the full page?
I found the answer to this problem on the link above and implemented but it seems that the solution wasn't perfect.
The code above resulted in the extra white space being just added to the current range of view.
What I'm trying to do
I'm trying to implement a view that can be masked. Let's say the user has an image like this:
which gets masked by a custom shape like this:
The resulting image should only let the shoe shine through and make the background transparent.
Implementation
I have tried to implement it this way:
Make all black pixels in the mask transparent
Override the View's draw function to draw the mask over the original image using an xfermode (PorterDuffXfermode) of DST_IN. (See Android docs for PorterDuff.Mode)
This works perfectly fine and gives me this image (green pixels mean transparent):
While this works perfectly fine, I couldn't implement a custom "drawing" functionality to let the user draw or erase the mask. I only succeeded in doing one or the other, but not both at the same time. This is what I achieved right now:
note that erasing the mask works as expected, but trying to extend the mask doesn't work and paints white pixels (instead of letting the original image (shoe) shine through).
This is the code I'm using right now:
override fun draw(baseCanvas: Canvas) {
super.draw(baseCanvas)
val image = imageBitmap
val mask = maskBitmap
val drawingBitmap = drawingBitmap
if (image != null && mask != null && drawingBitmap != null) {
run {
val canvas = Canvas(drawingBitmap)
// 1. Fill with white
canvas.drawColor(Color.WHITE)
// 2. Draw mask and only let non-transparent pixels through
val paint = Paint(Paint.ANTI_ALIAS_FLAG)
paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.DST_IN)
canvas.drawBitmap(mask, 0f, 0f, paint)
// 3. Draw all lines point to point with white color and custom xfermode
paint.xfermode = null
paint.style = Paint.Style.STROKE
paint.color = Color.WHITE
paint.strokeWidth = penSize
paint.isDither = true
paint.strokeJoin = Paint.Join.ROUND
paint.strokeCap = Paint.Cap.ROUND
paint.pathEffect = CornerPathEffect(10f)
paint.isAntiAlias = true
lines.forEach { line ->
paint.xfermode = when (line.drawMode) {
DrawMode.ERASE -> PorterDuffXfermode(PorterDuff.Mode.DST_OUT)
DrawMode.DRAW -> PorterDuffXfermode(PorterDuff.Mode.SRC)
}
val path = Path().also { path ->
val points = line.points
val range = points.size - 1
for (i in 1..range) {
path.moveTo(points[i - 1].x, points[i - 1].y)
path.lineTo(points[i].x, points[i].y)
}
}
canvas.drawPath(path, paint)
}
}
run {
val canvas = Canvas(image)
val paint = Paint(Paint.ANTI_ALIAS_FLAG)
paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.DST_ATOP)
canvas.drawBitmap(drawingBitmap, 0f, 0f, paint)
}
}
}
Important variables in scope:
imageBitmap: The shoe bitmap
maskBitmap: The bitmap that contains white colors for the original shape to shine through, transparent for everything that should be transparent
drawingBitmap: An empty bitmap with the same size as imageBitmap, I use this for drawing only and then draw that result onto the imageBitmap (using a Canvas, see second run block)
lines: The lines I want to draw. A line consists of a drawMode (draw or erase) and a list of all points I have tracked.
Weird Observation
The thing that's confusing me right now is that when I fill the canvas black before adding the image:
run {
val canvas = Canvas(image)
canvas.drawColor(Color.BLACK) // <-- ADD THIS HERE
val paint = Paint(Paint.ANTI_ALIAS_FLAG)
paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.DST_ATOP)
canvas.drawBitmap(drawingBitmap, 0f, 0f, paint)
}
it looks like it's working fine:
Same result if I fill the canvas RED, it lets the Red color shine through instead of Black. Why does it work with the color, but not the original bitmap?
It even looks correct if inspected in the debugger:
Question
Does anyone here know why this does not work as I expect it to work? I tried to play around with all kinds of different PorterDuff modes, but couldn't manage to get it working smoothly.
Any help appreciated!
Okay I got it working. I'm not sure if there's a more efficient way to achieve this, but this is what I did:
Create a fourth Bitmap called imageDrawingBitmap.
Set imageDrawingBitmap to the actual ImageView
As in the code snippet above, first draw the mask, then paths ontop of that
Then create a Canvas for the imageDrawingBitmap
Draw original image in that Canvas
Draw mask (maskDrawingBitmap from step 3.) in that Canvas with PorterDuff DST_ATOP.
That seems to be working fine.
I have an application where there is an Image view and a Edit text in android. Now whenever the user has entered the text in edittext, the text will appear on the top of imageview as a watermark text. I am able to achieve this thing, but i am not able to put a drag and drop functionality to that watermark text. I want the user to drag the watermark text and can drop anywhere inside the Image view.
Below is my code to redraw the Imageview with watermark Text:
Could anyone please help me in this, as I am stuck on this issue from past two days?
Thanks in advance.
fun redrawImage(): Bitmap? {
val w = src.width
val h = src.height
val result = Bitmap.createBitmap(w, h, src.config)
val canvas = Canvas(result)
canvas.drawBitmap(src, 0F, 0F, null)
val paint = Paint()
paint.setColor(Color.BLACK)
paint.setAlpha(100)
paint.setTextSize(26F)
paint.setAntiAlias(true)
paint.setUnderlineText(true)
canvas.drawText(waterMarkEditText.text.toString(), 0F , 0F, paint)
return result
}
I am calling the above method and passing the Image View bitmap to it to add the watermark text to the imageview.
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)
}
}