B4A Library ABExtDrawing 1.0

alwaysbusy

Expert
Licensed User
ABExtDrawing 1.7

ABExtDrawing is a wrapper around the Android Drawing classes.

14/08/2012: Updated to version 1.7

Bugfixes and additional functions for blurring and masks.

16/02/2012: Updated to version 1.1

It makes it possible to access all the drawing functions of the Canvas. It is like the B4A canvas, but you can also use all Paints.

Several sub objects are also included:
ABPaint: a wrapper for thePaint class
ABMatrix: a wrapper for the Matrix class
ABRegion: a wrapper for the Region class
ABColorMatrix: a wrapper for the ColorMatrix class
ABCamera: a wrapper for the Camera class [NEW version 1.1]
ABRgbFunctions: several functions to manipulate RGB values

Also two extended classes
ABRectF: a wrapper around the RectF class. B4A contains the Rect class, but to use some of the functions of this library I needed the float version of Rect.
ABPath: a full wrapper for the Path class. B4A contains the Path class but only exposes LineTo. ABPath exposes all functions (like addArc, addOval, etc...)

This lib is to big to list all functions, but a lot of them are used in the attached demo. It is a B4A translation of the Thermometer project on Mind The Robot



How to use this library:

The main object is ABExtDrawing. You pass the B4A canvas to each function:
B4X:
Sub Globals
        Dim ExDraw As ABExtDrawing
        Dim MyCanvas As Canvas
        Dim Panel1 as Panel
end Sub

Sub Activity_Create(FirstTime As Boolean)
        If FirstTime Then
      Activity.LoadLayout("2")
      MyCanvas.Initialize(Panel1)   
   End If
        drawRim(MyCanvas)
End Sub

Sub drawRim(Canv As Canvas)
   ' first, draw the metallic body
   ExDraw.drawOval(Canv, rimRect, rimPaint)
   ' now the outer rim circle
   ExDraw.drawOval(Canv, rimRect, rimCirclePaint)
End Sub
The fun part is you can create all kind of Paints:
B4X:
        ' the linear gradient Is a Bit skewed For realism
   rimPaint.Initialize
   rimPaint.SetFlags(rimPaint.flag_ANTI_ALIAS_FLAG)
   rimPaint.SetLinearGradient2(1,0.40, 0.0, 0.60, 1.0, Colors.RGB(0xf0, 0xf5, 0xf0),Colors.RGB(0x30, 0x31, 0x30),rimPaint.ShaderTileMode_CLAMP)   
   rimPaint.DoShaderSingle(1)

   rimCirclePaint.Initialize
   rimCirclePaint.SetAntiAlias(True)
   rimCirclePaint.SetStyle(rimCirclePaint.Style_STROKE)
   rimCirclePaint.SetColor(Colors.ARGB(0x4f, 0x33, 0x36, 0x33))
   rimCirclePaint.SetStrokeWidth(0.005)
or make extended Paths:
B4X:
        handPath.Initialize
   handPath.moveTo(0.5, 0.5 + 0.2)
   handPath.lineTo(0.5 - 0.010, 0.5 + 0.2 - 0.007)
   handPath.lineTo(0.5 - 0.002, 0.5 - 0.32)
   handPath.lineTo(0.5 + 0.002, 0.5 - 0.32)
   handPath.lineTo(0.5 + 0.010, 0.5 + 0.2 - 0.007)
   handPath.lineTo(0.5, 0.5 + 0.2)
   handPath.addCircle(0.5, 0.5, 0.025, handPath.Direction_CW)
You can also use the Save and Restore functions of the canvas:
B4X:
Sub drawScale(Canv As Canvas)
   ExDraw.drawOval(Canv, scaleRect, ScalePaint)

   ExDraw.save2(Canv, ExDraw.MATRIX_SAVE_FLAG)
   Dim i As Int
   Dim y1 As Float
   Dim y2 As Float
   Dim value As Int
   Dim valueString As String
   For i = 0 To totalNicks
      y1 = scaleRect.top
      y2 = y1 - 0.020
         
      ExDraw.drawLine(Canv, 0.5, y1, 0.5, y2, ScalePaint)
         
      If (i Mod 5 = 0) Then
         value = nickToDegree(i)
         If (value >= minDegrees AND value <= maxDegrees) Then
            valueString = value
            ExDraw.drawText(Canv, valueString, 0.5, y2 - 0.015, ScalePaint)
         End If
      End If
         
      ExDraw.rotate2(Canv, degreesPerNick, 0.5, 0.5)
   Next
   ExDraw.restore(Canv)
End Sub
For more information on what is possible, look into the Android documentation

There is a new article on the Camera class (see the AB3DCamera project) that shows you how to make a 3D list with ABExtDrawing 1.1. The article can be found at Alwaysbusy's Corner. The project is also attached to this post.

 

Attachments

Last edited:

