How to save GLSurfaceView to texture/buffer/bitmap - java

I'm developing application for Android with GLSurfaceView. One moment i have to replace my GLSurfaceView with an image of it in that moment. The question is, how to get an image right? I used this code:
v.setDrawingCacheEnabled(true);
v.measure(View.MeasureSpec.makeMeasureSpec(500, View.MeasureSpec.AT_MOST),
View.MeasureSpec.makeMeasureSpec(500, View.MeasureSpec.AT_MOST));
v.layout(0, 0, v.getMeasuredWidth(), v.getMeasuredHeight());
v.buildDrawingCache(true);
Bitmap b = Bitmap.createBitmap(v.getDrawingCache());
v.setDrawingCacheEnabled(false); // clear drawing cache
return b;
but it always return black bitmap.
Maybe we can make something other then Bitmap (which also can be placed to GLSurfaceView)?

I don't think it works this way with a GLSurfaceView. The framebuffer may live inside the GPU, where it's not directly accessible on the CPU.
You can use a framebuffer object to render the image to a texture, then use glReadPixels to download the data into a buffer and turn the buffer into a Bitmap.

Save GLSurfaceView to bitmap. Its working Correct.
MyRenderer Class :
#Override
public void onDrawFrame(GL10 gl) {
try {
int w = width_surface ;
int h = height_surface ;
Log.i("hari", "w:"+w+"-----h:"+h);
int b[]=new int[(int) (w*h)];
int bt[]=new int[(int) (w*h)];
IntBuffer buffer=IntBuffer.wrap(b);
buffer.position(0);
GLES20.glReadPixels(0, 0, w, h,GLES20.GL_RGBA,GLES20.GL_UNSIGNED_BYTE, buffer);
for(int i=0; i<h; i++)
{
//remember, that OpenGL bitmap is incompatible with Android bitmap
//and so, some correction need.
for(int j=0; j<w; j++)
{
int pix=b[i*w+j];
int pb=(pix>>16)&0xff;
int pr=(pix<<16)&0x00ff0000;
int pix1=(pix&0xff00ff00) | pr | pb;
bt[(h-i-1)*w+j]=pix1;
}
}
Bitmap inBitmap = null ;
if (inBitmap == null || !inBitmap.isMutable()
|| inBitmap.getWidth() != w || inBitmap.getHeight() != h) {
inBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
}
//Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
inBitmap.copyPixelsFromBuffer(buffer);
//return inBitmap ;
// return Bitmap.createBitmap(bt, w, h, Bitmap.Config.ARGB_8888);
inBitmap = Bitmap.createBitmap(bt, w, h, Bitmap.Config.ARGB_8888);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
inBitmap.compress(CompressFormat.JPEG, 90, bos);
byte[] bitmapdata = bos.toByteArray();
ByteArrayInputStream fis = new ByteArrayInputStream(bitmapdata);
final Calendar c=Calendar.getInstance();
long mytimestamp=c.getTimeInMillis();
String timeStamp=String.valueOf(mytimestamp);
String myfile="hari"+timeStamp+".jpeg";
dir_image=new File(Environment.getExternalStorageDirectory()+File.separator+
"printerscreenshots"+File.separator+"image");
dir_image.mkdirs();
try {
File tmpFile = new File(dir_image,myfile);
FileOutputStream fos = new FileOutputStream(tmpFile);
byte[] buf = new byte[1024];
int len;
while ((len = fis.read(buf)) > 0) {
fos.write(buf, 0, len);
}
fis.close();
fos.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
Log.v("hari", "screenshots:"+dir_image.toString());
}
}catch(Exception e) {
e.printStackTrace() ;
}

Related

NullPointerException when convert and save Image file on Android device

I'm trying to save a Image type data as a jpg file to SD card, what I plan to do is first converting it to Bitmap, compress bitmap as jpeg format, then use FileOutputStream to save it to SD card.
Here's my code:
File imagefile = new File(sdCardPath, "image.jpg");
Image image = fromFrame.getImage();
ByteBuffer bbuffer = image.getPlanes()[0].getBuffer();
byte[] byts = new byte[bbuffer.capacity()];
bbuffer.get(byts);
Bitmap bitmapImage = BitmapFactory.decodeByteArray(byts, 0, byts.length, null);
try{
FileOutputStream out = new FileOutputStream(imagefile);
bitmapImage.compress(Bitmap.CompressFormat.JPEG, 100, out);
out.flush();
out.close();
} catch (FileNotFoundException e) {
Log.v(TAG, "FileNotFoundExceptionError " + e.toString());
} catch (IOException e) {
Log.v(TAG, "IOExceptionError " + e.toString());
}
It gives error:
java.lang.NullPointerException: Attempt to invoke virtual method 'boolean
android.graphics.Bitmap.compress(android.graphics.Bitmap$CompressFormat,
int, java.io.OutputStream)' on a null object reference
Is there anything I missed or did it wrong?
It turns out that the image file has to be re-organized and as for Depth16 file, every pixel has 16 bits of data, so the code to convert it into a jpg file is:
Image depthimage = fromFrame.getImage();
int imwidth = depthImage.getWidth();
int imheight = depthImage.getHeight();
Image.Plane plane = depthImage.getPlanes()[0];
ShortBuffer shortDepthBuffer = plane.getBuffer().asShortBuffer();
File sdCardFile = Environment.getExternalStorageDirectory();
File file = new File(sdCardFile, "depthImage.jpg");
Bitmap disBitmap = Bitmap.createBitmap(imwidth, imheight, Bitmap.Config.RGB_565);
for (int i = 0; i < imheight; i++) {
for (int j = 0; j < imwidth; j++) {
int index = (i * imwidth + j) ;
shortDepthBuffer.position(index);
short depthSample = shortDepthBuffer.get();
short depthRange = (short) (depthSample & 0x1FFF);
byte value = (byte) depthRange ;
disBitmap.setPixel(j, i, Color.rgb(value, value, value));
}
}
Matrix matrix = new Matrix();
matrix.setRotate(90);
Bitmap rotatedBitmap = Bitmap.createBitmap(disBitmap, 0, 0, imwidth, imheight, matrix, true);
try {
FileOutputStream out = new FileOutputStream(file);
rotatedBitmap.compress(Bitmap.CompressFormat.JPEG, 90, out);
out.flush();
out.close();
MainActivity.num++;
} catch (Exception e) {
e.printStackTrace();
}
} catch (Exception e) {
e.printStackTrace();
}
I also rotate the picture to make it easier to review on mobile devices

