fcl-image on Android lazarus / 43-Android ---------------------------------------------------------------------- jmpessoa · 2013-07-22 00:59 Hi there! Does Anyone have any experience/information on how to get a simple and pure fcl-image  to Android via JNI? Thanks! ---------------------------------------------------------------------- jmpessoa · 2013-07-23 22:58 In fact I think my problems are related to the format of the pictures in Android Bitmap X fcl-image .... if someone can help me, following the main part of my code: Java Side. Main Activity.... Code: [Select]package org.jmpessoa.usingsurfaceview; import android.os.Bundle; import android.app.Activity; public class MainActivity extends Activity { DrawingBoard MyDraw; <<--- My draw surface.. @Override protected void onCreate(Bundle savedInstanceState) {      super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);                      //Set the content view to a new instance of DrawingBoard.      MyDraw = new DrawingBoard(this);              setContentView(MyDraw); } } My Surface draw... Code: [Select]package org.jmpessoa.usingsurfaceview; import java.io.ByteArrayOutputStream; import java.nio.ByteBuffer; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Color; import android.util.DisplayMetrics; import android.util.Log; import android.view.SurfaceHolder; import android.view.SurfaceView; public class DrawingBoard extends SurfaceView implements SurfaceHolder.Callback, Runnable {    public int width =  75;    public int height = 75;    public int gCount = 0;    private SurfaceHolder surfaceHolder = null;    public Bitmap graphicBitmap = null;    public ByteBuffer graphicBuffer = null;    static{      System.loadLibrary("drawingboard"); <<--- My library    }     public native int getJNIGraphics(ByteBuffer graphicsBuffer, int w, int h, int k);     public DrawingBoard(Context context) {          super(context);         //Retrieve the SurfaceHolder instance associated with this SurfaceView.         surfaceHolder = getHolder();        //Specify this class (DrawingBoard) as the class that implements the             //three callback methods required by SurfaceHolder.Callback.        surfaceHolder.addCallback(this);    }     //SurfaceHolder.Callback callback method.    @Override    public void surfaceCreated(SurfaceHolder holder) {         //Create and start a drawing thread whose Runnable object is defined by         //this class (DrawingBoard).         new Thread(this).start();    }    //Runnable callback method.    @Override    public void run() {          //Lock the canvas before drawing.          Canvas canvas = surfaceHolder.lockCanvas();          render(canvas);          //After drawing, unlock the canvas and display it.         surfaceHolder.unlockCanvasAndPost(canvas);    }    private void render(Canvas canvas) {                     graphicBitmap = Bitmap.createBitmap(metrics, width, height, Bitmap.Config.ARGB_8888);     graphicBuffer = ByteBuffer.allocateDirect(width*height*4); //ARGB_8888 --> 4 bytes     gCount= getJNIGraphics(graphicBuffer, width, height, 4); //<<--- JNI Call...........     Log.e(VIEW_LOG_TAG, Integer.toString(gCount));     canvas.drawColor(Color.YELLOW);      if (gCount > 0) {        graphicBitmap.copyPixelsFromBuffer(graphicBuffer); //copy the buffer inside the bitmap canvas.drawBitmap(graphicBitmap, 100,100, null);    }          }   //Neither of these two methods are used in this example, however, their definitions   //are required because SurfaceHolder.Callback was implemented.   @Override public void surfaceChanged(SurfaceHolder sh, int f, int w, int h) {}   @Override public void surfaceDestroyed(SurfaceHolder sh) {} } Pascal Side (just incomplete).... Code: [Select]var   PImgJavaDirectBuffer: PJByte;   FclImageStream: TMemoryStream;   Img: TFPMemoryImage;   WriterPNG: TFPWriterPNG;   WriterJPEG: TFPWriterJPEG;   ImgCanvas: TFPImageCanvas; function getGraphics(w: longint; h: longint; k: int64): longint; begin   Img:=TFPMemoryImage.Create(w, h);   Img.UsePalette:=false;   ImgCanvas:=TFPImageCanvas.create(Img);   ImgCanvas.Brush.FPColor:= colGreen;   ImgCanvas.Brush.Style:= bsSolid;   ImgCanvas.pen.mode    := pmCopy;   ImgCanvas.pen.style   := psSolid;   ImgCanvas.pen.width   := 1;   ImgCanvas.pen.FPColor := colRed;   {Simple draw test.. }   ImgCanvas.Rectangle(10,10, Trunc(w/2), Trunc(h/2));   FclImageStream:= TMemoryStream.Create;   //WriterPNG:=TFPWriterPNG.Create;   WriterJPEG:= TFPWriterJPEG.Create;   //WriterPNG.ImageWrite(FclImageStream,Img);   WriterJPEG.CompressionQuality:= 100;   WriterJPEG.ImageWrite(FclImageStream,Img);   FclImageStream.Position:= 0;   if FclImageStream.Size <= k then   begin      Result:= FclImageStream.Size;      //Move(FclImageStream.Memory^, PImgJavaDirectBuffer^, FclImageStream.Size); //ok too..      FclImageStream.ReadBuffer(PImgJavaDirectBuffer^, FclImageStream.Size);  //ok   end else Result:= 0;   FclImageStream.Free;   //WriterPNG.Free;   WriterJPEG.Free;   ImgCanvas.Free;   Img.Free; end; { Class:     org_jmpessoa_usingsurfaceview_DrawingBoard   Method:    getJNIGraphics   Signature: (Ljava/nio/ByteBuffer;III)I } function getJNIGraphics(PEnv: PJNIEnv; this: JObject; graphicsBuffer: JObject; w: JInt; h: JInt;  k: JInt): JInt; cdecl; var   jlen: int64;   plen: longint; begin   PImgJavaDirectBuffer:= PJByte((PEnv^).GetDirectBufferAddress(PEnv,graphicsBuffer));   jlen:= (PEnv^).GetDirectBufferCapacity(PEnv, graphicsBuffer);   Result:= JInt(getGraphics(w, h, jlen)); end; Follows attached the picture I got so far... Any contribution will be well received. Thanks in advance! PS.  I am using cross fcp 2.5.1 (hack) on Windows... ---------------------------------------------------------------------- felipemdc · 2013-07-28 06:16 Hello, LCL-CustomDrawn does something similar to what you are trying. Study it and adapt code from it. In file lazarus/lcl/interfaces/customdrawn/customdrawnobject_android.inc there is this JNI routine: function Java_com_pascal_lclproject_LCLActivity_LCLDrawToBitmap(     env:PJNIEnv;this:jobject; width, height: jint; abitmap: jobject): jint; cdecl; It will draw to a Bitmap java object provided by the Java side. Study also the routine UpdateControlLazImageAndCanvas in the unit lcl/interfaces/customdrawn/customdrawnproc.pas Note how LCLDrawToBitmap will call UpdateControlLazImageAndCanvas with format set to clfRGBA32 This is because the pixel format of the java Bitmap object is RGBA32. So you need to create a surface in this format to draw to. But don't create your own image data, instead use the pointer provided by AndroidBitmap_lockPixels. LCL-CustomDrawn uses it's own extension around fcl-image, so it is not pure fcl-image, but the extensions are not so big, I guess you could copy them to your project or adjust this to more pure fcl-image. ---------------------------------------------------------------------- jmpessoa · 2013-07-28 16:51 Thank you Felipe! Greetings! ---------------------------------------------------------------------- jmpessoa · 2013-07-28 23:15 Hello People! Yes! Fcl-Image does Android! Here is a very simple and didactic code (proof of concept!). Java Side: 1.Main Activity Code: [Select]package org.jmpessoa.fpfclimageonandroid; import android.os.Bundle; import android.app.Activity; import android.view.Menu; public class MainActivity extends Activity {     public FclImageDrawingBoard DrawingBoard;  //<<---  .so warapper... @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main);                                DrawingBoard = new FclImageDrawingBoard(this); //<<---- create warapper setContentView(DrawingBoard); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); return true; } } 2.Surface Draw Code: [Select]package org.jmpessoa.fpfclimageonandroid; import java.nio.ByteBuffer; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; import android.util.Log; import android.view.View; public class FclImageDrawingBoard extends View {         public int width =  100;     public int height = 100;     public int okRes = 0;     public Bitmap graphicBitmap = null;     public ByteBuffer graphicBuffer = null;    static{        System.loadLibrary("fclimagedrawingboard");    }    public native int getJNIFclImageGraphics(ByteBuffer graphicsBuffer, int w, int h); //JNI Method    public FclImageDrawingBoard(Context context) {              super(context);      }    @Override      public void onDraw(Canvas c){           super.onDraw(c);           this.setBackgroundColor(Color.BLACK);          render(c);      }        private void render(Canvas canvas){           Log.e(VIEW_LOG_TAG, "rendering...");          graphicBuffer = ByteBuffer.allocateDirect(width*height*4); //4 -->> ARGB_8888                okRes= getJNIFclImageGraphics(graphicBuffer, width, height);          Log.e(VIEW_LOG_TAG, Integer.toString(okRes));                 if (okRes > 0) {      graphicBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);   graphicBitmap.copyPixelsFromBuffer(graphicBuffer);  canvas.drawBitmap(graphicBitmap, 50, 50, null);         }      } } Pascal Side: Code: [Select]library fclimagedrawingboard; {$mode delphi} uses   Classes, SysUtils, CustApp, jni, unit1, FPimage, FPImgCanv,FPCanvas; const   curClassPathName: string='';   curClass: JClass=nil;   curVM: PJavaVM=nil;   curEnv: PJNIEnv=nil; type   TColorRGB=packed record {copy from  ...\fcl-image\src\BMPcomn.pp}      B,G,R:Byte;   end;   TColorRGBA=packed record  {copy from  ...\fcl-image\src\BMPcomn.pp}   case Boolean of       False:(B,G,R,A:Byte);       True:(RGB:TColorRGB);   end; var   PImgJavaDirectBuffer: PJByte;   {copy from  ...\fcl-image\src\fpwritebmp.pp} Function FPColorToRGBA(Const Color : TFPColor) : TColorRGBA; begin   With Result,Color do     begin     R:=(Red   and $FF00) shr 8;     G:=(Green and $FF00) shr 8;     B:=(Blue  and $FF00) shr 8;     A:=(Alpha and $FF00) shr 8;     end; end; function getFclImageGraphics(w: longint; h: longint; bufLen: int64): boolean; var    Img: TFPMemoryImage;    ImgCanvas: TFPImageCanvas;    Bits: PByte; //array of byte... That's where the RGBA array goes.    i, col, row: integer;    rgba: TColorRGBA; begin   Result:= True;   Img:=TFPMemoryImage.Create(w, h);   //Img.UsePalette:= True;   ImgCanvas:=TFPImageCanvas.create(Img);   ImgCanvas.Brush.FPColor:= colYellow;   ImgCanvas.Brush.Style:= bsSolid;   ImgCanvas.pen.mode:= pmCopy;   ImgCanvas.pen.style:= psSolid;   ImgCanvas.pen.width:= 1;   ImgCanvas.pen.FPColor:= colYellow;   ImgCanvas.Rectangle(0,0,Img.Width-1,Img.Height-1); //frame   ImgCanvas.Brush.Style:= bsClear;   {Draw a simple test.. }   ImgCanvas.pen.FPColor := colBlue;   ImgCanvas.Line(2,2,w-2,h-2);   ImgCanvas.Line(2,h-2,w-2,2);   ImgCanvas.pen.FPColor:= colRed;   ImgCanvas.Rectangle(10,10, w-10, h-10);   ImgCanvas.pen.FPColor := colLime; //Light   ImgCanvas.Ellipse(12,12,w-12,h-12);   //convert  FPColor To RGBA (Android)   try       Bits:= GetMem(bufLen);       i:=0;       for col:= 0 to w-1 do       begin         for row:= 0 to h-1 do         begin           rgba:= FPColorToRGBA(Img.Colors[col,row]);           Bits[i*4]:=   rgba.R;           Bits[i*4+1]:= rgba.G;           Bits[i*4+2]:= rgba.B;           Bits[i*4+3]:= rgba.A; //-1, that's the alpha.           inc(i);         end;       end;       Move(Bits^, PImgJavaDirectBuffer^, bufLen);       FreeMem(Bits);   except     Result:= False   end;   ImgCanvas.Free;   Img.Free; end; { Class:     org_jmpessoa_fpfclimageonandroid_FclImageDrawingBoard   Method:    getJNIFclImageGraphics   Signature: (Ljava/nio/ByteBuffer;II)I } function getJNIFclImageGraphics(PEnv: PJNIEnv; this: JObject; graphicsBuffer: JObject; w: JInt; h: JInt): JInt; cdecl; var   jBuflen: int64; begin   PImgJavaDirectBuffer:= PJByte((PEnv^).GetDirectBufferAddress(PEnv,graphicsBuffer));   jBuflen:= (PEnv^).GetDirectBufferCapacity(PEnv, graphicsBuffer);   if getFclImageGraphics(w, h, jBuflen) then Result:= 1 else Result:= 0; end; const NativeMethods:array[0..0] of JNINativeMethod = (    (name:'getJNIFclImageGraphics';     signature:'(Ljava/nio/ByteBuffer;II)I';     fnPtr:@getJNIFclImageGraphics;) ); function RegisterNativeMethodsArray(PEnv: PJNIEnv; className: PChar; methods: PJNINativeMethod; countMethods:integer):integer; begin   Result:= JNI_FALSE;   curClass:= (PEnv^).FindClass(PEnv, className);   if curClass <> nil then   begin     if (PEnv^).RegisterNatives(PEnv, curClass, methods, countMethods) > 0 then Result:= JNI_TRUE;   end; end; function RegisterNativeMethods(PEnv: PJNIEnv): integer; begin   curClassPathName:= 'org/jmpessoa/fpfclimageonandroid/FclImageDrawingBoard';   Result:= RegisterNativeMethodsArray(PEnv, PChar(curClassPathName), @NativeMethods[0], Length(NativeMethods)); end; function JNI_OnLoad(vm: PJavaVM; reserved: pointer): JInt; cdecl; var   PEnv: PPointer {PJNIEnv}; begin   PEnv:= nil;   (vm^).GetEnv(vm, @PEnv, JNI_VERSION_1_6); //TODO: sanity code!   Result:= JNI_VERSION_1_6;   if PEnv <> nil then RegisterNativeMethods(PJNIEnv(PEnv));   if PEnv <> nil then RegisterNativeMethods(PJNIEnv(PEnv));   curVM:= vm {PJavaVM};   curEnv:= PJNIEnv(PEnv); end; procedure JNI_OnUnload(vm: PJavaVM; reserved: pointer); cdecl; begin   if curEnv <> nil then (curEnv^).UnregisterNatives(curEnv, curClass);   curClass:= nil;   curEnv:= nil;   curVM:= nil; end; exports   JNI_OnLoad name 'JNI_OnLoad',   JNI_OnUnload name 'JNI_OnUnload',   getJNIFclImageGraphics name 'Java_org_jmpessoa_fpfclimageonandroid_F clImageDrawingBoard_getJNIFclImageGraphics'; begin end. Thanks to everyone and especially to Felipe who offered me great insight! Follows an attached picture.... Have Fun! ---------------------------------------------------------------------- felipemdc · 2013-07-31 05:23 Great to see it works =) One hint: If speed is a concern, instead of writing to a buffer and then writing that buffer to a Bitmap java object, you can write directly to the Bitmap java object in Pascal using "jnigraphics.pas", which are bindings to the jnigraphics native library in Android. It was introduced maybe in Android 2.2 or something like that. Using Jnigraphics means doing the call to AndroidBitmap_lockPixels. It will be faster and use less memory. That's the way recommended by the Android Google team. For small and/or static things you probably won't notice any difference, but for me it was important since I was doing fullscreen motion graphics. Thought I can't say exactly how to modify your code to do it since, like I said, I use a lot of extensions around fcl-image in LCL- CustomDrawn. ---------------------------------------------------------------------- jmpessoa · 2013-08-02 13:46 Ok Felipe. I Found "bitmap.pas"  (AKA "jnigraphics.pas"?). I implemented a new example illustrating the two techniques... (curiosity: the code structure mimics "Apache Module" wizard, because I'm implementing  my own "Android Module Wizard"  ) Java Side: 1.Main Activity Code: [Select]package org.jmpessoa.laz4androidapp2; import android.os.Bundle; import android.app.Activity; import android.view.Menu; public class MainActivity extends Activity { FPfclImageWrapper fclboard;  //<--- view @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); fclboard = new FPfclImageWrapper(this); setContentView(fclboard); } @Override public boolean onCreateOptionsMenu(Menu menu) {    //Inflate the menu; this adds items to the action bar if it is present.    getMenuInflater().inflate(R.menu.main, menu);   return true; } } 2. View/fcl-Image-warapper Code: [Select]package org.jmpessoa.laz4androidapp2; import java.nio.ByteBuffer; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; import android.util.Log; import android.view.View; public class FPfclImageWrapper extends View {         public int width =  100;     public int height = 100;     public int okRes = 0;     public Bitmap graphicBitmap = null;     public ByteBuffer graphicBuffer = null;     static{  //TODO: sanity code         System.loadLibrary("fpfclimagewrapper");     }     public native int getJNIFclImageGraphics1(ByteBuffer graphicsBuffer, int w, int h);     public native int getJNIFclImageGraphics2(Bitmap graphicsBuffer, int w, int h);      public FPfclImageWrapper(Context context) {    super(context);       }        @Override    public void onDraw(Canvas c){       super.onDraw(c);       this.setBackgroundColor(Color.BLACK);       imageRender1(c);       imageRender2(c);    }     private void imageRender1(Canvas canvas){         Log.e(VIEW_LOG_TAG, "Rendering By Method 1...");             graphicBuffer = ByteBuffer.allocateDirect(width*height*4);               okRes= getJNIFclImageGraphics1(graphicBuffer, width, height);                 Log.e(VIEW_LOG_TAG, Integer.toString(okRes));           if (okRes > 0) {      graphicBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);   graphicBitmap.copyPixelsFromBuffer(graphicBuffer);  canvas.drawBitmap(graphicBitmap, 50, 50, null);        }    }    private void imageRender2(Canvas canvas){       Log.e(VIEW_LOG_TAG, "Rendering By Method 2...");       graphicBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);        okRes= getJNIFclImageGraphics2(graphicBitmap, width, height);        if (okRes > 0) {            canvas.drawBitmap(graphicBitmap, 160, 50, null);            Log.e(VIEW_LOG_TAG, Integer.toString(okRes));        }    } } Pascal Side: 1. Unit1/DataModule form Code: [Select]{Hint: save all files to location: C:\adt32\eclipse\workspace\Laz4AndroidApp2\jni } unit unit1;   {$mode objfpc}{$H+}   interface   uses   Classes, SysUtils, FPimage, FPImgCanv, FPCanvas;   type   TColorRGB=packed record {copy from  ...\fcl-image\src\BMPcomn.pp}     B,G,R:Byte;   end;   TColorRGBA=packed record  {copy from  ...\fcl-image\src\BMPcomn.pp}     case Boolean of       False:(B,G,R,A:Byte);       True:(RGB:TColorRGB);   end;   { TFclImageBridge }   TFclImageBridge = class     private       //     public       FImg: TFPMemoryImage;       FImgCanvas: TFPImageCanvas;       //constructor Create;       destructor Destroy; override;       function FPColorToRGBA(Const Color: TFPColor): TColorRGBA;       procedure DrawGraphics1(w: longint; h: longint);       procedure DrawGraphics2(w: longint; h: longint);       function GetRGBAGraphics(const buffer: PByte): boolean;   end;   { TAndroidModule1 }   TAndroidModule1 = class(TDataModule)       procedure DataModuleCreate(Sender: TObject);       procedure DataModuleDestroy(Sender: TObject);     private       {private declarations}     public       FfclImageBridge: TFclImageBridge;       {public declarations}   end; var   AndroidModule1: TAndroidModule1; implementation   {$R *.lfm} { TFclImageBridge } {copy from  ...\fcl-image\src\fpwritebmp.pp} function TFclImageBridge.FPColorToRGBA(const Color : TFPColor): TColorRGBA; begin   with Result,Color do   begin     R:=(Red   and $FF00) shr 8;     G:=(Green and $FF00) shr 8;     B:=(Blue  and $FF00) shr 8;     A:=(Alpha and $FF00) shr 8;   end; end; procedure  TFclImageBridge.DrawGraphics1(w: longint; h: longint); begin   if FImg <> nil then   begin      if FImgCanvas <> nil then FImgCanvas.Free;      FImg.Free;   end;   FImg:=TFPMemoryImage.Create(w, h);   FImg.UsePalette:= False;   FImgCanvas:=TFPImageCanvas.create(FImg);   FImgCanvas.Brush.FPColor:= colYellow;   FImgCanvas.Brush.Style:= bsSolid;   FImgCanvas.pen.mode:= pmCopy;   FImgCanvas.pen.style:= psSolid;   FImgCanvas.pen.width:= 1;   FImgCanvas.pen.FPColor:= colYellow;   FImgCanvas.Rectangle(0,0,FImg.Width-1,FImg.Height-1); //frame   FImgCanvas.Brush.Style:= bsClear;   {Draw a simple test.. }   FImgCanvas.pen.FPColor := colBlue;   FImgCanvas.Line(2,2,w-2,h-2);   FImgCanvas.Line(2,h-2,w-2,2);   FImgCanvas.pen.FPColor:= colRed;   FImgCanvas.Rectangle(10,10, w-10, h-10);   FImgCanvas.pen.FPColor := colLime;   FImgCanvas.Ellipse(12,12,w-12,h-12); end; procedure  TFclImageBridge.DrawGraphics2(w: longint; h: longint); begin   if FImg <> nil then   begin      if FImgCanvas <> nil then FImgCanvas.Free;      FImg.Free;   end;   FImg:=TFPMemoryImage.Create(w, h);   FImg.UsePalette:= False;   FImgCanvas:=TFPImageCanvas.create(FImg);   FImgCanvas.Brush.FPColor:= colGreen;   FImgCanvas.Brush.Style:= bsSolid;   FImgCanvas.pen.mode:= pmCopy;   FImgCanvas.pen.style:= psSolid;   FImgCanvas.pen.width:= 1;   FImgCanvas.pen.FPColor:= colGreen;   FImgCanvas.Rectangle(0,0,FImg.Width-1,FImg.Height-1); //frame   FImgCanvas.Brush.Style:= bsClear;   {Draw a simple test.. }   FImgCanvas.pen.FPColor := colBlue;   FImgCanvas.Line(2,2,w-2,h-2);   FImgCanvas.Line(2,h-2,w-2,2);   FImgCanvas.pen.FPColor:= colRed;   FImgCanvas.Rectangle(10,10, w-10, h-10);   FImgCanvas.pen.FPColor := colMagenta;   FImgCanvas.Ellipse(12,12,w-12,h-12); end; function TFclImageBridge.GetRGBAGraphics(const buffer: PByte): boolean; var   i, col, row: integer;   rgba: TColorRGBA; begin   Result:= True;   try     i:=0;     for row:= 0 to FImg.Height-1 do     begin       for col:= 0 to FImg.Width-1 do       begin         rgba:= FPColorToRGBA(FImg.Colors[col,row]);         buffer[i*4]:=   rgba.R;         buffer[i*4+1]:= rgba.G;         buffer[i*4+2]:= rgba.B;         buffer[i*4+3]:= rgba.A; //-1, that's the alpha.         inc(i);       end;     end;   except     Result:= False;   end; end; destructor TFclImageBridge.Destroy; begin   if FImgCanvas <> nil then FImgCanvas.Free;   if FImg <> nil then FImg.Free;   inherited Destroy; end; { TAndroidModule1 } procedure TAndroidModule1.DataModuleCreate(Sender: TObject); begin    FfclImageBridge:= TFclImageBridge.Create; end; procedure TAndroidModule1.DataModuleDestroy(Sender: TObject); begin    FfclImageBridge.Free; end; end. 2. Library Code: [Select]{hint: save all files to location: C:\adt32\eclipse\workspace\Laz4AndroidApp2\jni } library fpfclimagewrapper; {$mode delphi} uses   Classes, SysUtils, CustApp, jni, unit1, bitmap{libjnigraphics.so}; const   curClassPathName: string='';   curClass: JClass=nil;   curVM: PJavaVM=nil;   curEnv: PJNIEnv=nil; type   TAndroidApplication = class(TCustomApplication)    public      procedure CreateForm(InstanceClass: TComponentClass; out Reference);      constructor Create(TheOwner: TComponent); override;      destructor Destroy; override;   end; procedure TAndroidApplication.CreateForm(InstanceClass: TComponentClass; out Reference); var   Instance: TComponent; begin   Instance := TComponent(InstanceClass.NewInstance);   TComponent(Reference):= Instance;   Instance.Create(Self); end; constructor TAndroidApplication.Create(TheOwner: TComponent); begin   inherited Create(TheOwner);   StopOnException:=True; end; destructor TAndroidApplication.Destroy; begin   inherited Destroy; end; var   Application: TAndroidApplication;   PImgJavaPixelBuffer: PJByte; { Class:     org_jmpessoa_laz4androidapp2_FPfclImageWrapper   Method:    getJNIFclImageGraphics1   Signature: (Ljava/nio/ByteBuffer;II)I } function getJNIFclImageGraphics1(PEnv: PJNIEnv; this: JObject; graphicsBuffer: JObject; w: JInt; h: JInt): JInt; cdecl; var   jBuflen: int64; begin   Result:= 1;  //TODO: sanity code   PImgJavaPixelBuffer:= PJByte((PEnv^).GetDirectBufferAddress(PEnv,graphicsBuffer));   jBuflen:= (PEnv^).GetDirectBufferCapacity(PEnv, graphicsBuffer);   AndroidModule1.FfclImageBridge.DrawGraphics1(w, h);   AndroidModule1.FfclImageBridge.GetRGBAGraphics(PByte(PImgJavaPixelBu ffer)); end; { Class:     org_jmpessoa_laz4androidapp2_FPfclImageWrapper   Method:    getJNIFclImageGraphics2   Signature: (Landroid/graphics/Bitmap;II)I } function getJNIFclImageGraphics2(PEnv: PJNIEnv; this: JObject; graphicsBuffer: JObject; w: JInt; h: JInt): JInt; cdecl; begin   Result:= 1; //TODO: sanity code   AndroidBitmap_lockPixels(PEnv, graphicsBuffer, @PImgJavaPixelBuffer{: PPointer});   AndroidModule1.FfclImageBridge.DrawGraphics2(w, h);   AndroidModule1.FfclImageBridge.GetRGBAGraphics(PByte(PImgJavaPixelBu ffer));   AndroidBitmap_unlockPixels(PEnv, graphicsBuffer); end; const NativeMethods:array[0..1] of JNINativeMethod = (    (name:'getJNIFclImageGraphics1';     signature:'(Ljava/nio/ByteBuffer;II)I';     fnPtr:@getJNIFclImageGraphics1;),    (name:'getJNIFclImageGraphics2';     signature:'(Landroid/graphics/Bitmap;II)I';     fnPtr:@getJNIFclImageGraphics2;) ); function RegisterNativeMethodsArray(PEnv: PJNIEnv; className: PChar; methods: PJNINativeMethod; countMethods:integer):integer; begin   Result:= JNI_FALSE;   curClass:= (PEnv^).FindClass(PEnv, className);   if curClass <> nil then   begin     if (PEnv^).RegisterNatives(PEnv, curClass, methods, countMethods) > 0 then Result:= JNI_TRUE;   end; end; function RegisterNativeMethods(PEnv: PJNIEnv): integer; begin   curClassPathName:= 'org/jmpessoa/laz4androidapp2/FPfclImageWrapper';   Result:= RegisterNativeMethodsArray(PEnv, PChar(curClassPathName), @NativeMethods[0], Length(NativeMethods)); end; function JNI_OnLoad(VM: PJavaVM; reserved: pointer): JInt; cdecl; var   PEnv: PPointer {PJNIEnv}; begin   PEnv:= nil;   Result:= JNI_VERSION_1_6;   (VM^).GetEnv(VM, @PEnv, Result);   if PEnv <> nil then RegisterNativeMethods(PJNIEnv(PEnv));   curVM:= VM {PJavaVM};   curEnv:= PJNIEnv(PEnv); end; procedure JNI_OnUnload(VM: PJavaVM; reserved: pointer); cdecl; begin   if curEnv <> nil then (curEnv^).UnregisterNatives(curEnv, curClass);   curClass:= nil;   curEnv:= nil;   curVM:= nil;   Application.Terminate;   FreeAndNil(Application); end; exports   JNI_OnLoad name 'JNI_OnLoad',   JNI_OnUnload name 'JNI_OnUnload',   getJNIFclImageGraphics1 name 'Java_org_jmpessoa_laz4androidapp2_FPfc lImageWrapper_getJNIFclImageGraphics1',   getJNIFclImageGraphics2 name 'Java_org_jmpessoa_laz4androidapp2_FPfc lImageWrapper_getJNIFclImageGraphics2'; begin   Application:= TAndroidApplication.Create(nil);   Application.Title:= 'My Android Library';   Application.Initialize;   Application.CreateForm(TAndroidModule1, AndroidModule1); end. follows  the picture attached... Thank You!