Skip to content

Instantly share code, notes, and snippets.

@suzumura-ss
Last active September 20, 2018 06:41
Show Gist options
  • Save suzumura-ss/9a3f85d4dc088656d712c437a2e96603 to your computer and use it in GitHub Desktop.
Save suzumura-ss/9a3f85d4dc088656d712c437a2e96603 to your computer and use it in GitHub Desktop.
Unity/Android環境向けに1個のJPEGを2個のRGBバイト列に分割ロードするローダー / 幅4K〜8KのEqui画像をロードする想定
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
#if UNITY_ANDROID
#else
// PCではopenCVSharpを利用
using System.Runtime.InteropServices;
using OpenCvSharp;
#endif
public class JPEGLoader
{
public delegate void OnCompleteSplitBlock(byte[] leftBytes, byte[] rightBytes, int width, int height, TextureFormat format);
public delegate void OnCompleteRsizeBlock(byte[] bytes, int width, int height, TextureFormat format);
public delegate void OnFailedBlock(Exception e);
/*
@param[in] sourceName Android: ex. /mnt/sdcard/DCIM/SAMPLE.JPG, PC: ex. C:/SAMPLE.JPG
@param[in] onCompleteBlock(byte[] leftBytes, byte[] rightBytes, int width, int height, TextureFormat format)
@param[in] onFailedBlock(Exceptione e)
*/
#if UNITY_ANDROID
static public void Load(string sourceName, OnCompleteSplitBlock onCompleteBlock, OnFailedBlock onFailedBlock)
{
using(AndroidJavaClass BitmapFactory = new AndroidJavaClass("android.graphics.BitmapFactory"))
{
try
{
using(AndroidJavaObject bitmap = BitmapFactory.CallStatic<AndroidJavaObject>("decodeFile", new object[]{ sourceName }))
{
LoadBytesInternal(bitmap, onCompleteBlock);
}
}
catch (Exception e)
{
Debug.LogError($"JPEGLoader: Load({sourceName}) failed - {e}");
onFailedBlock(e);
}
}
}
static public void LoadResource(string sourceName, OnCompleteSplitBlock onCompleteBlock, OnFailedBlock onFailedBlock)
{
using(AndroidJavaClass BitmapFactory = new AndroidJavaClass("android.graphics.BitmapFactory"))
{
try
{
TextAsset sourceAsset = Resources.Load(sourceName) as TextAsset;
using(AndroidJavaObject bitmap = BitmapFactory.CallStatic<AndroidJavaObject>("decodeByteArray", new object[]{ sourceAsset.bytes, 0, sourceAsset.bytes.Length }))
{
LoadBytesInternal(bitmap, onCompleteBlock);
}
}
catch (Exception e)
{
Debug.LogError($"JPEGLoader: LoadResource({sourceName}) failed - {e}");
onFailedBlock(e);
}
}
}
static void LoadBytesInternal( /* android.graphics.Bitmap */ AndroidJavaObject bitmap, OnCompleteSplitBlock onCompleteBlock)
{
byte[] leftBytes = null, rightBytes = null;
int width, height;
using(AndroidJavaClass Bitmap = new AndroidJavaClass("android.graphics.Bitmap"))
using(AndroidJavaClass ByteBuffer = new AndroidJavaClass("java.nio.ByteBuffer"))
using(AndroidJavaObject flip = new AndroidJavaObject("android.graphics.Matrix"))
{
Func<AndroidJavaObject, int, int, int, byte[]> extractPixels = (bmp, w, h, offset) =>
{
using(AndroidJavaObject partBmp = Bitmap.CallStatic<AndroidJavaObject>("createBitmap", new object[]{ bmp, offset, 0, w, h, flip, false }))
{
int size = partBmp.Call<int>("getByteCount");
using(AndroidJavaObject buffer = ByteBuffer.CallStatic<AndroidJavaObject>("allocate", new object[]{ size }))
{
partBmp.Call("copyPixelsToBuffer", new object[]{ buffer });
return buffer.Call<byte[]>("array");
}
}
};
width = bitmap.Call<int>("getWidth") / 2;
height = bitmap.Call<int>("getHeight");
flip.Call<bool>("postScale", new object[]{ 1.0f, -1.0f, (float)width, (float)height });
leftBytes = extractPixels(bitmap, width, height, 0);
rightBytes = extractPixels(bitmap, width, height, width);
}
onCompleteBlock(leftBytes, rightBytes, width, height, TextureFormat.RGBA32);
}
static public void Load(string sourceName, int width, int height, OnCompleteRsizeBlock onCompleteBlock, OnFailedBlock onFailedBlock)
{
using(AndroidJavaClass BitmapFactory = new AndroidJavaClass("android.graphics.BitmapFactory"))
{
try
{
using(AndroidJavaObject bitmap = BitmapFactory.CallStatic<AndroidJavaObject>("decodeFile", new object[]{ sourceName }))
{
LoadBytesInternal(bitmap, width, height, onCompleteBlock);
}
}
catch (Exception e)
{
Debug.LogError($"JPEGLoader: Load {sourceName}({width}x{height}) failed - {e}");
onFailedBlock(e);
}
}
}
static void LoadBytesInternal( /* android.graphics.Bitmap */ AndroidJavaObject bitmap, int width, int height, OnCompleteRsizeBlock onCompleteBlock)
{
byte[] bytes = null;
using(AndroidJavaClass Bitmap = new AndroidJavaClass("android.graphics.Bitmap"))
using(AndroidJavaClass ByteBuffer = new AndroidJavaClass("java.nio.ByteBuffer"))
using(AndroidJavaObject flipResize = new AndroidJavaObject("android.graphics.Matrix"))
{
int orig_w = bitmap.Call<int>("getWidth");
int orig_h = bitmap.Call<int>("getHeight");
flipResize.Call<bool>("preScale", new object[]{ (float)width / (float)orig_w, -(float)height / (float)orig_h });
using(AndroidJavaObject partBmp = Bitmap.CallStatic<AndroidJavaObject>("createBitmap", new object[]{ bitmap, 0, 0, orig_w, orig_h, flipResize, true }))
{
int size = partBmp.Call<int>("getByteCount");
using(AndroidJavaObject buffer = ByteBuffer.CallStatic<AndroidJavaObject>("allocate", new object[]{ size }))
{
width = partBmp.Call<int>("getWidth");
height = partBmp.Call<int>("getHeight");
partBmp.Call("copyPixelsToBuffer", new object[]{ buffer });
bytes = buffer.Call<byte[]>("array");
}
}
}
onCompleteBlock(bytes, width, height, TextureFormat.RGBA32);
}
#else
static public void Load(string sourceName, OnCompleteSplitBlock onCompleteBlock, OnFailedBlock onFailedBlock)
{
try
{
LoadBytesInternal(new Mat(sourceName), onCompleteBlock);
}
catch (Exception e)
{
Debug.LogError($"JPEGLoader: Load {sourceName} failed - {e}");
onFailedBlock(e);
}
}
static public void LoadResource(string sourceName, OnCompleteSplitBlock onCompleteBlock, OnFailedBlock onFailedBlock)
{
try
{
TextAsset sourceAsset = Resources.Load(sourceName) as TextAsset;
LoadBytesInternal(Cv2.ImDecode(sourceAsset.bytes, ImreadModes.Color), onCompleteBlock);
}
catch (Exception e)
{
Debug.LogError($"JPEGLoader: Load {sourceName} failed - {e}");
onFailedBlock(e);
}
}
static void LoadBytesInternal(Mat bitmap, OnCompleteSplitBlock onCompleteBlock)
{
Func<Mat, int, int, int, byte[]> extractPixels = (bmp, w, h, offset) =>
{
Mat partBmp = bmp.Clone(new OpenCvSharp.Rect(offset, 0, w, h));
byte[] pix = new byte[partBmp.Width * partBmp.Height * partBmp.ElemSize()];
Marshal.Copy(partBmp.Data, pix, 0, pix.Length);
return pix;
};
Mat[] bgr = Cv2.Split(bitmap.Flip(FlipMode.X));
Mat rgb = new Mat();
Cv2.Merge(new Mat[]{ bgr[2], bgr[1], bgr[0] }, rgb);
int width = bitmap.Width / 2;
byte[] leftBytes = extractPixels(rgb, width, bitmap.Height, 0);
byte[] rightBytes = extractPixels(rgb, width, bitmap.Height, width);
onCompleteBlock(leftBytes, rightBytes, width, bitmap.Height, TextureFormat.RGB24);
}
static public void Load(string sourceName, int width, int height, OnCompleteRsizeBlock onCompleteBlock, OnFailedBlock onFailedBlock)
{
try
{
LoadBytesInternal(new Mat(sourceName), width, height, onCompleteBlock);
}
catch (Exception e)
{
Debug.LogError($"JPEGLoader: Load {sourceName}({width}x{height}) failed - {e}");
onFailedBlock(e);
}
}
static void LoadBytesInternal(Mat bitmap, int width, int height, OnCompleteRsizeBlock onCompleteBlock)
{
Mat[] bgr = Cv2.Split(bitmap.Resize(new Size(width, height)).Flip(FlipMode.X));
Mat rgb = new Mat();
Cv2.Merge(new Mat[]{ bgr[2], bgr[1], bgr[0] }, rgb);
byte[] pix = new byte[rgb.Width * rgb.Height * rgb.ElemSize()];
Marshal.Copy(rgb.Data, pix, 0, pix.Length);
onCompleteBlock(pix, rgb.Width, rgb.Height, TextureFormat.RGB24);
}
#endif
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment