'PNG Image which received from c++ server, crashes on java android client
I'm developing android marine navigation client. The client should receive nautical chart images from c++ server. I'm sending data via socket. (from c++: ClientSocket.h, to java: java.net.Socket)
Receiving image size was fine.(I bearly noticed that the byte order of java integer type is reversed c++ order)
But Image data are not fine. First I converted the image using
BitmapFactory.decodeByteArray(imageAr, 0, imageAr.length);
And the result was null. So I tried saving the data as a png file in android device. But the image files are crashed. Is there any differences between c++ png format and java png format?
Here are my codes
server.cpp
HBITMAP bmp = hencCaptureBuffer();//get bitmap from nautical chart library
if (bmp == NULL)
{
std::cout << "Failed to create bitmap\n";
return;
}
CImage img;
img.Attach(bmp);
HGLOBAL h_buffer = ::GlobalAlloc(GMEM_MOVEABLE, MAX_IMAGE_SIZE);
if (h_buffer != NULL) {
int png_data_size = 0;
IStream* p_stream = NULL;
if (::CreateStreamOnHGlobal(h_buffer, FALSE, &p_stream) == S_OK) {
img.Save(p_stream, ImageFormatPNG);
p_stream->Release();
}
void* p_png_data = ::GlobalLock(h_buffer);
char* new_png_data = (char*)p_png_data;
int pos = 0;
while (true)
{
if (*(new_png_data + pos) == 0x00 &&
*(new_png_data + pos + 1) == 0x00 &&
*(new_png_data + pos + 2) == 0x00 &&
*(new_png_data + pos + 3) == 0x00 &&
*(new_png_data + pos + 4) == 0x00 &&
*(new_png_data + pos + 5) == 0x00 &&
*(new_png_data + pos + 6) == 0x00 &&
*(new_png_data + pos + 7) == 0x00)
break;
pos++;
}
int p_size = pos - 1;
double p_rate = horiRate;
cs->Send(&p_size, 4, 0);//send image size
cs->Send(new_png_data, p_size, 0);//send image data
cs->Send(&p_rate, 8, 0);
::GlobalUnlock(h_buffer);
::GlobalFree(h_buffer);
}
client.java
package com.mapsea.test002;
import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Build;
import android.os.Bundle;
import android.view.View;
import android.widget.ImageView;
import android.widget.Button;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.InputStream;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.Base64;
public class MainActivity extends AppCompatActivity {
private ImageView img;
private Socket socket;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
try {
socket = new Socket("192.168.20.102", 4578);
} catch (
IOException e) {
e.printStackTrace();
}
}
});
thread.start();
img = (ImageView) findViewById(R.id.ENCImageView);
((Button) findViewById(R.id.button_display))
.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Thread thread = new Thread(new Runnable() {
@RequiresApi(api = Build.VERSION_CODES.O)
@Override
public void run() {
try {
OutputStream outputStream = socket.getOutputStream();
String encString = "1920&1080&126.3457&37.2222&10000&0&10&1&";
byte[] masks = new byte[49];
for (int i = 0; i < masks.length - 1; i++) {
masks[i] = -127;
}
masks[masks.length - 1] = 0;
byte[] tmpParam = encString.getBytes();
byte[] encParam = new byte[tmpParam.length + masks.length];
System.arraycopy(tmpParam, 0, encParam, 0, tmpParam.length);
System.arraycopy(masks, 0, encParam, tmpParam.length, masks.length);
outputStream.write(encParam);
outputStream.flush();
InputStream inputStream = socket.getInputStream();
byte[] sizeAr = new byte[4];
inputStream.read(sizeAr);
byte[] reverseSize = new byte[sizeAr.length];
for (int i = 0; i < sizeAr.length; i++) {
reverseSize[sizeAr.length - i - 1] = sizeAr[i];
}
int size = ByteBuffer.wrap(reverseSize).asIntBuffer().get();
byte[] imageAr = new byte[size];
inputStream.read(imageAr);
File file = new File("/sdcard/DCIM/file3.png");
FileOutputStream fos = new FileOutputStream(file);
fos.write(imageAr);
fos.flush();
fos.close();
Bitmap encbmp = BitmapFactory.decodeByteArray(imageAr, 0, imageAr.length);
if(encbmp == null)
{
encbmp = null;
}
synchronized (this) {
runOnUiThread(new Runnable() {
@Override
public void run() {
//img.setImageBitmap(encbmp);
}
});
}
byte[] rateAr = new byte[8];
inputStream.read(rateAr);
byte[] reverseRate = new byte[rateAr.length];
for (int i = 0; i < rateAr.length; i++) {
reverseRate[rateAr.length - i - 1] = rateAr[i];
}
double rate = ByteBuffer.wrap(reverseRate).asDoubleBuffer().get();
} catch (Exception e) {
e.printStackTrace();
}
}
});
thread.start();
}
});
}
}
Solution 1:[1]
I added my client code like this, and it worked.
The problem is that I tried saving image before the data fully received.
I added do-while to wait complete receiving
byte[] imageAr = new byte[size];
int sumLength = 0;
do {
byte[] tmpImage = new byte[size];
int nRead = inputStream.read(tmpImage, 0, size - sumLength);
for(int i = 0; i < nRead; i++)
{
imageAr[sumLength + i] = tmpImage[i];
}
sumLength += nRead;
}while (sumLength < size);
Bitmap encbmp = BitmapFactory.decodeByteArray(imageAr, 0, imageAr.length);
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 |