joseluis

Active Member
Licensed User
Thank you for sharing! This library has so many possibilities, and the example is very useful. :icon_clap:.
 

peacemaker

Expert
Licensed User
Interesting. Is it possible to get normally sized grayscaled bitmap from a source one ?
 

alwaysbusy

Expert
Licensed User
GreyScale example

@peacemaker: I've updated the library so that you also can use the class ABColorMatrix. Now you can write a GreyScale function like this:

B4X:
Sub GrayScale(bmp As Bitmap) As Bitmap
    Dim bmpGrayscale As Bitmap
    bmpGrayscale.InitializeMutable(bmp.Width, bmp.height)
    Dim c As Canvas
    c.Initialize2(bmpGrayscale)

    Dim paint As ABPaint
    paint.Initialize
   
    Dim mat As ABColorMatrix
    mat.Initialize
    mat.SetSaturation(0)

    paint.SetColorMatrixColorFilter2(mat)

    ExDraw.drawBitmap2(c, bmp, 0, 0, paint)

    Return bmpGrayscale
End Sub
 

bluejay

Active Member
Licensed User
This is a great library addition - thanks for sharing this :sign0142:

Also a very nice example.

Some comments regarding the example:
Activity objects need to initialized 'everytime' the activity is created so

Activity.LoadLayout("2")
MyCanvas.Initialize(Panel1)

should not be inside an 'If Firstime then'

Similarly the PhoneSensors objects should be Dim'd in Process Globals.

The B4A version is much easier to read than the Java version - great work on the B4A conversion.:icon_clap:

thanks

bluejay
 

hdtvirl

Active Member
Licensed User
Thanks for the Library

Always Busy, If I was in Belgium I would buy you an Orval Beer or what ever else you wanted to drink, This work is outstanding.

I have signed up for your news letter also.

Regards


hdtvirl
 

alwaysbusy

Expert
Licensed User
Glad I could help :)

@bparent: a wrapper is actually very easy to write. It is just a lot of typing work. eg for the Path wrapper:

What I did was include the real Java object as a protected property:
B4X:
protected Path mPath;
Replace all the constructors of Path by Initialize functions. Make sure all parameters you set in the functions are known b4a objects!
B4X:
public void Initialize() {
      mPath==new Path();
}
      
public void Initialize2(ABPath path) {
      mPath = path.mPath;
}
I added some of the enums this class needed as finals. You could make subclasses to do this but this is to avoid to many unneeded objects are visible in b4a. There may be a better way to expose Enums in b4a, but I don't know how.
B4X:
public static final int Direction_CCW=0;
public static final int Direction_CW=1;
      
public static final int FillType_EVEN_ODD=0;
public static final int FillType_INVERSE_EVEN_ODD=1;
public static final int FillType_INVERSE_WINDING=2;
public static final int FillType_WINDING=3;
And then it is just a matter of taking the android documentation next to you and start copying all the functions and making the needed changes.

Very important: make sure all function names are unique!
If for example in the documentation there are two addPath functions, you have to give them different names in the wrapper (addPath and addPath2).

B4X:
public void addPath(ABPath src) {
   mPath.addPath(src.mPath);
}
            
public void addPath2(ABPath src, ABMatrix matrix) {
   mPath.addPath(src.mPath, matrix.mMatrix);
}
Here is for example part of the ABPath wrapper:

B4X:
@ShortName("ABPath")
public static class ABPath {
      protected Path mPath;
      protected Direction direction=Path.Direction.CW;
      private int mDirection=1;
      
      public static final int Direction_CCW=0;
      public static final int Direction_CW=1;
      
      public static final int FillType_EVEN_ODD=0;
      public static final int FillType_INVERSE_EVEN_ODD=1;
      public static final int FillType_INVERSE_WINDING=2;
      public static final int FillType_WINDING=3;
      
      public void Initialize() {
         mPath=new Path();
      }
      
      public void Initialize2(ABPath path) {
         mPath = path.mPath;
      }
      
      public void SetDirection(int value) {
         mDirection = value;
         if (value==Direction_CCW) {
            direction = Path.Direction.CCW;            
         } else {
            direction = Path.Direction.CW;
         }
      }
      
      protected void Set(Path path) {
         mPath = path;
      }
      
      public int GetDirection() {
         return mDirection;         
      }
            
      public void addArc(ABRectF oval, float startAngle, float sweepAngle) {
         mPath.addArc(oval.mRectF, startAngle, sweepAngle);
      }
      
      public void addCircle(float x, float y, float radius, int dir) {
         if (dir==Direction_CCW) {
            mPath.addCircle(x, y, radius, Path.Direction.CCW);
         } else {
            mPath.addCircle(x, y, radius, Path.Direction.CW);
         }
      }
      