How to convert any image to JPG?

I am receiving a MultipartFile Spring object from rest controller. I am trying to convert any inage file to JPG image but I just need the byte array to save it on mongoDb
I found this code to do that
public boolean convertImageToJPG(InputStream attachedFile) {
try {
BufferedImage inputImage = ImageIO.read(attachedFile);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
boolean result = ImageIO.write(inputImage, "jpg", byteArrayOutputStream);
return result;
} catch (IOException e) {
System.err.println("Error " + e);
}
return false;
}
But result as a false with not error, so ImageIO.write is not working
Also I found this to do the same but using File object, I don't want to create the file on directory, I just need the byte array
public static boolean convertFormat(String inputImagePath,
String outputImagePath, String formatName) throws IOException {
FileInputStream inputStream = new FileInputStream(inputImagePath);
FileOutputStream outputStream = new FileOutputStream(outputImagePath);
// reads input image from file
BufferedImage inputImage = ImageIO.read(inputStream);
// writes to the output image in specified format
boolean result = ImageIO.write(inputImage, formatName, outputStream);
// needs to close the streams
outputStream.close();
inputStream.close();
return result;
}
Testing
public class TestImageConverter {
public static void main(String[] args) {
String inputImage = "D:/Photo/Pic1.jpg";
String oututImage = "D:/Photo/Pic1.png";
String formatName = "PNG";
try {
boolean result = ImageConverter.convertFormat(inputImage,
oututImage, formatName);
if (result) {
System.out.println("Image converted successfully.");
} else {
System.out.println("Could not convert image.");
}
} catch (IOException ex) {
System.out.println("Error during converting image.");
ex.printStackTrace();
}
}
}
How can I solve my problem?
UPDATED SOLUTION (alternative with no need for Raster and ColorModel)
It had indeed bothered me that my older solution (see below) still required Rasters and ColorModels. I got challenged on my solution, so I spent some more time looking for alternatives. So the best thing I could come up with now is the following:
try {
final FileInputStream fileInputStream = new FileInputStream("dice.png");
final BufferedImage image = ImageIO.read(fileInputStream);
fileInputStream.close(); // ImageIO.read does not close the input stream
final BufferedImage convertedImage = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_RGB);
convertedImage.createGraphics().drawImage(image, 0, 0, Color.WHITE, null);
final FileOutputStream fileOutputStream = new FileOutputStream("dice-test.jpg");
final boolean canWrite = ImageIO.write(convertedImage, "jpg", fileOutputStream);
fileOutputStream.close(); // ImageIO.write does not close the output stream
if (!canWrite) {
throw new IllegalStateException("Failed to write image.");
}
} catch (IOException e) {
e.printStackTrace();
}
I ended up with a copy of the BufferedImage as I did before. It does more or less the same thing, but you can actually reuse the ColorModel and Raster more easily.
drawImage() seems to take care of most of what I did before manually. And since it is standard java library code all the way, it seems indeed to be a better way.
Note that you end up with an Image of type BufferedImage.TYPE_INT_RGB. While it seems to work for the types jpg, png, and gif, I am not sure what will happen to other file formats or files with a different storage ColorModel - information might be lost (e.g. 4 color-channels to 3). For the mentioned types we don't need an alpha channel, even if we convert from gif or jpg to png (it will be Color.WHITE).
OLD SOLUTION
I was not happy with my first design and also it did not quite work the way it should have.
Therefore, I have created one from scratch. I ended up with a little converter for sRGB files. You can convert from png to jpg and vice versa (Edit: Added gif support also). If you want to handle other types feel free to extend this further. You can more or less add it the same way. It might work for other file types as well, but I have not tested them yet. Luckily, it seems that sRGB is quite common though.
Tbh. I have no idea how many combinations and variants (color palettes, precision, quality, b/w, etc.) you can produce or which common properties they share.
Maybe this is good enough for you. Maybe not. At least it was a nice exercise for me.
This solution is by no means perfect. The results looked okay-ish. The file-type conversion worked and the file-size is also smaller than the png.
try {
final String fileName = "dice.png";
final BufferedImage inputImage = ImageIO.read(new FileInputStream(fileName));
final boolean isSRGB = inputImage.getColorModel().getColorSpace().isCS_sRGB();
final String outputFormat = "gif";
if (!isSRGB) {
throw new IllegalArgumentException("Please provide an image that supports sRGB.");
}
final WritableRaster raster = createRaster(inputImage);
final ColorModel colorModel = createColorModel(inputImage);
final BufferedImage outputImage = new BufferedImage(colorModel, raster, colorModel.isAlphaPremultiplied(), null);
final String outputFileName = fileName + "-converted." + outputFormat;
final boolean writeResult = ImageIO.write(outputImage, outputFormat, new FileOutputStream(outputFileName));
if (!writeResult) {
throw new IllegalStateException("Could not convert file: " + fileName + " to format: " + outputFormat);
}
System.out.println(">> Created file: " + outputFileName);
} catch (Exception e) {
e.printStackTrace();
}
#NotNull
public static ColorModel createColorModel(#NotNull BufferedImage bufferedImage) {
Objects.requireNonNull(bufferedImage);
final int type = bufferedImage.getType();
boolean isAlphaPremultiplied = false;
int transparency = Transparency.OPAQUE;
if (type == BufferedImage.TYPE_3BYTE_BGR) {
isAlphaPremultiplied = true;
}
return new ComponentColorModel(
ColorModel.getRGBdefault().getColorSpace(),
false, isAlphaPremultiplied, transparency,
bufferedImage.getData().getDataBuffer().getDataType()
);
}
#NotNull
public static WritableRaster createRaster(#NotNull BufferedImage bufferedImage) {
Objects.requireNonNull(bufferedImage);
final int type = bufferedImage.getType();
final int width = bufferedImage.getWidth();
final int height = bufferedImage.getHeight();
final int pixelStride = 3;
int[] offset = new int[]{0, 1, 2};
DataBufferByte dataBufferByte;
if (type == BufferedImage.TYPE_4BYTE_ABGR || type == BufferedImage.TYPE_BYTE_INDEXED) {
int dataIndex = 0;
final byte[] data = new byte[height * width * pixelStride];
final int bitmask = 0xff;
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
final int rgb = bufferedImage.getRGB(x, y);
final int blue = bitmask & rgb;
final int green = bitmask & (rgb >> 8);
final int red = bitmask & (rgb >> 16);
if (rgb == 0) {
data[dataIndex++] = (byte) bitmask;
data[dataIndex++] = (byte) bitmask;
data[dataIndex++] = (byte) bitmask;
} else {
data[dataIndex++] = (byte) red;
data[dataIndex++] = (byte) green;
data[dataIndex++] = (byte) blue;
}
}
}
dataBufferByte = new DataBufferByte(data, data.length);
} else if (type == BufferedImage.TYPE_3BYTE_BGR) {
dataBufferByte = (DataBufferByte) bufferedImage.getRaster().getDataBuffer();
offset = new int[]{2, 1, 0};
} else {
throw new IllegalArgumentException("Cannot create raster for unsupported image type.");
}
return Raster.createInterleavedRaster(
dataBufferByte, width, height,
pixelStride * width, pixelStride,
offset,
null
);
}
EDIT: Added support for gif.

