'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