      public void addOval(ABRectF oval, int dir) {
         if (dir==Direction_CCW) {
            mPath.addOval(oval.mRectF, Path.Direction.CCW);
         } else {
            mPath.addOval(oval.mRectF, Path.Direction.CW);
         }
      }
      
      public void addPath(ABPath src) {
         mPath.addPath(src.mPath);
      }
            
      public void addPath2(ABPath src, ABMatrix matrix) {
         mPath.addPath(src.mPath, matrix.mMatrix);
      }
      
      ...
}
Cheers,

Alwaysbusy
 

lachbus

New Member
Licensed User
Thanks man this is great stuff!
I went into your blog and got stuck for a good hour! You do some seriously fun and great looking things.

Greetings from Chicago, Stephan
 

bluedude

Well-Known Member
Licensed User
Pretty cool but sample project contains not enough comments

Hi,

I want to adapt the gauge sample but have no clue what to change. Would be nice to include comments for:

- how to change the size of the gauge;
- how to change the values on the scale;
- how to change the needle.

Cheers and thanks in advance.
 

NeoTechni

Well-Known Member
Licensed User
I'm trying to use this but the documentation is nigh nonexistent

I've taken
B4X:
Sub DrawScreen
   ExDraw.save2(BG, ExDraw.MATRIX_SAVE_FLAG)
   
   Dim dest As Rect 
   dest.Initialize(0,0,200,200)
   BG.DrawBitmap(tempbmp, Null, dest)
   drawFace(BG,tempbmp,  Activity.Width/2, Activity.Height/2,100,100,  1,90)

   ExDraw.restore(BG)
   Activity.Invalidate 
End Sub


Sub drawFace(myBG As Canvas, BMP As Bitmap, Left As Int, Top As Int, CenterX As Float, CenterY As Float, scale As Float, RotationX As Float) 
   ' save the camera state
    mCamera.save

    ' translate AND Then rotate the camera
    mCamera.translate(0, 0, CenterY)
    mCamera.rotateX(RotationX)
   mCamera.translate(0, 0, -CenterY)

    
    ' get the matrix from the camera AND Then restore the camera
    mCamera.getMatrix(mMatrix)
    mCamera.restore()

    ' translate AND scale the matrix
    mMatrix.preTranslate(-CenterX, -CenterY)
    mMatrix.postScale(scale, scale)
    mMatrix.postTranslate(Left + CenterX, Top + CenterY)

    ' set the light
   Dim cosRotation As Double
   cosRotation = Cos(PI * RotationX / 180)
    Dim intensity As Int
   intensity = AMBIENT_LIGHT + (DIFFUSE_LIGHT * cosRotation)
    Dim highlightIntensity As Int
   highlightIntensity = (SPECULAR_LIGHT * Power(cosRotation,SHININESS))   
    If (intensity > MAX_INTENSITY) Then
        intensity = MAX_INTENSITY
    End If
    If (highlightIntensity > MAX_INTENSITY) Then
        highlightIntensity = MAX_INTENSITY
    End If
    Dim light As Int
   light = Colors.rgb(intensity, intensity, intensity)
    Dim highlight As Int
   highlight = Colors.rgb(highlightIntensity, highlightIntensity, highlightIntensity)
    mPaint.SetLightingColorFilter(light, highlight)   
    
    ' draw the Bitmap
    ExDraw.drawBitmap4(myBG, BMP,  mMatrix, mPaint)
End Sub
How do I use it to draw a trapezoid?
When I try, only the test square draws

Is there a different way where I can just feed in 4 X/Y coordinates?
That way I can keep one size a constant length of my choosing?
 

scrat

Active Member
Licensed User
Thank you for this very useful lib !

I have a small problem with ABPaint initialize3 function. (null pointer)

B4X:
'Activity module
Sub Process_Globals
   'These global variables will be declared once when the application starts.
   'These variables can be accessed from all modules.
Dim ExDraw As ABExtDrawing
Dim OriPaint As ABPaint
Dim CopyPaint As ABPaint
End Sub

Sub Globals
   'These global variables will be redeclared each time the activity is created.
   'These variables can only be accessed from this module.

End Sub

Sub Activity_Create(FirstTime As Boolean)

If FirstTime=True Then
'contexte original
OriPaint.Initialize
OriPaint.SetFlags(OriPaint.flag_ANTI_ALIAS_FLAG)
OriPaint.SetStyle(OriPaint.Style_STROKE)
OriPaint.SetStrokeWidth(2.5)

'copy du contexte
CopyPaint.Initialize3(OriPaint)'null pointer

End If



End Sub

Sub Activity_Resume

End Sub

Sub Activity_Pause (UserClosed As Boolean)

End Sub

and a request :
is it possible to have a property "isinitialized" for ABpath, ABpaint, ABregion, etc ?

Another request ::sign0013:
It's possible to add quickReject(Region rgn)
Very usefull to test with non-rectanglar region

Thanks again
 
Last edited:
Top