I am using the FingerPaintCanvasView from this xamarin sample.
I am working with 2 layers. The first layer is an ImageView i want to draw on. The second layer is the PaintCanvasView to draw.
<RelativeLayout
android:layout_height="match_parent"
android:layout_width="match_parent">
<ImageView
android:id="#+id/markImageImageView"
android:layout_height="wrap_content"
android:layout_width="wrap_content"/>
<fingerpaint.FingerPaintCanvasView
android:id="#+id/canvasMarkMeta"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</RelativeLayout>
The canvas has a transparent background and the layout params of both views are set programmatically. This way the marking works fine.
Now the question is, how can I merge this imageview with the marking canvas with as little as possible quality loss to an singel file (bitmap or imageFile in FileSystem).
Let me explain why I mentiond the quality loss:
For example the image in the background has a size of 1920x1080 from the devices camera. The display only has 1280x800 pixel. Since I can't fit the image in the display, I need to display a scaled down version AND the marking happens on this scaled down version.
EDIT:
#Joe LV:
This is your demo without any changes deployed on my devices:
Lenovo Yoga 3, Android 6.0.1
Huawei Honor 8, Android 7.0
I will try an Android 8 Emulator soon.
Pixel 2XL, Android 8.1
So this method does not Work for API <= 24 :-(
(API 25 and 26 not tested)
markImageImageView just holds an image that I load from the device storage (can be any image)
canvasMarkMeta is the FingerPaintCanvas from the linked template which the holds the drawn lines.
Below is the code, I have add comments in it:
public class MainActivity : Activity
{
private Rect mSrcRect, mDestRect;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
// Set our view from the "main" layout resource
SetContentView(Resource.Layout.Main);
//your background picture ---markImageImageView
Bitmap background = BitmapFactory.DecodeResource(Resources, Resource.Drawable.pause);
//your foreground picture ---FingerPaintCanvasView
Bitmap foreground = BitmapFactory.DecodeResource(Resources, Resource.Drawable.play);
Paint p = new Paint();
p.SetXfermode(new PorterDuffXfermode(PorterDuff.Mode.SrcOver));
//use background to create a canvas
Bitmap workingBitmap = Bitmap.CreateBitmap(background);
Bitmap mutableBitmap = workingBitmap.Copy(Bitmap.Config.Argb8888, true);
Canvas c = new Canvas(mutableBitmap);
int mBWith = background.Width;
int mBHeight = background.Height;
int mFWith = foreground.Width;
int mFHeight = foreground.Height;
mSrcRect = new Rect(0, 0, mBWith, mBHeight);
mDestRect = new Rect(0, 0, mFWith, mFHeight);
//draw foreground on the backaground, then they will be single bitmap
c.DrawBitmap(foreground, mSrcRect, mDestRect, p);
ImageView imageView = FindViewById<ImageView>(Resource.Id.iv);
imageView.SetImageBitmap(mutableBitmap);
}
}
And I have provide the demo on github.
Update:
Change Bitmap.Config.Argb4444 to Bitmap.Config.Argb8888.
Related
UPDATE:
Works on Emulator with Android Oreo (8.X). I have the possibility to do changes right to the android sources, so it would also help if someone knews what in the android sources I have to change or update to get this working (so I don't really need an Android 7 workaround for this. An update to Android 8 though is not possible.)
I'm having a SurfaceView inside a FrameLayout. The SurfaceView usually displays a video, for example purposes I'm actually drawing an image.
The problem is, if I'm setting the size of the FrameLayout (or the SurfaceView) above 10.000 pixels in width, it gets cropped on the left side.
Tested on Android 7.1.1 (on a device and Emulator: Android TV (1080p) API 25
public class TestActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//add surfaceview
TestSurfaceView testSurfaceView = new TestSurfaceView(this);
setContentView(testSurfaceView);
//resize
FrameLayout.LayoutParams bgParams = (FrameLayout.LayoutParams) testSurfaceView.getLayoutParams();
//testcase full hd - working
bgParams.width = 1920;
//testcase 2 - working - each segment is 330px as 9900 / 1920 * 64 (default segment width) == 330
//bgParams.width = 9900;
//testcase 3 - working
//bgParams.width = 10000;
//testcase 4 - not working - each segment is 335px which is correct but first cell gets cropped by exactly 50px to the left
//bgParams.width = 10050;
bgParams.height = (int)Math.floor(bgParams.width * (9d/16d)); //16:9
testSurfaceView.setX(0); //doesnt help
/*
Also the position counts into the 10000px limitation as you can see on following testcases
*/
/*
bgParams.width = 9900;
bgParams.height = (int)Math.floor(bgParams.width * (9d/16d)); //16:9
//works - as 9900 + 90 < 10000
testSurfaceView.setX(90);
//doesnt work, crops 50px to the left - 9900 + 150 -> 10050
testSurfaceView.setX(150);
*/
}
}
public class TestSurfaceView extends SurfaceView implements SurfaceHolder.Callback
{
public TestSurfaceView(TestActivity context) {
super(context);
SurfaceHolder holder = this.getHolder();
holder.addCallback(this);
}
#Override
public void surfaceCreated(SurfaceHolder surfaceHolder)
{
//load bitmap from file
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
Bitmap bitmap = BitmapFactory.decodeFile(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getAbsolutePath() + "/testimg.png", options);
Canvas c = surfaceHolder.lockCanvas();
Rect rect = new Rect();
rect.set(0,0, 1920, 1080); //image size
Rect destRect = new Rect();
destRect.set(0, 0, this.getWidth(), this.getHeight());
//draw the image on the surface
c.drawBitmap(bitmap, rect, destRect, null);
surfaceHolder.unlockCanvasAndPost(c);
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
}
styles.xml - for the theme
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="android:Theme.Material.Light.NoActionBar.Fullscreen">
<!-- Customize your theme here. -->
</style>
</resources>
The code above produces following output, which is correct.
If I change the bgParams.width to 9600 - it scales up correctly and still displays starting from the left edge of the image:
But if I change the code to e.g. 10050, the image gets cropped by 50 pixels to the left.
If I set:
destRect.set(50, 0, this.getWidth(), this.getHeight());
It gets displayed correctly, but as I can't do that for the MediaPlayer and it's super weird, I'm trying to find a good solution.
I also tried setting the sizes directly on the SurfaceView and instead of changing the LayoutParams, I tried setting scaleX and scaleY of the Framelayout but ended up with the same results.
(btw. opengl max texture size is about 16000px - setting it above the ~16000px results in a black screen and an exception, so that is not the cause of the problem)
Update:
Posted all sources. Anyway here is the complete android studio project:
WeTransfer project download link
I am want to display Barcode on android. As input I get SVG string. As a SVG library I use AndroidSVG. I used sample code from library website and everything seem to be fine. But when I zoom on image, I get distorted edges (Anti-alias?). I tried to disable all the flags. But the image still has fuzzy edges. What can be wrong with my code?
Picture:
Try to zoom to max, you will see the fuzzy edges.
Code:
private void loadQRCode(String svgString) {
SVG svg = null;
try {
svg = SVG.getFromString(svgString);
} catch (SVGParseException e) {
e.printStackTrace();
}
if (svg.getDocumentWidth() != -1) {
int widthPx = Utils.pxFromDp(400);
int heightDp = Utils.pxFromDp(300);
svg.setDocumentWidth(widthPx);
svg.setDocumentHeight(heightDp);
int width = (int) Math.ceil(svg.getDocumentWidth());
int height = (int) Math.ceil(svg.getDocumentHeight());
Bitmap newBM = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas bmcanvas = new Canvas(newBM);
final DrawFilter filter = new PaintFlagsDrawFilter(Paint.ANTI_ALIAS_FLAG| Paint.FILTER_BITMAP_FLAG | Paint.DITHER_FLAG, 0);
bmcanvas.setDrawFilter(filter);
barcode.setLayerType(View.LAYER_TYPE_SOFTWARE,null);
bmcanvas.drawRGB(255, 255, 255);
svg.renderToCanvas(bmcanvas);
barcode.setImageBitmap(newBM);
}
}
If the edges of the bars do not lie exactly on pixel boundaries, you will get anti-aliasing. On a high resolution screen, this should not normally be visible.
However, in your code, you are rendering the SVG to a bitmap and setting the bitmap to an ImageView. If that ImageView has a size larger than the bitmap - ie. greater than 400 x 300, then the anti-aliased pixels in that bitmap will likely be rendered larger and thus more visible.
One solution is to avoid using a bitmap. Use a Picture/PictureDrawable instead. That way the barcode will be rendered at highest quality no matter what size it is. As vector graphics are supposed to be.
Follow the example on this page:
http://bigbadaboom.github.io/androidsvg/use_with_ImageView.html
So your code should probably look something like the following:
private void loadQRCode(String svgString) {
try {
SVG svg = SVG.getFromString(svgString);
barcode.setLayerType(View.LAYER_TYPE_SOFTWARE,null);
Drawable drawable = new PictureDrawable(svg.renderToPicture());
barcode.setImageDrawable(drawable);
} catch (SVGParseException e) {
e.printStackTrace();
}
}
If for some reason you need to use bitmaps - maybe you are caching them or something - then you should watch for changes in the size of the ImageView and then recreate the bitmap at the new size. So the bitmap is always the same size as the ImageView to which it is assigned.
Background I am currently working on creating a map with pins. For the marker and pins, we are using VectorDrawables. Our issue is with obtaining an ideal scale for the marker/pins on all devices in relation to the map. What is the best practice for handling vector scaling on multiple devices?
Note: For this question, we are only concerned with Lollipop or greater devices as we are planning on updating to the latest support library which provides support for VectorDrawables.
Images:
To demonstrate this, I have included a picture that is close to the ideal size. I do have another photo showing what it looks like on tablets, but unfortunately I am only allowed to post one link at this time.
[1] As viewed on an S6: This is an ideal size, but the difficulty is obtaining it for all devices.
Map Marker/Pins on Phone
Code
Most of what determines the overlay size is handled by the marker code and its associated vector file, but I will supply more code if needed.
Adding Overlay:
#Override
protected void onDraw(Canvas canvas)
{
// Draw backgrounds
super.onDraw(canvas);
//set up the color and the mode for the filtering function
// paint.setColorFilter(cf);
// Draw overlays
for (ImageOverlay overlay : this.overlayList)
{
Matrix overlayMatrix = overlay.getTranslationMatrix();
if (overlay.getIsScalable())
{
overlayMatrix.postConcat(getImageMatrix());
}
else
{
float[] vals = new float[]{0,0,0,0,0,0,0,0,0};
getImageMatrix().getValues(vals);
float scaleDx = overlay.getX() - (vals[Matrix.MSCALE_X]*overlay.getX());
float scaleDy = overlay.getY() - (vals[Matrix.MSCALE_Y]*overlay.getY());
overlayMatrix.postTranslate(vals[Matrix.MTRANS_X]-scaleDx, vals[Matrix.MTRANS_Y]-scaleDy);
}
canvas.drawBitmap(overlay.getImage(), overlayMatrix, paint);
}
}
Marker Code:
/**
* This method creates a bitmap for the mapMarker that we use to show where the customer is located on the map.
* #return Returns a bitmap of the marker.
*/
private Bitmap buildMapMarker(){
// Display the marker for that company
Bitmap mapMarkerBitmap;
if (Build.VERSION.SDK_INT >= 21){
VectorDrawable mapMarkerVectorDrawable = (VectorDrawable) getResources().getDrawable(R.drawable.ic_place_red_24dp);
mapMarkerBitmap = getOverlayBitmap(mapMarkerVectorDrawable, 79);
} else {
mapMarkerBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_place_red_24dp);
}
return mapMarkerBitmap;
}
/**
* This method is used to obtain the overlay bitmap for devices that are >= API 21.
* #param vectorDrawable Passed vectorDrawable to obtain info from.
* #return
*/
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
private Bitmap getOverlayBitmap(VectorDrawable vectorDrawable, int width) {
Bitmap bitmap = Bitmap.createBitmap(width,
width, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
vectorDrawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
vectorDrawable.draw(canvas);
return bitmap;
}
Marker Vector XML:
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:pathData="M12,2C8.13,2 5,5.13 5,9c0,5.25 7,13 7,13s7,-7.75 7,-13c0,-3.87 -3.13,-7 -7,-7zm0,9.5c-1.38,0 -2.5,-1.12 -2.5,-2.5s1.12,-2.5 2.5,-2.5 2.5,1.12 2.5,2.5 -1.12,2.5 -2.5,2.5z"
android:fillColor="#F44336"/>
Attempts I have made:
Using the vectors own dpi width/height ratio, this resulted in the markers being too large on higher dpi devices such as the s6 and small on devices such as the Nexus 7
Setting a fixed width and height for the canvas, this resulted in a much larger marker on low dpi devices vs high dpi devices.
Initially, we made individual pngs for each device and this was the most ideal solution thus far, but we were hoping to implement vectors in some way if possible.
Note:
Please let me know if more information or code is needed to provide a sufficient response.
I have animated transparent gif image and need to display it in android. I tried Component GifImageView, everything is ok but background is black not transparent.
Thanks
Stream input = Assets.Open("oie_2103618iUK1c2Pr.gif");
byte[] bytes;
using (MemoryStream ms = new MemoryStream())
{
input.CopyTo(ms);
bytes = ms.ToArray();
}
_gifImageView = new GifImageView(this);
_gifImageView.SetBytes(bytes);
_gifImageView.Background = new ColorDrawable(Color.Transparent);
LinearLayout giflayout = FindViewById<LinearLayout>(Resource.Id.gif);
giflayout.AddView(_gifImageView);
try to add:
GifImageView.setBackgroung();
or read this: Transparent GIF in Android ImageView
You can use another library, like Android Gif Drawable (GifImageView does not support transparent gifs), or set backround color (not transpatent) in gif, with gif editor.
you cant set transparent background for gif image. Just remove it with online gif editor. There are a lot of them
The current version of GifImageView doesn't have transparency changes that were just made a few days ago. I was able to get it working by having my Activity inherit from GifImageView.IOnFrameAvailableListener and setting my gifImageView.OnFrameAvailableListener to the Activity(as seen here). The last thing was to implement OnFrameAvailable in the Activity itself.
public Bitmap OnFrameAvailable(Bitmap bitmap)
{
if (bitmap != null) {
var newBitmap = bitmap.Copy (Bitmap.Config.Argb8888, true);
newBitmap.HasAlpha = true;
var pixelArray = new int[bitmap.Width * bitmap.Height];
bitmap.GetPixels (pixelArray, 0, bitmap.Width, 0, 0, bitmap.Width, bitmap.Height);
for (int ctr = 0; ctr <pixelArray.Count(); ctr++)
{
pixelArray[ctr] = pixelArray[ctr] == -16777216 ? 0 : pixelArray[ctr];
}
newBitmap.SetPixels (pixelArray, 0, bitmap.Width, 0, 0, bitmap.Width, bitmap.Height);
return newBitmap;
}
return bitmap;
}
This worked for me, hopefully it works for you too. You may need to change -16777216 to -1 if you see white backgrounds. Hopefully GifImageView gets updated soon, as this post-processing is not ideal.
Hi i guess this is quite simple solution but i cant figure it out myself.
lets say we have 4 points ( start_X, start_Y, end_X, end_Y) and we have to show the user this selection.
For now i thought best solution was to have 3 imageviews:
Original(nothing changed);
Mask(just any semi transparent color)
Portion(cutted out portion of original image)
and to show them as folows: 3>2>1
This solution would be great but i cant finish it. Stuck at croping an image portion and inserting it in 'the place' it belongs according to original image;
Questions are - Is there any other solution for this problem ? if not then - How to crop part of image using those 4 points and then put this image very exact place it belongs ?
Udate 1
Create new bitmap with transparent background (.png maybe) and same size as original image. Then add the cutted portion to it at special position and use it as image 3(described above); Is this solution correct ? if yes how to do it ?
try this:
class BD extends BitmapDrawable {
private Rect mSelection;
public BD(Resources res, Bitmap bitmap) {
super(res, bitmap);
mSelection = new Rect(20, 20, 60, 60);
}
#Override
public void draw(Canvas canvas) {
super.draw(canvas);
Log.d(TAG, "draw " + canvas.getMatrix());
canvas.clipRect(mSelection, Op.DIFFERENCE);
canvas.drawColor(0x66000000);
}
}
test code (place it in onCreate):
ImageView iv = new ImageView(this);
Resources res = getResources();
Bitmap b = BitmapFactory.decodeResource(res, R.drawable.layer0);
Drawable d = new BD(res, b);
iv.setImageDrawable(d);
setContentView(iv);