Java Android - JPEG Image rotation

I'm working on an application to capture images but I'd like to rotate a JPEG image before saving it, I already saw this link :
Android Rotate Picture before saving
This is what I'm doing right now.
ByteBuffer byteBuffer = mImage.getPlanes()[0].getBuffer();
byte[] bytes = new byte[byteBuffer.remaining()];
byteBuffer.get(bytes);
FileOutputStream fileOutputStream = null;
try {
fileOutputStream = new FileOutputStream(mImageFileName);
fileOutputStream.write(bytes);
} catch (IOException e) {
e.printStackTrace();
}
I tried this to rotate the image like this :
// Bytes array to bitmap and matrix rotation
Bitmap sourceBitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
Matrix m = new Matrix();
m.setRotate((float)90, sourceBitmap.getWidth(), sourceBitmap.getHeight());
Bitmap targetBitmap = Bitmap.createBitmap(sourceBitmap, 0, 0, sourceBitmap.getWidth(), sourceBitmap.getHeight(), m, true);
// Bitmap to bytes array
int size = targetBitmap.getRowBytes() * targetBitmap.getHeight();
ByteBuffer targetByteBuffer = ByteBuffer.allocate(size);
targetBitmap.copyPixelsToBuffer(targetByteBuffer);
bytes = targetByteBuffer.array();
But when I look into the file into my gallery, I cannot read it, the image seems broken.
EDIT: Doesn't work on Android 7.1.1 :/ Any idea ? Can I do something similar for a video record?
You are Coverting Your Bitmap to bytes array,
Now You stop That way save Bitmap directly to File
Bitmap sourceBitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
Matrix m = new Matrix();
m.setRotate((float)90, sourceBitmap.getWidth(), sourceBitmap.getHeight());
Bitmap rotatedBitmap= Bitmap.createBitmap(sourceBitmap, 0, 0, sourceBitmap.getWidth(), sourceBitmap.getHeight(), m, true);
// Save Bitmap directly to the file
String filename = "hello.jpg";
File sd = Environment.getExternalStorageDirectory();
File dest = new File(sd, filename);
try {
FileOutputStream out = new FileOutputStream(dest);
bitmap.compress(Bitmap.CompressFormat.JPEG, 90, out);
out.flush();
out.close();
} catch (Exception e) {
e.printStackTrace();
}
This little change apparently did the trick ! Thanks Nikunj !
ByteBuffer byteBuffer = mImage.getPlanes()[0].getBuffer();
byte[] bytes = new byte[byteBuffer.remaining()];
byteBuffer.get(bytes);
Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
Matrix matrix = new Matrix();
matrix.setRotate((float)90, bitmap.getWidth(), bitmap.getHeight());
FileOutputStream fileOutputStream = null;
try {
fileOutputStream = new FileOutputStream(mImageFileName);
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fileOutputStream);
fileOutputStream.flush();
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}

