I want to convert PinBitmap (SkiaSharp.SkBitmap) to Android.Graphics.Bitmap. I couldn't find online references, I only tried this in the Android project:
Android.Graphics.Bitmap bitmap = BitmapFactory.DecodeByteArray(myView.PinBitmap.Bytes, 0, myView.PinBitmap.Bytes.Length);
but the bitmap is null.
I'm creating the PinBitmap from a SKCanvasView:
private void SKCanvasView_PaintSurface(object sender, SkiaSharp.Views.Forms.SKPaintSurfaceEventArgs e)
{
var surface = e.Surface;
var canvas = surface.Canvas;
SKImageInfo info = e.Info;
canvas.DrawLine(10, 10, 10, 200, new SKPaint() { IsStroke = true, Color = SKColors.Green, StrokeWidth = 10 });
SKBitmap saveBitmap = new SKBitmap();
// Create bitmap the size of the display surface
if (saveBitmap == null)
{
saveBitmap = new SKBitmap(info.Width, info.Height);
}
// Or create new bitmap for a new size of display surface
else if (saveBitmap.Width < info.Width || saveBitmap.Height < info.Height)
{
SKBitmap newBitmap = new SKBitmap(Math.Max(saveBitmap.Width, info.Width),
Math.Max(saveBitmap.Height, info.Height));
using (SKCanvas newCanvas = new SKCanvas(newBitmap))
{
newCanvas.Clear();
newCanvas.DrawBitmap(saveBitmap, 0, 0);
}
saveBitmap = newBitmap;
}
// Render the bitmap
canvas.Clear();
canvas.DrawBitmap(saveBitmap, 0, 0);
var customPin = new CustomPin { PinBitmap = saveBitmap };
Content = customPin;
}
This is easy to do, you just need to have the SkiaSharp.Views NuGet
package installed. Then, there are extension methods:
skiaBitmap = androidBitmap.ToSKBitmap();
androidBitmap = skiaBitmap.ToBitmap();
There are also a few others, like: ToSKImage
and ToSKPixmap.
NOTE: these all make copies of the pixel data. To avoid memory issue,
you can dispose of the original as soon as the method returns.
Source: https://forums.xamarin.com/discussion/comment/294868/#Comment_294868
I want to convert PinBitmap (SkiaSharp.SkBitmap) to Android.Graphics.Bitmap
In Android MainActivity, you could use AndroidExtensions.ToBitmap Method to convert PinBitmap to Bitmap.
AndroidExtensions.ToBitmap Method: https://learn.microsoft.com/en-us/dotnet/api/skiasharp.views.android.androidextensions.tobitmap?view=skiasharp-views-1.68.1
Install SkiaSharp.Views.Forms from NuGet Package. https://www.nuget.org/packages/SkiaSharp.Views.Forms/
Use the reference.
using SkiaSharp.Views.Android;
Use the code below.
var bitmap = AndroidExtensions.ToBitmap(PinBitmap);
Related
I am able to load dicom image using imebra, and want to change the colors of image, but cant figure out a way. I want to achieve functionality as in Dicomite app.
Following is my code:
public void loadDCM() {
com.imebra.DataSet loadedDataSet = com.imebra.CodecFactory.load(dicomPath.getPath());
com.imebra.VOIs voi = loadedDataSet.getVOIs();
com.imebra.Image image = loadedDataSet.getImageApplyModalityTransform(0);
// com.imebra.Image image = loadedDataSet.getImage(0);
String colorSpace = image.getColorSpace();
long width = image.getWidth();
long height = image.getHeight();
TransformsChain transformsChain = new TransformsChain();
com.imebra.DrawBitmap drawBitmap = new com.imebra.DrawBitmap(transformsChain);
com.imebra.TransformsChain chain = new com.imebra.TransformsChain();
if (com.imebra.ColorTransformsFactory.isMonochrome(image.getColorSpace())) {
// Allocate a VOILUT transform. If the DataSet does not contain any pre-defined
// settings then we will find the optimal ones.
VOILUT voilutTransform = new VOILUT();
// Retrieve the VOIs (center/width pairs)
com.imebra.VOIs vois = loadedDataSet.getVOIs();
// Retrieve the LUTs
List < LUT > luts = new ArrayList < LUT > ();
for (long scanLUTs = 0;; scanLUTs++) {
try {
luts.add(loadedDataSet.getLUT(new com.imebra.TagId(0x0028, 0x3010), scanLUTs));
} catch (Exception e) {
break;
}
}
if (!vois.isEmpty()) {
voilutTransform.setCenterWidth(vois.get(0).getCenter(), vois.get(0).getWidth());
} else if (!luts.isEmpty()) {
voilutTransform.setLUT(luts.get(0));
} else {
voilutTransform.applyOptimalVOI(image, 0, 0, width, height);
}
chain.addTransform(voilutTransform);
com.imebra.DrawBitmap draw = new com.imebra.DrawBitmap(chain);
// Ask for the size of the buffer (in bytes)
long requestedBufferSize = draw.getBitmap(image, drawBitmapType_t.drawBitmapRGBA, 4, new byte[0]);
byte buffer[] = new byte[(int) requestedBufferSize]; // Ideally you want to reuse this in subsequent calls to getBitmap()
ByteBuffer byteBuffer = ByteBuffer.wrap(buffer);
// Now fill the buffer with the image data and create a bitmap from it
drawBitmap.getBitmap(image, drawBitmapType_t.drawBitmapRGBA, 4, buffer);
Bitmap renderBitmap = Bitmap.createBitmap((int) image.getWidth(), (int) image.getHeight(), Bitmap.Config.ARGB_8888);
renderBitmap.copyPixelsFromBuffer(byteBuffer);
image_view.setImageBitmap(renderBitmap);
}
If you are dealing with a monochrome image and you want to modify the presentation luminosity/contrast, then you have to modify the parameters of the VOILUT transform (voilutTransform variable in your code).
You can get the center and width that the transform is applying to the image before calculating the bitmap to be displayed, then modify them before calling drawBitmap.getBitmap again.
E.g., to double the contrast:
voilutTransform.setCenterWidth(voilutTransform.getCenter(), voilutTransform.getWidth() / 2);
// Now fill the buffer with the image data and create a bitmap from it
drawBitmap.getBitmap(image, drawBitmapType_t.drawBitmapRGBA, 4, buffer);
See this answer for more details about the center/width
Hello I am new to android, I am currently trying to print a receipt from my Android 4.4.2 table to my Zicox thermal receipt printer. I have been able to print the text so far but now I need to go a step further and print barcodes / qrcodes. Unfortunately this is way beyond my knowledge, I have googled solutions and have not found one for me yet.
These are the methods I use to generate my barcode:
/**************************************************************
* getting from com.google.zxing.client.android.encode.QRCodeEncoder
*
* See the sites below
* http://code.google.com/p/zxing/
* http://code.google.com/p/zxing/source/browse/trunk/android/src/com/google/zxing/client/android/encode/EncodeActivity.java
* http://code.google.com/p/zxing/source/browse/trunk/android/src/com/google/zxing/client/android/encode/QRCodeEncoder.java
*/
private static final int WHITE = 0xFFFFFFFF;
private static final int BLACK = 0xFF000000;
Bitmap encodeAsBitmap(String contents, BarcodeFormat format, int img_width, int img_height) throws WriterException {
String contentsToEncode = contents;
if (contentsToEncode == null) {
return null;
}
Map<EncodeHintType, Object> hints = null;
String encoding = guessAppropriateEncoding(contentsToEncode);
if (encoding != null) {
hints = new EnumMap<EncodeHintType, Object>(EncodeHintType.class);
hints.put(EncodeHintType.CHARACTER_SET, encoding);
}
MultiFormatWriter writer = new MultiFormatWriter();
BitMatrix result;
try {
result = writer.encode(contentsToEncode, format, img_width, img_height, hints);
} catch (IllegalArgumentException iae) {
// Unsupported format
return null;
}
int width = result.getWidth();
int height = result.getHeight();
int[] pixels = new int[width * height];
for (int y = 0; y < height; y++) {
int offset = y * width;
for (int x = 0; x < width; x++) {
pixels[offset + x] = result.get(x, y) ? BLACK : WHITE;
}
}
Bitmap bitmap = Bitmap.createBitmap(width, height,
Bitmap.Config.ARGB_8888);
bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
return bitmap;
}
private static String guessAppropriateEncoding(CharSequence contents) {
// Very crude at the moment
for (int i = 0; i < contents.length(); i++) {
if (contents.charAt(i) > 0xFF) {
return "UTF-8";
}
}
return null;
}
This is my onClick method that starts the entire process:
// barcode data
String barcode_data = "123456";
// barcode image
ImageView iv = new ImageView(this);
try {
barCode = encodeAsBitmap(barcode_data, BarcodeFormat.CODE_128, 300, 40);
// bitmap.getRowBytes();
iv.setImageBitmap(barCode);
} catch (WriterException e) {
e.printStackTrace();
}
iv.setLayoutParams(new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
innerLayout.addView(iv);
So now I am basically able to generate and display the barcode now I want to be able to print in on my receipts.
As I can understand from your question, you basically want to print a bitmap, that contains the QR code.
You can use PrintHelper class for the same. Here's a sample code from the official documentation that shows how to use it.
private void doPhotoPrint() {
PrintHelper photoPrinter = new PrintHelper(getActivity());
photoPrinter.setScaleMode(PrintHelper.SCALE_MODE_FIT);
Bitmap bitmap = BitmapFactory.decodeResource(getResources(),
R.drawable.droids);
photoPrinter.printBitmap("droids.jpg - test print", bitmap);
}
Also, it says :
After the printBitmap() method is called, no further action from your application is required. The Android print user interface appears, allowing the user to select a printer and printing options.
Update :
The above method prints only a bitmap. For printing a whole layout (like a receipt, as you said), instructions have been laid out here. I'll try to summarize it for you here :
The first step is to to extend PrintDocumentAdapter class and override certain methods. In that adapter, there's a callback method known as onWrite() that is called for drawing content on the file to be printed. In this method, you'll have to use Canvas object to draw content. This object has all the helper methods to draw bitmap/line/text etc.
Then, when the print is requested, obtain an instance of `PrintManager' class as follows :
PrintManager printManager = (PrintManager) getActivity()
.getSystemService(Context.PRINT_SERVICE);
Then, call the print method as :
printManager.print(jobName, new CustomDocumentAdapter(getActivity()),
null);
Let me know if this helps.
So, I have this test project where I try to circumnavigate issues that I find with bitmaps and I'm having more issues than usual with resizing bitmaps. The project, pre-resizing tests, allowed me to load a bitmap from a stream, show it, and dispose it. In a loop.
protected override void OnCreate (Bundle bundle){
...
int i = 0;
System.Timers.Timer t = new System.Timers.Timer (100);
t.Elapsed += delegate(object sender, System.Timers.ElapsedEventArgs e) {
RunOnUiThread(delegate() {
if(i++ % 2 == 0){
Console.WriteLine("Iteration no: " + i);
tmpView tView = new tmpView(this);
mainView.AddView(tView, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MatchParent, ViewGroup.LayoutParams.MatchParent));
}else{
((tmpView)mainView.GetChildAt(0)).dispose();
mainView.RemoveAllViews();
}
});
};
t.AutoReset = true;
t.Start ();
}
and the tmpView code:
private class tmpView:RelativeLayout{
PTImage img;
ImageView img2;
PTImageObj imgObj;
Android.Graphics.Bitmap bmp;
Bitmap resized;
public tmpView(Context cntx):base(cntx){
SetBackgroundColor(new Android.Graphics.Color(200, 0, 0, 200));
System.IO.Stream imgStream = Application.Context.Assets.Open ("backgroundLeft.png");
bmp = Android.Graphics.BitmapFactory.DecodeStream (imgStream, new Android.Graphics.Rect(), new Android.Graphics.BitmapFactory.Options(){InPurgeable = true, InInputShareable = true});
img2 = new ImageView(cntx);
img2.SetImageBitmap(bmp);
imgStream.Close();
imgStream.Dispose();
GC.SuppressFinalize(imgStream);
imgStream = null;
RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MatchParent, 500);
lp.TopMargin = 150;
this.AddView(img2, lp);
}
public void dispose(){
bmp.Recycle ();
img2.SetImageDrawable (null);
}
}
So far that works fine, I can leave it iterating for as long as I want without having outOfMemory issues. However, if I want to change the tmpView code to this:
public tmpView(Context cntx):base(cntx){
SetBackgroundColor(new Android.Graphics.Color(200, 0, 0, 200));
System.IO.Stream imgStream = Application.Context.Assets.Open ("backgroundLeft.png");
bmp = Android.Graphics.BitmapFactory.DecodeStream (imgStream, new Android.Graphics.Rect(), new Android.Graphics.BitmapFactory.Options(){InPurgeable = true, InInputShareable = true});
resized = Bitmap.CreateScaledBitmap(bmp, bmp.Width + 50, bmp.Height + 50, false);
bmp.Recycle();
img2 = new ImageView(cntx);
img2.SetImageBitmap(resized);
imgStream.Close();
imgStream.Dispose();
GC.SuppressFinalize(imgStream);
imgStream = null;
}
I end up with outOfMemory issues at about ~200 iterations. My guess is that the rescaling is creating temp bitmaps that I can't force recycle. Is there a better way of resizing the bitmap so I can continue the loop without crashing?
Note: if I don't actually resize, if I give the CreateScaledBitmap the same sizes the bitmap has, I don't run into the memory issues. But... that kind of defeats the purpose :P. Oh, and yes, I did add the resized.Recycle (); in the dispose
Note2: The code is in C# but I'm fine with answers in java
Thank you,
Axel
I have the following code which fails on IOS but works on Android (using ionic/cordova). What am I missing (maybe I am being lucky with Android!) ? On Androids I get a seamless vertical stacking of image(s), whereas on IOS I get a blank look. It fails on IOS whether I am stacking one SVG or many.
The SVG elements by themselves show up fine on the page, and in debugger I can see their complete markup.
function _appendImgToCanvas(canvas, image, first_image) {
// image contents are -- {id, h, w}
var ctx = canvas.getContext("2d");
var img_type = "data:image/svg+xml;charset=utf-8";
try {
var img_el = document.getElementById(image.id).getElementsByTagName("svg")[0];
var img_src = new XMLSerializer().serializeToString(img_el); // thx Kaiido
var img = new Image();
img.src = img_type + "," + img_src;
var h = image.h, w = image.w; // is in CSS pixels
var old_h = canvas.height, old_w = canvas.width;
var old_image;
if (first_image) {
ctx.clearRect(0, 0, old_w, old_h);
old_h = old_w = 0; // it's a new beginning
} else {
old_image = canvas.toDataURL("image/png"); // Android appears to wipe out image on resizing
}
// update canvas dims, and update its CSS style too
canvas.setAttribute("height", old_h + h);
canvas.setAttribute("width", Math.max(w, old_w));
canvas.style.height = canvas.height + "px";
canvas.style.width = canvas.width + "px";
// retrieve any old image into the resized canvas
if (old_image) {
var x = new Image();
x.src = old_image;
ctx.drawImage(x,0,0);
}
// add the given image
ctx.drawImage(img, 0, old_h);
$log.debug("IMG#" + image.id, w,"x",h, "appended at h=", old_h, "new dim=", canvas.width, "x", canvas.height);
} catch(e){$log.error("IMG#" + image.id, "ERROR in appending", e);
}
}
You have to wait for your images has loaded, add the drawing operations into their onload handler.
Also, for converting your svg element to a dataURL, use encodeURIComponent(new XMLSerializer().serializeToString(yourSVGElement)) method instead of outerHTML/innerHTML, you will avoid encoding issues and this if/else //probably ios..
I have two png image files that I would like my android app to combine programmatically into one png image file and am wondering if it is possible to do so? if so, what I would like to do is just overlay them on each other to create one file.
the idea behind this is that I have a handful of png files, some with a portion of the image on the left with the rest transparent and the others with an image on the right and the rest transparent. and based on user input it will combine the two to make one file to display. (and i cant just display the two images side by side, they need to be one file)
is this possible to do programmatically in android and how so?
I've been trying to figure this out for a little while now.
Here's (essentially) the code I used to make it work.
// Get your images from their files
Bitmap bottomImage = BitmapFactory.decodeFile("myFirstPNG.png");
Bitmap topImage = BitmapFactory.decodeFile("myOtherPNG.png");
// As described by Steve Pomeroy in a previous comment,
// use the canvas to combine them.
// Start with the first in the constructor..
Canvas comboImage = new Canvas(bottomImage);
// Then draw the second on top of that
comboImage.drawBitmap(topImage, 0f, 0f, null);
// comboImage is now a composite of the two.
// To write the file out to the SDCard:
OutputStream os = null;
try {
os = new FileOutputStream("/sdcard/DCIM/Camera/" + "myNewFileName.png");
comboImage.compress(CompressFormat.PNG, 50, os)
} catch(IOException e) {
e.printStackTrace();
}
EDIT :
there was a typo,
So, I've changed
image.compress(CompressFormat.PNG, 50, os)
to
bottomImage.compress(CompressFormat.PNG, 50, os)
You can do blending. This is not particular to Android. It's just universal image processing.
EDIT:
You may find these articles & samples & code useful:
http://www.jhlabs.com/ip/
http://kfb-android.blogspot.com/2009/04/image-processing-in-android.html
http://code.google.com/p/jjil/
Image Processing on Android
I use this code
private class PhotoComposition extends AsyncTask<Object, Void, Boolean> {
private String pathSave;//path save combined images
#Override
protected Boolean doInBackground(Object... objects) {
List<String> images = (List<String>) objects[0]; //lsit of path iamges
pathSave = (String) objects[1];//path save combined images
if (images.size() == 0) {
return false;
}
List<Bitmap> bitmaps = new ArrayList<>();
for (int i = 0; i < images.size(); i++) {
bitmaps.add(BitmapFactory.decodeFile( images.get(i)));
}
int width = findWidth(bitmaps);//Find the width of the composite image
int height = findMaxHeight(bitmaps);//Find the height of the composite image
Bitmap combineBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);//create bitmap of composite image
combineBitmap.eraseColor(Color.parseColor("#00000000")); //bcakgraound color of composite image
Bitmap mutableCombineBitmap = combineBitmap.copy(Bitmap.Config.ARGB_8888, true);//create mutable bitmap to create canvas
Canvas canvas = new Canvas(mutableCombineBitmap);// create canvas to add bitmaps
float left = 0f;
for (int i = 0; i < bitmaps.size(); i++) {
canvas.drawBitmap(bitmaps.get(i), left, 0f, null);//Taking photos horizontally
left += bitmaps.get(i).getWidth();//Take right to the size of the previous photo
}
OutputStream outputStream = null;
try {
outputStream = new FileOutputStream(pathSave);//path of save composite image
mutableCombineBitmap.compress(Bitmap.CompressFormat.PNG, 80, outputStream);
} catch (IOException e) {
e.printStackTrace();
return false;
}
return true;
}
#Override
protected void onPostExecute(Boolean isSave) {
if (isSave) {
//iamge save on pathSave
Log.i("PhotoComposition", "onPostExecute: " + pathSave);
}
super.onPostExecute(isSave);
}
private int findMaxHeight(List<Bitmap> bitmaps) {
int maxHeight = Integer.MIN_VALUE;
for (int i = 0; i < bitmaps.size(); i++) {
if (bitmaps.get(i).getHeight() > maxHeight) {
maxHeight = bitmaps.get(i).getHeight();
}
}
return maxHeight;
}
private int findWidth(List<Bitmap> bitmaps) {
int width = 0;
for (int i = 0; i < bitmaps.size(); i++) {
width += bitmaps.get(i).getWidth();
}
return width;
}
USAGE
List<String> images = new ArrayList<>();
images.add("/storage/emulated/0/imageOne.png");//path of image in storage
images.add("/storage/emulated/0/imageTwo.png");
// images.add("/storage/emulated/0/imageThree");
// ... //add more images
String pathSaveCombinedImage = "/storage/emulated/0/CombinedImage.png";//path save result image
new PhotoComposition().execute(images, pathSaveCombinedImage);
And the result of using the above code will be as follows
You may wish to look into the Canvas object, which would make it easy to do other drawing operations as well. You can just draw your bitmaps onto a canvas where you want them, then save the resulting bitmap.
If they have transparent sections, then if you draw one on top of the other, only the non-transparent portions will overlap. It will be up to you to arrange the bitmaps however you like.
For the separate issue of re-saving your image to a png, use bitmap.compress().
Try this .
public Bitmap mergeBitmap(Bitmap frame, Bitmap img){
Bitmap bmOverlay = Bitmap.createBitmap(frame.getWidth(), frame.getHeight(), frame.getConfig());
Canvas canvas = new Canvas(bmOverlay);
canvas.drawBitmap(img, 0, 0, null);
canvas.drawBitmap(frame, new Matrix(), null);
return bmOverlay;
}
Returns a bitmap image
Pass two bitmap images to your function as shown below
Bitmap img= mergeBitmap(imgone, imagetwo);
See the entire post or also see merge multiple images in android programmatically