convert Image to byte

I want to resize an image and then to write it back to outputstream, for this I need to convert the scaled image into bytes, how can I convert it?
ByteArrayInputStream bais = new ByteArrayInputStream(ecn.getImageB());
BufferedImage img = ImageIO.read(bais);
int scaleX = (int) (img.getWidth() * 0.5);
int scaleY = (int) (img.getHeight() * 0.5);
Image newImg = img.getScaledInstance(scaleX, scaleY, Image.SCALE_SMOOTH);
outputStream.write(newImg); //cannot resolve
how to fix outputStream.write(newImg)???
Use this method for scaling:
public static BufferedImage scale(BufferedImage sbi,
int imageType, /* type of image */
int destWidth, /* result image width */
int destHeight, /* result image height */
double widthFactor, /* scale factor for width */
double heightFactor /* scale factor for height */ )
{
BufferedImage dbi = null;
if(sbi != null) {
dbi = new BufferedImage(destWidth, destHeight, imageType);
Graphics2D g = dbi.createGraphics();
AffineTransform at = AffineTransform.getScaleInstance(widthFactor, heightFactor);
g.drawRenderedImage(sbi, at);
}
return dbi;
}
Then you'll have a BufferedImage which you can write to a byte array
public static byte[] writeToByteArray(BufferedImage bi, String dImageFormat) throws IOException, Exception {
byte[] scaledImageData = null;
ByteArrayOutputStream baos = null;
try {
if(bi != null) {
baos = new ByteArrayOutputStream();
if(! ImageIO.write(bi, dImageFormat, baos)) {
throw new Exception("no appropriate writer found for the format " + dImageFormat);
}
scaledImageData = baos.toByteArray();
}
} finally {
if(baos != null) {
try {
baos.close();
} catch(Exception e) {
e.printStackTrace();
}
}
}
return scaledImageData;
}
Include this line and check:-
ByteArrayOutputStream outputStream=new ByteArrayOutputStream();
ImageIO.write(originalImage, "jpg", outputStream);
byte[] imageInByte=outputStream.toByteArray();

Sending an image as a byte array between client and server

I can successfully send and draw a resized, 125 x 125 image from my client to my server. only problem is, thats way too small. I want to be able to send a larger image but the byte array can't handle it and I get a java heap exception. currently I'm using this to interpret my image. Is there a more efficient way?
On the client
screenShot = new Robot().createScreenCapture(new Rectangle(Toolkit.getDefaultToolkit().getScreenSize()));
screenShot = resize(screenShot, 125, 125);
ByteArrayOutputStream byteArrayO = new ByteArrayOutputStream();
ImageIO.write(screenShot,"PNG",byteArrayO);
byte [] byteArray = byteArrayO.toByteArray();
out.writeLong(byteArray.length);
out.write(byteArray);
resize method as called above.
public static BufferedImage resize(BufferedImage img, int newW, int newH) {
int w = img.getWidth();
int h = img.getHeight();
BufferedImage dimg = new BufferedImage(newW, newH, img.getType());
Graphics2D g = dimg.createGraphics();
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g.drawImage(img, 0, 0, newW, newH, 0, 0, w, h, null);
g.dispose();
return dimg;
}
server that interprets the image
in = new DataInputStream(Client.getInputStream());
long nbrToRead = in.readLong();
byte[] byteArray = new byte[(int) nbrToRead];
int nbrRd = 0;
int nbrLeftToRead = (int) nbrToRead;
while (nbrLeftToRead > 0) {
int rd = in.read(byteArray, nbrRd, nbrLeftToRead);
if (rd < 0)
break;
nbrRd += rd; // accumulate bytes read
nbrLeftToRead -= rd;
}
ByteArrayInputStream byteArrayI = new ByteArrayInputStream(
byteArray);
image = ImageIO.read(byteArrayI);
if (image != null) {
paint(f.getGraphics(), image);
} else {
System.out.println("null image.");
}
as you can tell the code is massive and most likely inefficient. I could send 1/10 of the image 10 times for with and height, drawing on those parts instead but I wanted to know if there was an easier way to do this.
You should probably think of transferring data as stream over the network. You can make use of third-party libraries like RMIIO . In case you can make data transfer using web service then you can look at Message Transmission Optimization Mechanism (MTOM) which lets you transfer data as stream in more efficient manner. For more details please have a look here
this worked for me
public class ImageClient {
public static void main(String[] args) throws AWTException, IOException {
BufferedImage screenShot = new Robot().createScreenCapture(new Rectangle(Toolkit.getDefaultToolkit().getScreenSize()));
Socket socket = new Socket("localhost",11111);
MemoryCacheImageOutputStream byteArrayO = new MemoryCacheImageOutputStream(socket.getOutputStream());
ImageIO.write(screenShot, "PNG", byteArrayO);
byteArrayO.flush();
socket.close();
}
}
public class ImageServer {
public static void main(String[] args) throws IOException, ClassNotFoundException {
ServerSocket ss = new ServerSocket(11111);
try{
Socket s = ss.accept();
InputStream is = s.getInputStream();
MemoryCacheImageInputStream ois = new MemoryCacheImageInputStream(is);
ImageIO.read(ois);
s.close();
}finally{
ss.close();
}
}
}

Categories

Resources