/*
// wrapper for {Picture} control, {Bitmap}, {Canvas}, {Font}

CLASS {AL4_drawing|_Java_Android},
NATIVE, UNTESTED, BITWISE, BYTES :

IMPORT: {Control}, {Canvas}, {Bitmap}, {Font}, {Geometry}, {Mathematics}, 
		{Long_int} .
IMPORT: {Icon}, {Stream}, {NATIVE_Stream_on_C_Stream}, {Bitmap_direct} .
IMPORT: {String_functions}, {AL4_form}, {Paintbox}, {File_stream}, {File_path} .
IMPORT, DEBUG: {Log_html} .
      
NATIVE: @"import android.graphics.*;" .

NATIVE: "///copy NATIVE_Stream_on_C_Stream.java" .

*/

import java.util.ArrayList;
import java.io.*;
import java.lang.Math.*;
import android.widget.*;
import android.view.*;
import android.graphics.*;

import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.TypedArray;
import android.preference.ListPreference;
import android.preference.PreferenceManager;

import java.util.Timer;
import java.util.TimerTask;

import android.view.inputmethod.*;
import android.text.*;
import android.os.*;

import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.nio.ByteOrder;

import java.util.concurrent.*;

//?{Gif}
//-- import here what additionally necessary to decode gif into frames
//.

public class AL4_drawing
{

	public static class FontEx
	{
		public Typeface Face;
		public String Facename;
		public Object Tag;
        public boolean Bold;
        public boolean Italic;
        public float Size;
        
        //----------- for debug:
		String face_size = "";
	}
    
    public static void fill_r_g_b_standard_palette16(
           byte[] r, byte[] g, byte[] b) {
        int[] colors = new int[16];
        colors[0] = 0x000000;
        colors[1] = 0x000080;
        colors[2] = 0x008000;
        colors[3] = 0x008080;
        colors[4] = 0x800000;
        colors[5] = 0x800080;
        colors[6] = 0x808000;
        colors[7] = 0x808080;
        colors[8] = 0xC0C0C0;
        colors[9] = 0x0000FF;
        colors[10]= 0x00FF00;
        colors[11]= 0x00FFFF;
        colors[12]= 0xFF0000;
        colors[13]= 0xFF00FF;
        colors[14]= 0xFFFF00;
        colors[15]= 0xFFFFFF;
        for (int i = 0; i < 16; i++) {
            r[i] = (byte)(colors[i] & 0xff);
            g[i] = (byte)((colors[i] >> 8) & 0xff);
            b[i] = (byte)((colors[i] >> 16) & 0xff);
        }
    }
    
    public static Bitmap create_bitmap_by_depth(
        int W, int H, int bits_per_pixel) {
            
        // to economy memory, default format is set to 16 bit per pixels:
        Bitmap.Config cfg = Bitmap.Config.RGB_565; 
        Bitmap bmp = null;
        switch (bits_per_pixel) {
            case 1: cfg = Bitmap.Config.ALPHA_8; break;
            case 4: cfg = Bitmap.Config.ALPHA_8; break;
            case 8: cfg = Bitmap.Config.ALPHA_8; break;
            case 15: break;
            case 16: break;
            case 24: cfg = Bitmap.Config.ARGB_8888; break;
            case 32: cfg = Bitmap.Config.ARGB_8888; break;
        }
        if (bmp == null)
            bmp = Bitmap.createBitmap(W, H, cfg);
        return bmp;
    }

	public static class BitmapEx {
		public Bitmap Bmp;
        public Ref_obj Tag;
        public int Bits_per_pixel;
        public Matrix Transform = new Matrix();
        //public float Translate_x = 0;
        //public float Translate_y = 0;
        //public float Scale_x = 1;
        //public float Scale_y = 1;
        public Bitmap Ico;

		public BitmapEx(int bits_per_pixel) {
            Bmp = create_bitmap_by_depth(1, 1, bits_per_pixel);
            Bits_per_pixel = bits_per_pixel;
		}

		void dispose()	{
			//Bmp.dispose();
            Bmp = null;
		}
        
        public ByteBuffer Pixel_data;
        public int locked_Y;
        public int locked_H;
        public int bits_per_pixel;
        
        //-------------------- 'animated GIF support'
//?{Gif}
		//-- uncomment/edit to store gif frames decoded
		//--public ArrayList<GifFrame> frames;
		//--public int frameIndex;
//.
	}
	
//?{Gif}
    //public static class GifFrame {
    //    public int delay;
    //    public final Bitmap /*or some other graphics type*/ image;
    //    public String disposal;
		
    //    public GifFrame(Bitmap image, int delay, String disposal) {
    //        this.image = image;
    //        this.delay = delay;
    //        this.disposal = disposal;
    //    }
    //}
//.
        
    /*
    
    FUNCTION Get_system_color(
    {Canvas}.{system_color} C|olor_index) ==> INT 
    	:
		RESULT = do_get_system_color(C) 
		CASE C ?
		['ACTIVE_CAPTION']: {system_color} a|lter_color_index = 'ACTIVE_TEXT'
		['ACTIVE_GRADIENT']: a = 'ACTIVE_TEXT'
		['ACTIVE_BORDER']: ==>
		['ACTIVE_TEXT']: a = 'ACTIVE_CAPTION'
		['BUTTON']: a = 'BUTTON_TEXT'
		['BUTTON_TEXT']: a = 'BUTTON'
		['BUTTON_HIGHLIGHT']: a = 'BUTTON_TEXT'
		['BUTTON_SHADOW']: ==>
		['DESKTOP']: a = 'GRAY_TEXT'
		['GRAY_TEXT']: a = 'DESKTOP'
		[{system_color}.'HELP']: a = 'HELP_TEXT'
		['HELP_TEXT']: a = {system_color}.'HELP'
		['INACTIVE_CAPTION', 'INACTIVE_GRADIENT']: a = 'INACTIVE_TEXT'
		['INACTIVE_BORDER']: ==>
		['INACTIVE_TEXT']: a = 'INACTIVE_CAPTION'
		[{system_color}.'MENU', 'MENU_HIGHLIGHT']: a = 'MENU_TEXT'
		['MENU_TEXT']: a = {system_color}.'MENU'
		['SCROLLBAR']: ==>
		['WINDOW']: a = 'WINDOW_TEXT'
		['WINDOW_TEXT']: a = 'WINDOW'
		['WINDOW_FRAME']: ==>
		['HIGHLIGHT']: a = 'WINDOW' 
		['HIGHLIGHT_TEXT']: a = 'HIGHLIGHT' 
		;
		INT n|ot_result = do_get_system_color(a)
		CASE n & 0xFFFFFF == RESULT & 0xFFFFFF ?
		     CASE C.Name < a.Name ? RESULT = 0xFFFFFFFF
		     ELSE RESULT = 0xFF000000 ;
		; .
    FUN do_get_system_color({system_color} C|olor_index) ==> INT, NATIVE : @
    "	RESULT = AL4_drawing.Do_Get_system_color(X_C);" 
    .
    */
    public static int Do_Get_system_color(byte X_C) {
		int i = android.R.attr.colorBackground;
		switch (X_C)
		{
		// 'ACTIVE_CAPTION'
		case 0: i = android.R.attr.titleTextColor; break;
		//'ACTIVE_GRADIENT', 		//1
		case 1: i = android.R.attr.colorActivatedHighlight; break;
    	//'ACTIVE_BORDER',		//2
		case 2: i = android.R.attr.colorActivatedHighlight; break;
    	//'ACTIVE_TEXT',			//3
		case 3: i = android.R.attr.textColorHighlight; break;
    	//'BUTTON|_FACE',			//4
		case 4: i = android.R.attr.colorControlNormal; break;
    	//'BUTTON_TEXT',			//5
		case 5: i = android.R.attr.textColor; break;
    	//'BUTTON_HIGHLIGHT',		//6
		case 6: i = android.R.attr.colorControlHighlight; break;
    	//'BUTTON_SHADOW',		//7
		case 7: i = android.R.attr.shadowColor; break;
    	//'DESKTOP|_BACKGROUND',	//8
		case 8: i = android.R.attr.colorBackground; break;
    	//'GRAY_TEXT',			//9
		case 9: i = android.R.attr.colorForeground; break;
    	//'HELP|_BACKGROUND',		//10
		case 10: i = android.R.attr.textColorHintInverse; break;
    	//'HELP_TEXT',			//11
		case 11: i = android.R.attr.textColorHint; break;
	    //'INACTIVE_CAPTION',		//12
		case 12: i = android.R.attr.startColor; break;
	    //'INACTIVE_GRADIENT',	//13
		case 13: i = android.R.attr.endColor; break;
	    //'INACTIVE_BORDER',		//14
		case 14: i = android.R.attr.strokeColor; break;
	    //'INACTIVE_TEXT',		//15
		case 15: i = android.R.attr.textColorPrimaryNoDisable; break;
	    //'MENU|_BACKGROUND',		//16
		case 16: i = android.R.attr.textColorLinkInverse; break;
	    //'MENU_TEXT',			//17
		case 17: i = android.R.attr.textColorLink; break;
	    //'MENU_HIGHLIGHT',		//18
		case 18: i = android.R.attr.statusBarColor; break;
	    //'SCROLLBAR',			//19
		case 19: i = android.R.attr.colorBackground; break;
	    //'WINDOW|_COLOR',		//20
		case 20: i = android.R.attr.colorForeground; break;
	    //'WINDOW_TEXT',			//21
		case 21: i = android.R.attr.textColor; break;
	    //'WINDOW_FRAME'			//22
		case 22: i = android.R.attr.outlineAmbientShadowColor; break;
	    //'HIGHLIGHT'			//23
		case 23: i = android.R.attr.textColorHighlight; break;
	    //'HIGHLIGHTTEXT'			//24
		case 24: i = android.R.attr.colorForeground; break;
		}
		
		android.content.Context context = MainActivity.main_activity; 
		android.content.res.TypedArray array = 
			context.getTheme().obtainStyledAttributes(new int[] {  
		    i }); 
		i = array.getColor(0, 0x000000); 
		array.recycle();
		
		int RESULT = (int)((i & 0xFF) << 16 | (i & 0xFF0000) >> 16 | 
			(i & 0xFF00) | 0xFF000000);
		return RESULT;
    }
    
    /*
    
    FUN Get_rgba_order ==> {Canvas}.{rgba_order} := 'R_G_B_A' . 
    */
    
    //--------------------------------------------- P A I N T   B O X

	/*
	// Create paint box control, return descriptor. 
	//-------------------------------------------
	
	FUN Create_paint_box(
        {Control} P|aintbox_control,
        STR Op|tions|_Vertical_scroll|_Horizontal_scroll|_Can_focus) ==> INT,
        NATIVE :
        INT form|_handle = P.Form_.Handle
        BOOL v|ertical_scroll = "V" IN Op
        BOOL h|orizontal_scroll = "H" IN Op
        BOOL can_focus = "F" IN Op
        NATIVE @
		"RESULT = AL4_drawing.Do_Create_paintbox(X_form, X_P,"
		"	X_v, X_h, X_can_focus);" .
		
    FUN Create_scene_2D(
        {Control} P|icture_control,
        STR Op | tions|_V_H_F) ==> INT,
        NATIVE :
        INT form|_handle = P.Form_.Handle
        BOOL v|ertical_scroll = "V" IN Op
        BOOL h|orizontal_scroll = "H" IN Op
        BOOL can_focus = "F" IN Op
        NATIVE @
		"RESULT = AL4_drawing.Do_Create_paintbox(X_form, X_P,"
		"	X_v, X_h, X_can_focus);" .

	*/
	public static int Do_Create_paintbox(int H, Ref_obj X_P,
        boolean X_V, boolean X_H, boolean X_Can_focus) {
        \\return AL4_form.get_property_int(new Callable<Integer>() {
        \\    @Override public Integer call() {
        
                try {
                    int Result = 0;
                    AL4_form.AL4Column C = AL4_form.Get_parent_for_new_control(H);
                    Scrolled_paintbox SPB = new Scrolled_paintbox(
                            C.Form_, X_P, X_V, X_H, X_Can_focus);
                    Result = AL4_form.add_control_multi_line(H, SPB, X_P);
                    AL4_form.Set_control_events(X_P, SPB, true, X_Can_focus);
                    return Result;
                }
                catch (Exception ex) { AL4_form.report(ex); }
                return 0;
                
        \\    }
        \\});
	}

    
    /*
    
	FUN Update_paintbox(INT H|andle_paint_box) : .
        
    FUN Get_invalid_rect(
    INT H|andle_paint_box, REAL A|rray_coords|dyn[]): .    
    
    FUN Invalidate_paintbox(INT H|andle_paint_box,
        {Geometry}.{rect} R|ectangle_invalidate):
		do_Invalidate(H, R.Loc.X, R.Loc.Y, R.Width, R.Height) .
    FUN do_Invalidate(INT PB|ox_handle, REAL X|_rectangle,
    	REAL Y|_rectangle, REAL W|idth_rectangle, REAL H|eight_rectangle),
    	NATIVE :
    NATIVE @
        "      AL4_drawing.Do_Invalidate_paintbox(X_PB);" .
    */
    
    public static void Do_Invalidate_paintbox(int C)
    {
        AL4_form.run_on_ui_thread(true, new Callable<Void>(){
            @Override public Void call() {
                try {
        //_____________
			        Scrolled_paintbox P = (Scrolled_paintbox)
			        	AL4_form.All_descr.get(C).Jvc;
			        if (P.drawing) return 
			        	null
			        	;
			        P.requestLayout();
			        P.invalidate();
        //^^^^^^^^^^^^^
	           }
	           catch (Exception ex) { AL4_form.report(ex); }
	           return null;
	    	  }
		});
    }
    
    /*
    
    FUN Set_paintbox_content_area(
    INT P|aint_box_handle,
    REAL W|idth_area_points, REAL H|eight_area_points,
    REAL Row_w|idth, REAL Line_h|eight), NATIVE :
        "      AL4_drawing.Do_set_content_area_size(X_P, X_W, X_H,"
        " X_Row_w, X_Line_h);" .
    */
    public static void Do_set_content_area_size(int C, double W, double H,
    	double Row_w, double Line_h)
    {
        \\AL4_form.run_on_ui_thread(true, new Callable<Void>(){
        \\    @Override public Void call() {
        \\        try {
        //____________
                    Scrolled_paintbox P = (Scrolled_paintbox)AL4_form
                    .All_descr.get(C).Jvc;
                    P.Content_height = AL4_form.Points(H);
                    P.Content_width = AL4_form.Points(W);
                    P.row_width = Row_w;
                    P.line_height = Line_h;
			        
                    if (W <= Integer.MAX_VALUE) P.coeff_horz = 1;
                    else P.coeff_horz = W / Integer.MAX_VALUE;
                    if (H <= Integer.MAX_VALUE) P.coeff_vert = 1;
                    else P.coeff_vert = H / Integer.MAX_VALUE;
        //^^^^^^^^^^^^^
        \\        }
        \\        catch (Exception ex) { AL4_form.report(ex); }
        \\        return null;
        \\    }
        \\});
    }
    
    /*
    
	FUN Paintbox_client_width(
	INT P|aint_box_handle) ==> REAL, NATIVE : @
	"	RESULT = AL4_drawing.do_Paintbox_client_width(X_P);" .
	*/
	public static double do_Paintbox_client_width(int X_P) {
        \\return AL4_form.get_property_double(new Callable<Double>(){
        \\    @Override public Double call() {
        \\        try {

                    AL4_drawing.Scrolled_paintbox P =
                        (AL4_drawing.Scrolled_paintbox)AL4_form.All_descr.get(X_P).Jvc;
                    return AL4_form.Pixels(P.getWidth());
                    
        \\        }
        \\        catch (Exception ex) { AL4_form.report(ex); }
        \\        return 0.0;
        \\    }
        \\});
	}
	/*
		
	FUN Paintbox_client_height(INT P|aint_box_handle) ==> REAL, NATIVE : @
	"	RESULT = AL4_drawing.do_Paintbox_client_height(X_P);" .
	*/
	public static double do_Paintbox_client_height(int X_P) {
        \\return AL4_form.get_property_double(new Callable<Double>(){
        \\    @Override public Double call() {
        \\        try {
        
                    AL4_drawing.Scrolled_paintbox P =
                        (AL4_drawing.Scrolled_paintbox)AL4_form.All_descr.get(X_P).Jvc;
                    return AL4_form.Pixels(P.getHeight());
                    
        \\        }
        \\        catch (Exception ex) { AL4_form.report(ex); }
        \\        return 0.0;
        \\    }
        \\});
	}
	/*

    FUN Set_paint_lines_line_height|_DUMMY(
    INT P|aint_box_handle,
        REAL H|eight_area_points) : .
        
    FUN Get_hscroll_height|_DUMMY(
    INT H|andle_paint_box) ==> REAL: .

    FUN Get_vscroll_width|_DUMMY(
    INT H|andle_paint_box) ==> REAL: . 
        
    FUN Get_scroll_position(
        INT H|andle_paint_box,
        STR What|_box_VH) ==> REAL RESULT|_points, NATIVE
        :
        BOOL v|ertical = "v" IN What.Lower
        BOOL h|orizontal = "h" IN What.Lower
        NATIVE @ " RESULT = AL4_drawing.do_Get_scroll_position(X_H, X_v, X_h);"
        .
    */
    public static double do_Get_scroll_position(int X_H, boolean X_v, boolean X_h) {
        \\return AL4_form.get_property_double(new Callable<Double>(){
        \\    @Override public Double call() {
                  try {
                       AL4_drawing.Scrolled_paintbox P
                          = (AL4_drawing.Scrolled_paintbox)
                              AL4_form.All_descr.get(X_H).Jvc;
                       if (X_v)
                          return P.V_position;
                       else if (X_h)
                          return P.H_position;
                  }
                  catch (Exception ex) { AL4_form.report(ex); }
                  return 0.0;
        \\    }
        \\});
    }
    /*
        
        
    FUN Set_scroll_position(
        INT H|andle_paint_box,
        STR What|_box_VH, REAL Value|_points), NATIVE
        :
        BOOL v|ertical = "v" IN What.Lower
        BOOL h|orizontal = "h" IN What.Lower
        NATIVE @ " AL4_drawing.do_Set_scroll_position(X_H, X_v, X_h, X_Value);"
        .
    */
    public static void do_Set_scroll_position(int X_H, boolean X_v, boolean X_h,
    double X_Value) {
        \\AL4_form.run_on_ui_thread(true, new Callable<Void>(){
        \\    @Override public Void call() {
        \\        try {

                     AL4_drawing.Scrolled_paintbox P = (AL4_drawing.Scrolled_paintbox)
                                            AL4_form.All_descr.get(X_H).Jvc;
                     if (X_v)
                        P.V_position = X_Value;
                     else if (X_h)
                        P.H_position = X_Value;

        \\        }
        \\        catch (Exception ex) { AL4_form.report(ex); }
        \\        return null;
        \\    }
        \\});
    }
    /*
        
    
    FUNCTION Set_paint_lines_cursor_shape(
        INT H|andle_paint_lines,
        {Paintbox}.{cursor} C|ursor_shape )
        :
        Set_control_cursor_shape(H, C) .    
    
    FUNCTION Set_control_cursor_shape(
        INT H|andle_control,
        {cursor} C|ursor_shape ), UNCOOKED : .

	*/
    public static class Scrolled_paintbox extends View {
        public AL4_form form;
//? {Paintbox}
        public C_Paintbox Paintbox_control;
//.
        
        int Content_height; // in pixels
        int Content_width;  // in pixels
        double row_width;
        double line_height;
        public double coeff_vert;
        public double coeff_horz;      
            
		public double H_position;
		public double V_position;
        public Timer waiter;
        public boolean Can_focus;
        String previous_in = "";
        
        private int mBatch;
        private Editable mEditable;
        private final XTextWatcher mTextWatcher;
        //private final XInputFilter mInputFilter;

        public Scrolled_paintbox(AL4_form F, Ref_obj X_Paintbox,
                boolean V, boolean H, boolean X_Can_focus) {
        	super(AL4_form.context_activity);
//? {Paintbox}
			Content_height = AL4_form.Points(100);
			Content_width = AL4_form.Points(100);
		//AL4_system.WriteLine("SET DEFAULT Content_height=" + Content_height +
		//	" Content_width=" + Content_width);

            form = F;
            
            Can_focus = X_Can_focus;
            mTextWatcher = new XTextWatcher();
            
            if (Can_focus) {
            	mTextWatcher.SPB = this;
            	mEditable = new SpannableStringBuilder();
            	Selection.setSelection(mEditable, 0);
            	mEditable.setSpan(mTextWatcher, 0, mEditable.length(),
            		Spanned.SPAN_INCLUSIVE_INCLUSIVE);
            }
            
            Paintbox_control = (C_Paintbox)X_Paintbox.Obj;
            
            coeff_horz = 1;
            coeff_vert = 1;
            
            if (Can_focus) {
		//AL4_system.WriteLine("!!! set scrolled paintbox focusable");
                setFocusable(true);
                setFocusableInTouchMode(true);
                setClickable(true);
            }
//.
        }
        
	    /*
	    
		FUN Suspend_paintbox({Paintbox} P|aintbox_control): .
			    
		FUN Resume_paintbox({Paintbox} P|aintbox_control): .	    
	    
	    FUN Get_control_bitmap_bpp ==> INT := 0 .

	    
	    FUNCTION Call_form_scrolled(
	    INT P|aintbox_control_handle), NATIVE : @
	    "AL4_drawing.Scrolled_paintbox SPB = (AL4_drawing.Scrolled_paintbox)"
	    "						AL4_form.All_descr.get(X_P).Jvc;"
	    "SPB.call_form_scrolled();" .
	    
		FUN Adjust_paintbox_components(
		{Paintbox} P|aintbox_object): .
		
		FUN Paint_lines_activated(INT H|andle_control): .
	    */
	    
	    public void call_form_scrolled() {
//?{Form}.scrolled
			try {
				Ref_obj CR = AL4_form.All_descr.get(Paintbox_control.F_Handle).Ref;
				C_Form F = (C_Form)Paintbox_control.F_Form_.Obj;
		        if (AL4_form.operation_started(F, 25, CR)) {
		        //------------------------------------------
                    \\AL4_form.run_on_background_thread(new Callable<Void>(){
                    \\    @Override public Void call() {
                    
                            try {
                                try { F.M_scrolled(); }
                                finally { AL4_form.operation_ended(F, 25); }
                            }
                            catch(Exception ex) { AL4_form.report(ex); }
                            
                    \\        return null;
                    \\    }
                    \\});
		        //------------------------------------------
		        }
			}
			catch (Exception ex) { AL4_form.report(ex); }
//.
	    }
        
        boolean measuring = false;
        
        @Override
        public void onMeasure(int wSpec, int hSpec) {
        	if (measuring) {
        		super.onMeasure(wSpec, hSpec); return;
        	} 
        	measuring = true;
        	try {
	            int wIn = MeasureSpec.getSize(wSpec);
	            int hIn = MeasureSpec.getSize(hSpec);
	            int wSet = AL4_form.Points(Paintbox_control.F_last_set_w);
	            int hSet = AL4_form.Points(Paintbox_control.F_last_set_h);
	            int w = wSet;
	            int h = hSet;
	            AL4_form.AL4Column col = AL4_form.Get_parent_column(this);
	            int col_w = 0;
            	if (col != null) {
            		col_w = col.Width_pixels;
            		if (col_w == 0) col_w = col.getWidth();
            	}
	            if (wSet <= -2 || wSet == 0 && col != null && col.Layout == 0) {
	            	if (col != null) {
	            		w = col_w;
            //AL4_system.WriteLine("parent column [" + col.Alias + "]:" +
            //" w=" + w + " Width_points=" + col.Width_points +
            //" Width_pixels=" + col.Width_pixels +
            //" col_w=" + col_w);
	            		wIn = w;
	            	}
	            }
	            else {
        	//AL4_system.WriteLine("use wSet=" + wSet + " <- " + Paintbox_control.F_last_set_w);
	            }
	            
	            if (hSet <= -2 ||
	            	Paintbox_control.F_Anchor_bottom && col != null &&
	            	col.getChildCount() == 1) {
	            	if (col != null) {
	            		h = col.getHeight();
	            		if (h < AL4_form.Points(col.Height_points))
	            			h = AL4_form.Points(col.Height_points);
	            		if (col.Height_pixels != 0) h = col.Height_pixels;
	            		hIn = h;
	            	}
	            }
	        	switch(MeasureSpec.getMode(wSpec)) {
	        	case MeasureSpec.EXACTLY:
					w = Math.max(wIn, w);
					if (col.Layout < 3)
						w = Math.min(w, col_w);
					break;
	        	case MeasureSpec.AT_MOST: w = Math.min(wIn, w); break;
	        	default: w = w; break;
	        	}
	        	switch(MeasureSpec.getMode(hSpec)) {
	        	case MeasureSpec.EXACTLY: h = Math.max(hIn, h); break;
	        	case MeasureSpec.AT_MOST: h = Math.min(hIn, h); break;
	        	default: h = h; break;
	        	}
	            if (w <= 0) w = wIn;
	            if (w <= 0) w = wSet; 
	            if (w <= 0) w = AL4_form.Points(100); 
	            if (h <= 0) h = hIn;
	            if (h <= 0) h = hSet; 
	            if (h <= 0) h = AL4_form.Points(100); 
        //AL4_system.WriteLine(Paintbox_control.F_Alias +
        //".onMeasure(" + AL4_form.AL4Column.spec(wSpec) + "," +
        //AL4_form.AL4Column.spec(hSpec) + ")" +
        //" / set[" + wSet + "," + hSet + "]" + " ==> (" + w + " x " + h + ")");
            	setMeasuredDimension(w, h);
        	}
        	finally {
        		measuring = false;
        	}
        }
        
        public boolean drawing;
        
        @Override
        public void onDraw(Canvas g)
        {
//? {Paintbox}
//try {
//C_Log_html.M_Log_colored2(0x80, 0, "PAINT: w=" + getWidth() + " h=" + getHeight());
//} catch (Exception exx) { AL4_form.report(exx); }
			drawing = true;
                    try {
                        try {
                            C_Form F = (C_Form)AL4_form.All_descr.get(form.Handle).Ref.Obj;
			                
                            if (AL4_form.operation_started(
                                F, 21, Paintbox_control.hard_link)) {
                            //---------------------------------------
                                try {
                                    try {
                                        Paintbox_control.M_before_paint();
                                    } catch(Exception ex) { AL4_form.report(ex); }
                                }
                                finally {
                                    AL4_form.operation_ended(F, 21);
                                }
                            //---------------------------------------
                            }
                            C_Bitmap CBmp = (C_Bitmap)Paintbox_control.F_buffer.Obj;
                            BitmapEx BmpEx = (BitmapEx)AL4_form.All_descr.get(
                            		CBmp.F_Handle).Jvc;

        //AL4_system.WriteLine("PAINT: paint2");
        					if (Paintbox_control.F_need_repaint)
        					{
	                            if(AL4_form.operation_started(
	                                F, 22, Paintbox_control.hard_link)) {
	                            //---------------------------------------
	                                try {
	                                    try {
	                                    F.M_paint();
	                                    } 
	                                    catch(Exception Ex) 
	                                    { AL4_form.report(Ex); }
	                                }
	                                finally {
	                                    AL4_form.operation_ended(F, 22);
	                                }
                            //---------------------------------------
                            }
        					}
		
        //AL4_system.WriteLine("PAINT: paint3");
                            if (AL4_form.operation_started(
                                F, 23, Paintbox_control.hard_link)) {
                            //---------------------------------------
                                try {
                                    try {
                                    Paintbox_control.M_after_paint();
                                    } catch(AL4StopException StopEx) {}
                                }
                                finally {
                                    AL4_form.operation_ended(F, 23);
                                }
                            //---------------------------------------
                            }
		
        //AL4_system.WriteLine("PAINT: paint4");
                            //F.F_sender = AL4_object.Object_assign_hard(F.F_sender,
                            //            C_Control.NoneObj_C_Control);
			                
                            if (BmpEx != null && BmpEx.Bmp != null) {
                                if (Caret != null && Caret.Flash_state % 2 == 0) {
                                    int w_caret = Caret.Area.right - Caret.Area.left;
                                    int h_caret = Caret.Area.bottom - Caret.Area.top;
                                    if (Caret.Area.left + w_caret > BmpEx.Bmp.getWidth()) {
                                        w_caret = BmpEx.Bmp.getWidth() - Caret.Area.left;
                                    }
                                    if (Caret.Area.top + h_caret > BmpEx.Bmp.getHeight()) {
                                        h_caret = BmpEx.Bmp.getHeight() - Caret.Area.top;
                                    }
        //AL4_system.WriteLine("flash w_caret=" + w_caret + " h_caret=" + h_caret +
        //" Caret.Area.left=" + Caret.Area.left + " Caret.Area.top=" + Caret.Area.top);
                                    if (w_caret > 0 && w_caret * h_caret > 0) {
                                        int[] data = new int[w_caret * h_caret];
                                        int stride = w_caret;
                                        // BmpEx.Bmp.getWidth(); error in documentation
                                        BmpEx.Bmp.getPixels(data, 0, stride, Caret.Area.left, Caret.Area.top,
                                            w_caret, h_caret);
                                        for (int i = 0; i < data.length; i++)
                                            data[i] = data[i] ^ 0xFFFFFF;
                                        BmpEx.Bmp.setPixels(data, 0, stride, Caret.Area.left, Caret.Area.top,
                                            w_caret, h_caret);
                                    }
                                }
                                g.drawBitmap(BmpEx.Bmp, 0, 0, null);
                            }
        //AL4_system.WriteLine("PAINT: paint5");
                    //do_save_bitmap(BmpEx.Bmp,
                    //C_System_functions.M_Get_working_dir() +
                    //"draw.png", "PNG", 100);
                    //AL4_system.WriteLine("DRAWN BITMAP: " + BmpEx.Bmp.getWidth() + "x" +
                    //BmpEx.Bmp.getHeight() + ", size=" + getWidth() + "x" + getHeight());
                        }
                        catch (Exception e) { AL4_form.report(e); }
        //AL4_system.WriteLine("PAINT OK");
        //try { //////////////////////////////////////////////////////////////////////////
        //C_Log_html.M_Log_colored2(0xFFFFFF, 0, /////////////////////////////////////////
        //"PAINT OK\n"); ////////////////////////
        //} catch(Exception exx) {} //////////////////////////////////////////////////////
                    }
                    finally {
                        drawing = false;
                    }
//.
        }
        
        @Override
        public boolean onKeyDown(int keyCode, KeyEvent ev) {
        
        //try {
//C_Log_html.M_Log_colored(0xFF000000, "*key pressed: " + keyCode + " *[" + ev.getUnicodeChar() + "]\n");
        //} catch(Exception ex) {
        //    Toast.makeText(AL4_form.context_activity, "exception*key pressed",
        //    Toast.LENGTH_LONG).show();
        //}
        
            if (keyCode == 4 || keyCode == 82) {
                return false;
            }
            return false;
        }
        
        @Override
        public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
        	outAttrs.inputType = Paintbox_control.F_options.contains("S")
        		? InputType.TYPE_CLASS_TEXT
				: InputType.TYPE_NULL; 
        	outAttrs.imeOptions = EditorInfo.IME_ACTION_DONE;
        	outAttrs.initialSelEnd = outAttrs.initialSelStart = 0;
        	return new BaseInputConnection(this, true) {
        		@Override
        		public Editable getEditable() {
        			return mEditable;
        		}
        		
        		@Override
        		public boolean endBatchEdit() {
        			mBatch++;
        			return super.endBatchEdit();
        		}
        		
        		@Override
        		public boolean beginBatchEdit() {
        			mBatch--;
        			return super.beginBatchEdit();
        		}
        	};
        }
        
        @Override
        public boolean onCheckIsTextEditor() {
        	return Can_focus;
        }
            


        //----------------------------------------------- C A R E T ------------
        /*
        FUNCTION Provide_window_caret(
            INT S|crolled_paintbox_handle,
            REAL X|_pos_points,
            REAL Y|_pos_points,
            REAL Caret_w|idth_points,
            REAL Caret_h|eight_points), NATIVE
            :
            INT off_x|_in_pixels = Points(X)
            INT off_y|_in_points = Points(Y)
            INT w_caret|_pixels = Points(Caret_w)
            INT h_caret|_pixels = Points(Caret_h)
            NATIVE @
            "AL4_drawing.Scrolled_paintbox.Do_provide_window_caret("
            "   X_S, X_off_x, X_off_y, X_w_caret, X_h_caret);" .
        */
        
        public CaretRectangle Caret;
        
        public static void Do_provide_window_caret(int X_S, int off_x, int off_y, 
                int w_caret, int h_caret)
        {
            \\AL4_form.run_on_ui_thread(true, new Callable<Void>(){
            \\    @Override public Void call() {
            
                    try {
                        int wcaret = w_caret;
                        int hcaret = h_caret;
                        int offx = off_x;
                        int offy = off_y;
                        Scrolled_paintbox SPB = (Scrolled_paintbox)AL4_form.All_descr.get(X_S).Jvc;
                        if (w_caret < 1) wcaret = 1;
                        if (h_caret < 1) hcaret = 1;
                        if (SPB.Caret == null)
                            SPB.Caret = new CaretRectangle(SPB);
                        if (offx < 0) {
                            wcaret += offx;
                            offx = 0;
                        }
                        if (offy < 0) {
                            hcaret += offy;
                            offy = 0;
                        }
                        SPB.Caret.Area = new Rect(
                            offx, offy, offx + wcaret, offy + hcaret);
                        SPB.Caret.Activate();
                    }
                    catch (Exception ex) { AL4_form.report(ex); }
                    
            \\        return null;
            \\    }
            \\});
        }
        
        /*
        FUNCTION Destroy_window_caret|_DUMMY(INT S|crolled_paintbox_handle): .
        */

    }
    
    public static class XTextWatcher implements TextWatcher {
    	Scrolled_paintbox SPB;
    
    	@Override
    	public void beforeTextChanged(CharSequence seq, int i, int i1, int i2) {
            //AL4_system.WriteLine("changing: " + mEditable + seq.toString() +
            //" i="+i + " i1="+i1 + " i2="+i2);
    	}
    	
    	@Override
    	public void onTextChanged(CharSequence seq, int i, int i1, int i2) {
    		String s = seq.toString();
    		String p = SPB.previous_in;
            //AL4_system.WriteLine("Changed: " + mEditable +
            //" p="+p+ ", ");
            //if (s.length() >= p.length()) {
            //AL4_system.WriteLine("[" + s.substring(0, p.length()) + "]" +
            //" ?? " + "[" + p + "]");
            //}
    		String toHandle = s;
    		if (s.length() >= p.length() && (p.equals("") ||
    			s.substring(0, p.length()).equals(p))) {
    			toHandle = s.substring(p.length());
    		}
    		else if (s.length() == p.length()-1 &&
    				 s.equals(p.substring(0, s.length()))) {
    			toHandle = "" + (char)8;
    		}
    		try {
        		for (int j = 0; j < toHandle.length(); j++) {
        			C_Paintbox CC = SPB.Paintbox_control;
        			CC.F_stop_handling = false;
	                C_Form F = (C_Form)CC.F_Form_.Obj;
	                AL4_form form = (AL4_form)AL4_form.All_descr.get(F.F_Handle).Jvc;
        		//AL4_system.WriteLine("handling char[" + toHandle.charAt(j) + "]");
	                F.F_Key = AL4_object.Object_assign_hard(F.F_Key, 
	                	AL4_form.prepare_key(null, toHandle.charAt(j)));
                    if (AL4_form.operation_started(F, 17, CC.hard_link)) {
        			//----------------------------------------------------
                        \\AL4_form.run_on_background_thread(new Callable<Void>(){
                        \\    @Override public Void call() {
                                  try {
                                      try {
                                          if (F.M_key_press() != 0) {
                                          }
                                      } finally { AL4_form.operation_ended(F, 17); }
                                  }
                                  catch(Exception ex) { AL4_form.report(ex); }
                        \\        return null;
                        \\    }
                        \\});
        			//----------------------------------------------------
                    }
        		}
    		}
    		catch (Exception ex) {
    			AL4_system.WriteLine("Exception " + ex.getMessage() + '\n' +
    			AL4_form.stack_trace(ex));
    		}
    		SPB.previous_in = new String(s);
    	//AL4_system.WriteLine("set previous=[" + SPB.previous_in + "]");
    	}
    	
    	@Override
    	public void afterTextChanged(Editable editable) {
    		//AL4_system.WriteLine("CHANGED: " + mEditable);
    	}
    }

    public static class CaretRectangle {
        
        Scrolled_paintbox SPB;
        Timer Timer_flash;
        TimerTask Timer_task;
        Handler mHandler;
        public int Flash_state;
        Rect Area;
        
        public CaretRectangle(Scrolled_paintbox SPB_) {
            SPB = SPB_;
            Area = new Rect(0, 0, 1, 20);
        }
        
        public void Activate() {
            if (AL4_form.Application_terminated) return;
            if (Timer_flash != null) return;
            //%DEBUG%
            //AL4_system.WriteLine("Start flash timer");
            
            Timer_flash = new Timer();
            SPB.form.All_timers.add(Timer_flash);
            
            mHandler = new Handler() {
            	public void handleMessage(Message msg) {
            		Flash_state += 1;	
            		SPB.invalidate();
            	}
            };
            
            Timer_task = new TimerTask() {
                @Override
                public void run() {
                	mHandler.obtainMessage(1).sendToTarget();
                }
            };
            Timer_flash.schedule(Timer_task, 200, 400);
        }
        
        public void Stop_timer() {
            //%DEBUG%
            //AL4_system.WriteLine("STOP FLASH TIMER!");
            
            if (Timer_flash == null) return;
            SPB.form.All_timers.remove(Timer_flash);
            Timer_flash.cancel();
            Timer_flash = null;
            mHandler = null;
        }
        
    }

	//------------------------------------------ B I T M A P

	/*
	// Create BitmapEx object, return descriptor.
	//-------------------------------------------
	FUN Create_bitmap({Bitmap} B|itmap_object,
		INT Bpp|bits_per_pixel) ==> INT, NATIVE :
		INT w|idth_bitmap = B.Width
		INT h|eight_bitmap = B.Height
		NATIVE @
		"    RESULT = AL4_drawing.Do_Create_bitmap(X_B, X_Bpp);"
		"	 AL4_drawing.Do_Set_bitmap_size(RESULT, X_w, X_h);" .
	*/
	public static int Do_Create_bitmap(Ref_obj X_B, int X_Bpp)
	{
		BitmapEx B = new BitmapEx(X_Bpp);
		B.Tag = X_B;
		int Result = AL4_form.Alloc_descriptor(B, X_B);
		return Result;
	}

	/*
	// Set Bitmap new size
	//--------------------
	FUN Set_bitmap_size(INT B|itmap_handle,
		INT W|idth_bitmap, INT H|eight_bitmap), NATIVE :
		"     AL4_drawing.Do_Set_bitmap_size(X_B, X_W, X_H);" .
	*/
	public static void Do_Set_bitmap_size(int X_B, int X_W, int X_H)
	{
		BitmapEx B = (BitmapEx)AL4_form.All_descr.get(X_B).Jvc;
		if (B.Bmp.getWidth() == X_W && B.Bmp.getHeight() == X_H) return;
		Bitmap New = create_bitmap_by_depth(X_W, X_H, B.Bits_per_pixel);
        Canvas g = new Canvas(New);
		try {
			g.drawBitmap(B.Bmp, 0, 0, null);
		}
		catch (Exception e)	{ AL4_form.report(e); return; }
		B.Bmp = New;
	}
    
    /*
    FUN Get_bitmap_depth(INT B|itmap_handle) ==> INT RESULT|bpp_or_0, NATIVE :
    @   "   AL4_drawing.BitmapEx B = (AL4_drawing.BitmapEx)"
        "       AL4_form.All_descr.get(X_B).Jvc;"
        "   RESULT = B.Bits_per_pixel;" .
    */

	/*
	// Load Bitmap from file
	//----------------------
	FUN Load_bitmap_native|_from_file(INT B|itmap_handle,
		STR Path|_image_file), NATIVE :
		"     AL4_drawing.Do_Load_bitmap(X_B, X_Path);" .
	*/
    public static void Do_Load_bitmap(int X_B, String X_Path)
    {
        BitmapEx B = (BitmapEx)AL4_form.All_descr.get(X_B).Jvc;
        java.io.File f = new java.io.File(X_Path);
        if (!f.isFile()) return;

        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inPreferredConfig = Bitmap.Config.ARGB_8888;
        Bitmap New = BitmapFactory.decodeFile(X_Path, options);
        B.Bmp = New;
    }

	/*
	
	//FUN Load_jpeg_native|_from_file(
	//INT B|itmap_handle, STR Path|_image_file) : Load_bitmap_native(B, Path) .
	
	FUN Get_jpeg_from_stream(
	{Stream} Source|_stream, INT B|itmap_handle) ==> BOOL, NATIVE :
		BYTE a|s_memory_bytes[]
		CASE !Source.Load_bytes(a[], Source.Size.To_int) ? ==> ;
		NATIVE @
		"	RESULT = AL4_drawing.Do_get_jpeg_from_stream(X_a, X_B, 1);" .

	FUN Get_jpeg_from_stream_ex(
	{Stream} Source|_stream, INT B|itmap_handle, INT Div|ide_size) ==> BOOL,
	NATIVE :
		BYTE a|s_memory_bytes[]
		CASE !Source.Load_bytes(a[], Source.Size.To_int) ? ==> ;
		NATIVE @
		"	RESULT = AL4_drawing.Do_get_jpeg_from_stream(X_a, X_B, X_Div);" .

	*/	
	public static boolean Do_get_jpeg_from_stream(
	DynAByte X_A, int X_B, int X_Div) throws Exception
   	{
		byte[] a = new byte[X_A.Count()];
		for (int i = 0; i < X_A.Count(); i++)
			a[i] = X_A.Get_item(i);
			
		MemoryFile MF = new MemoryFile(null, X_A.Count());
		MF.getOutputStream().write(a);

        BitmapEx B = (BitmapEx)AL4_form.All_descr.get(X_B).Jvc;
        
        BitmapFactory.Options options = new BitmapFactory.Options();
        switch (X_Div) {
        case 2: options.inSampleSize = 2; break;
        case 4: options.inSampleSize = 3; break;
        case 8: options.inSampleSize = 4; break;
        default: options.inSampleSize = 1; break;
        }
        //options.inPreferredConfig = Bitmap.Config.ARGB_8888;
        Bitmap New = BitmapFactory.decodeStream(
        	MF.getInputStream(), null, options);
        B.Bmp = New;
        if (New.getWidth() > 0 && New.getHeight() > 0) {
        	C_Bitmap cB = (C_Bitmap)B.Tag.Obj; 
        	cB.F_Width = New.getWidth();
        	cB.F_Height = New.getHeight();
        	return true;
        }
        else return false;
    }
	/*
	
	
	//FUN Load_gif_native|_from_file(
	//INT B|itmap_handle, STR Path|_image_file) : Load_bitmap_native(B, Path) .
	
	//FUN Load_png_native|_from_file(
	//INT B|itmap_handle, STR Path|_image_file) : Load_bitmap_native(B, Path) .    

	FUN Get_png_from_stream(
	{Stream} Source|_stream, INT B|itmap_handle) ==> BOOL, NATIVE :
		BYTE a|s_memory_bytes[]
		CASE !Source.Load_bytes(a[], Source.Size.To_int) ? ==> ;
		NATIVE @
	    "     RESULT = AL4_drawing.Do_Get_bitmap_from_stream(X_a, X_B, 1);" .
        
	FUN Get_png_from_stream_ex(
	{Stream} Source|_stream, INT B|itmap_handle, INT Div|ide_size) ==> BOOL,
	NATIVE :
		BYTE a|s_memory_bytes[]
		CASE !Source.Load_bytes(a[], Source.Size.To_int) ? ==> ;
		NATIVE @
	    "     RESULT = AL4_drawing.Do_Get_bitmap_from_stream(X_a, X_B, X_Div);" .
        
	FUN Get_gif_from_stream(
	{Stream} Source|_stream, INT B|itmap_handle) ==> BOOL, NATIVE :
		BYTE a|s_memory_bytes[]
		CASE !Source.Load_bytes(a[], Source.Size.To_int) ? ==> ;
		NATIVE @
	    "     RESULT = AL4_drawing.Do_Get_bitmap_from_stream(X_a, X_B, 1);" .
        
	FUN Get_animated_gif_from_stream(
	{Stream} Source|_stream, INT B|itmap_handle) ==> BOOL, NATIVE :
		BYTE a|s_memory_bytes[]
		CASE !Source.Load_bytes(a[], Source.Size.To_int) ? ==> ;
		NATIVE @
	    //"     RESULT = AL4_drawing.Do_Get_bitmap_from_stream(X_a, X_B, 1);"
	    "RESULT = AL4_drawing.Do_Get_animated_gif_from_stream(X_a, X_B, 1);" .
	    
	*/
	
	public static boolean Do_Get_animated_gif_from_stream(
	DynAByte X_A, int X_B, int X_Div) throws Exception
	{
		byte[] a = new byte[X_A.Count()];
		for (int i = 0; i < X_A.Count(); i++)
			a[i] = X_A.Get_item(i);
		MemoryFile MF = new MemoryFile(null, X_A.Count());
		MF.getOutputStream().write(a);
		
		BitmapEx B = (BitmapEx)AL4_form.All_descr.get(X_B).Jvc;
		boolean ok = false;
//?{Gif}
		//-- place here code to extract gif frames into bitmaps
		//if (B.frames == null)
			ok = Do_Get_bitmap_from_stream(X_A, X_B, X_Div);
//.		
		return ok;
	} 
	
	/*
        
	FUN Get_bitmap_from_stream(
	{Stream} Source|_stream, INT B|itmap_handle) ==> BOOL, NATIVE :
		BYTE a|s_memory_bytes[]
		CASE !Source.Load_bytes(a[], Source.Size.To_int) ? ==> ;
		NATIVE @
		"	RESULT = AL4_drawing.Do_Get_bitmap_from_stream(X_a, X_B, 1);" .
		
  	*/
	public static boolean Do_Get_bitmap_from_stream(
		DynAByte X_A, int X_B, int X_Div) throws Exception
	{
		byte[] a = new byte[X_A.Count()];
		for (int i = 0; i < X_A.Count(); i++)
			a[i] = X_A.Get_item(i);
		MemoryFile MF = new MemoryFile(null, X_A.Count());
		MF.getOutputStream().write(a);
		
        BitmapEx B = (BitmapEx)AL4_form.All_descr.get(X_B).Jvc;

    	BitmapFactory.Options options = new BitmapFactory.Options();
        switch (X_Div) {
        case 2: options.inSampleSize = 2; break;
        case 4: options.inSampleSize = 3; break;
        case 8: options.inSampleSize = 4; break;
        default: options.inSampleSize = 1; break;
        }
    	//options.inPreferredConfig = Bitmap.Config.ARGB_8888;
        Bitmap New = BitmapFactory.decodeStream(
        	MF.getInputStream(), null, options);
        B.Bmp = New;
        if (New.getWidth() > 0 && New.getHeight() > 0) {
        	C_Bitmap cB = (C_Bitmap)B.Tag.Obj; 
        	cB.F_Width = New.getWidth();
        	cB.F_Height = New.getHeight();
        	return true;
        }
        else return false;
	}
  	
  	/*
  	
  	FUN Get_gif_frames_count(
	INT B|itmap_handle) ==> INT, NATIVE :
	//"RESULT = AL4_drawing.Do_Get_gif_frames_count(X_B);" 
	"" .
	
	FUN Set_gif_current_frame(
	INT B|itmap_handle, INT I|ndex_frame_to_set), NATIVE :
	//"AL4_drawing.Do_Set_gif_current_frame(X_B, X_I);"
	"" .
	
	FUN Get_gif_frame_duration(
	INT B|itmap_handle) ==> INT, NATIVE : 
	//"RESULT = AL4_drawing.Do_Get_gif_frame_duration(X_B);" 
	"" .
	
	FUN Get_pcx_from_stream(
	{Stream} Source|_stream, INT B|itmap_handle) ==> BOOL : .
    
	FUN Get_psd_from_stream(
	{Stream} Source|_stream, INT B|itmap_handle) ==> BOOL : .
    
	FUN Get_tga_from_stream(
	{Stream} Source|_stream, INT B|itmap_handle) ==> BOOL : .

	FUN Get_tiff_from_stream(
	{Stream} Source|_stream, INT B|itmap_handle) ==> BOOL : .

	FUN Get_webp_from_stream(
	{Stream} Source|_stream, INT B|itmap_handle) ==> BOOL, NATIVE :
		BYTE a|s_memory_bytes[]
		CASE !Source.Load_bytes(a[], Source.Size.To_int) ? ==> ;
		NATIVE @
	    "     RESULT = AL4_drawing.Do_Get_bitmap_from_stream(X_a, X_B, 1);" .
		
	FUN Save_to_stream_webp(
	INT B|itmap_handle, {Stream} S|tream_dest, 
	REAL Q|uality_option, BOOL Lossless) ==> BOOL, NATIVE : @
    "RESULT = AL4_drawing.Do_Save_to_stream_format(X_B, X_S, (int)X_Q,"
       ''"WEBP");''
    .

	FUN Get_xpm_from_stream(
	{Stream} Source|_stream, INT B|itmap_handle) ==> BOOL : .
	    
	
	*/
	
	public static int Do_Get_gif_frames_count(int X_B)
	{
//?{Gif}
        //BitmapEx B = (BitmapEx)AL4_form.All_descr.get(X_B).Jvc;
        //if (B.frames != null) return B.frames.size();
        //if (B.Bmp != null) return 1;
//.
        return 0;
	}
	
	public static void Do_Set_gif_current_frame(int X_B, int X_I)
	{
//?{Gif}
        //BitmapEx B = (BitmapEx)AL4_form.All_descr.get(X_B).Jvc;
        //B.frameIndex = X_I;
//.
	}
	
	public static int Do_Get_gif_frame_duration(int X_B)
	{
//?{Gif}
        //BitmapEx B = (BitmapEx)AL4_form.All_descr.get(X_B).Jvc;
        //if (B.frames != null) return B.frames.get(B.frameIndex).delay;
//.
		return 0;
	}
	
	
	
	/*
	
	FUN Get_pcd_from_stream(
	{Stream} Source|_stream, INT B|itmap_handle) ==> BOOL : .
	

	// Save Bitmap to file
	//----------------------
	
	FUN Save_to_stream_bmp(
	INT B|itmap_handle, {Stream} S|tream_dest) ==> BOOL,
	NATIVE : @
	"	RESULT = AL4_drawing.Do_Save_to_stream_bmp(X_B, X_S);" .

	FUN Save_to_stream_format(
	INT B|itmap_handle, {Stream} S|tream_dest, INT Q|uality_compression, 
	STR Format|_image) ==> BOOL, NATIVE : @
	"	RESULT = AL4_drawing.Do_Save_to_stream_format(X_B, X_S, X_Q, X_Format);" .

	FUN Save_bitmap_native|_to_file(
	INT B|itmap_handle, STR Path|_image_file) ==> BOOL
		:
		STR fixed_path = Path.Fix_path
		STR format|_from_path = Path.[Path.Find_last(".")+1 TO *].Upper
		//CASE format == "JPG" ? format = "JPEG" ;
		{Stream} temp|_stream = Write_file_stream(fixed_path, FALSE)
		RESULT = Save_to_stream_format(B, temp, 100, format) .
	*/
    public static boolean Do_Save_to_stream_bmp(int X_B, Ref_obj X_S)
    {
    	return false;
    }

    public static boolean Do_Save_to_stream_format(int X_B, Ref_obj X_S,
    	int X_Quality, String X_Format)
    {
    	BitmapEx B = (BitmapEx)AL4_form.All_descr.get(X_B).Jvc;
        try	{
            OutputStream os = NATIVE_Stream_on_C_Stream
            	.NATIVE_output_Stream_on_C_Stream.Stream_from_C_Stream(
            	(C_Stream)X_S.Obj);
            Bitmap.CompressFormat cf = Bitmap.CompressFormat.PNG;
//?{Png}
            if (X_Format == "PNG") {
            	cf = Bitmap.CompressFormat.PNG;
            }
//.
//?{Gif}
            else if (X_Format == "GIF") {
            	//cf = Bitmap.CompressFormat.GIF;
            	return false;
            }
//.
//?{Jpeg}
            else if (X_Format == "JPG") {
            	cf = Bitmap.CompressFormat.JPEG;
            }
//.
            if (X_Format == "WEBP") cf = Bitmap.CompressFormat.WEBP;
            B.Bmp.compress(cf, X_Quality, os);
            return true;
        }
        catch(Exception e) {
            //AL4_system.WriteLine("*** error save_bitmap: " + e);
            e.printStackTrace();
            return false;
        }
    }

    //public static boolean Do_Save_bitmap(int X_B, String X_Path, String Format,
    //    int Quality_jpeg)
    //{
    //    BitmapEx B = (BitmapEx)AL4_form.All_descr.get(X_B).Jvc;
    //    return do_save_bitmap(B.Bmp, X_Path, Format, Quality_jpeg);
    //}
	
    //public static boolean do_save_bitmap(Bitmap b, String X_Path, String Format,
    //    int Jpeg_quality)
    //{
    //    java.io.File f = new java.io.File(X_Path);
    //    try	{
    //        if (f.isFile()) f.delete();
    //        FileOutputStream fos = new FileOutputStream(f);
    //        Bitmap.CompressFormat cf = Bitmap.CompressFormat.PNG;
////?{Jpeg}
    //        if (Format == "JPG") {
    //            cf = Bitmap.CompressFormat.JPEG;
    //        }
////.
    //        if (Format == "WEBP") cf = Bitmap.CompressFormat.WEBP;
    //        b.compress(cf, Jpeg_quality, fos);
    //        fos.close();
    //        return true;
    //    }
    //    catch(java.io.IOException e) {
    //        //AL4_system.WriteLine("*** error save_bitmap: " + e);
    //        e.printStackTrace();
    //        return false;
    //    }
    //}

	/*
		
	FUN Save_bitmap_bmp(INT B|itmap_handle_DUMMY,
        STR Path|_image_file_DUMMY) ==> BOOL := FALSE .
        //"	RESULT = AL4_drawing.Do_Save_bitmap(X_B, X_Path, "#QU"BMP"#QU");" .

	FUN Save_to_stream_png(INT B|itmap_handle,
    {Stream} S|tream_dest, REAL Quality|_10_to_100) ==> BOOL, NATIVE : @
    "RESULT = AL4_drawing.Do_Save_to_stream_format(X_B, X_S, (int)X_Quality,"
       ''"PNG");''
    .
	
	FUN Save_bitmap_png(INT B|itmap_handle,
	STR Path|_image_file) ==> BOOL
	:
		{Stream} f|ile_output_stream_temp = Write_file_stream(Path, FALSE)
		RESULT = Save_to_stream_png(B, f, 100) .

	FUN Save_to_stream_jpg(INT B|itmap_handle,
    {Stream} S|tream_dest, REAL Quality|_10_to_100) ==> BOOL, NATIVE : @
    "RESULT = AL4_drawing.Do_Save_to_stream_format(X_B, X_S, (int)X_Quality,"
       ''"JPG");''
    .
	
	FUN Save_bitmap_jpg(INT B|itmap_handle,
        STR Path|_image_file, REAL Quality|_jpeg_10_100) ==> BOOL
        :
		STR fixed_path = Path.Fix_path
		CASE ? [Quality < 10] ? INT quality|_jpeg = 10
			   [Quality > 100]? quality = 100
			   ELSE quality = Quality.Int ;
		{Stream} temp|_file_stream = Write_file_stream(fixed_path, FALSE)
		RESULT = Save_to_stream_format(B, temp, quality, "JPG") .

	FUN Save_bitmap_gif(INT B|itmap_handle_DUMMY,
        STR Path|_image_file_DUMMY) ==> BOOL := FALSE .
        
	FUN Save_to_stream_gif(INT B|itmap_handle,
        {Stream} S|tream_dest) ==> BOOL : .
        
	
	FUN Can_load_bitmap_fmt(
		STR Format|_BMP_JPG_PNG_GIF) ==> BOOL
		:
		RESULT = Format.Upper IN ["JPG", "PNG", "WEBP", "GIF"] . 

	FUN Can_save_bitmap_fmt(
		STR Format|_BMP_JPG_PNG_GIF) ==> BOOL
		:
		RESULT = Format.Upper IN ["JPG", "PNG", "WEBP"] . 

	// Destroy Bitmap object
	//----------------------
	
	FUN Destroy_bitmap(INT B|itmap_handle), NATIVE :
		"     AL4_drawing.Do_Destroy_bitmap(X_B);" .
	*/
	public static void Do_Destroy_bitmap(int X_B)
	{
		BitmapEx B = (BitmapEx)AL4_form.All_descr.get(X_B).Jvc;
		AL4_form.ReleaseDescriptor(X_B);
		B.Bmp.recycle();
        B.Bmp = null;
	}
    
    /*
    
	FUN Draw_bitmap_rotated_flipped(
	INT Target|_bitmap_handle, INT Source|_bitmap_handle,
	INT Angle|_0_90_180_270,
	BOOL Flip_w|idth, BOOL Flip_h|eight), NATIVE : @
	"	AL4_drawing.do_draw_bitmap_rotated_flipped(X_Target, X_Source, "
	"		X_Angle, X_Flip_w, X_Flip_h);" .
	
    */
    public static void do_draw_bitmap_rotated_flipped(
    int X_Target, int X_Source, int X_Angle, boolean X_Flip_w, boolean X_Flip_h)
    {
        BitmapEx B = (BitmapEx)AL4_form.All_descr.get(X_Source).Jvc;
        BitmapEx D = (BitmapEx)AL4_form.All_descr.get(X_Target).Jvc;
        if (B == null || D == null) return;
        Matrix matrix = new Matrix();
		matrix.postRotate(X_Angle);
		if (X_Flip_w) matrix.postScale(-1, 1);
		if (X_Flip_h) matrix.postScale(1, -1);
        //if (((C_Canvas)(D.Obj)).F_Handle == 0) return;
        D.Bmp = Bitmap.createBitmap(B.Bmp, 0, 0,
        	B.Bmp.getWidth(), B.Bmp.getHeight(), matrix, true);
    }
    
    /*
    
	FUN Draw_bitmap_inverted(
	INT Target|_bitmap_handle, INT Source|_bitmap_handle), NATIVE : @
   "    AL4_drawing.BitmapEx B = "
   "			(AL4_drawing.BitmapEx)AL4_form.All_descr.get(X_Source).Jvc;"
   "    AL4_drawing.BitmapEx D = "
   "			(AL4_drawing.BitmapEx)AL4_form.All_descr.get(X_Target).Jvc;"
   "    if (B == null || D == null) return;"
   "    ColorMatrix colorMatrix_Inverted ="
   "    new ColorMatrix(new float[] {"
   "      -1,  0,  0,  0, 255,"
   "      0, -1,  0,  0, 255,"
   "      0,  0, -1,  0, 255,"
   "      0,  0,  0,  1,   0});"
   "    ColorFilter ColorFilter_Inverted = new ColorMatrixColorFilter("
   "        colorMatrix_Inverted);"
   "    Canvas canvas = new Canvas(D.Bmp);"
   "    Paint paint = new Paint();"
   "    paint.setColorFilter(ColorFilter_Inverted);"
   "    canvas.drawBitmap(B.Bmp, 0, 0, paint); " .
    */
    
    /*
    
	FUN Canvas_copy_rect(
	{Canvas} Dest|ination_canvas, {rect} R_dest|ination_rectangle,
	{Canvas} Source|_canvas, {rect} R_source|_rectangle)
		:
		CASE Dest == NONE || Source == NONE ? ==> ;
		--------------------------------- 'destination defined'
		REAL x_dest|ination = R_dest.Loc.X
		REAL y_dest|ination = R_dest.Loc.Y
		REAL w_dest|ination = R_dest.Width
		REAL h_dest|ination = R_dest.Height
		---------------------------------- 'source defined'
		REAL x_s|ource = R_source.Loc.X
		REAL y_s|ource = R_source.Loc.Y
		REAL w_s|ource = R_source.Width
		REAL h_s|ource = R_source.Height
		
		do_canvas_copy_rect(Dest.Handle, x_dest, y_dest, w_dest, h_dest,
							Source.Handle, x_s, y_s, w_s, h_s) .
							
	FUN do_canvas_copy_rect(INT Dest|ination_handle,
		REAL x_dest|ination, REAL y_dest|ination, 
		REAL w_dest|ination, REAL h_dest|ination,
		INT Source|_handle,
		REAL x_s|ource, REAL y_s|ource, REAL w_s|ource, REAL h_s|ource),
		NATIVE : @
       "AL4_drawing.BitmapEx B = "
       "		(AL4_drawing.BitmapEx)AL4_form.All_descr.get(X_Source).Jvc;"
       "AL4_drawing.BitmapEx D = "
       "		(AL4_drawing.BitmapEx)AL4_form.All_descr.get(X_Dest).Jvc;"
       "Canvas G = AL4_drawing.graphics_from_bitmap(D, false);"
       "G.drawBitmap(B.Bmp,"
       "    new Rect("
       "        (int)X_x_s, (int)X_y_s, (int)X_w_s, (int)X_h_s),"
       "    new RectF("
       "        (float)X_x_dest, (float)X_y_dest,"
       "		(float)X_w_dest, (float)X_h_dest),"
       "	null);" .
       
    FUN Draw_bitmap_on_canvas(
    INT B|itmap_handle,
    {Canvas} D|est_canvas,
    REAL X|_destination_in_pixels,
    REAL Y|_destination_in_pixels,
    REAL W|idth_pixels,
    REAL H|eight_ixels), NATIVE :
        INT dh|andle_of_dest_canvas = D.Handle
        NATIVE @
        "   AL4_drawing.Do_draw_bitmap(X_B, X_dh, X_X, X_Y, X_W, X_H);" .
    */
    public static void Do_draw_bitmap(int X_B, int X_D, 
        double X, double Y, double W, double H)
    {
        BitmapEx B = (BitmapEx)AL4_form.All_descr.get(X_B).Jvc;
        BitmapEx D = (BitmapEx)AL4_form.All_descr.get(X_D).Jvc;
        Canvas G = graphics_from_bitmap(D, false);
//?{Gif}
        //if (B.frames != null && B.frames.size() > 0)
        //{
        //    GifFrame f = B.frames.get(B.frameIndex);
        //    Graphics2D g2d = f.image.createGraphics();
        	 
        //    G.drawBitmap(f.image,
        //        new Rect(0, 0, f.image.getWidth(),f.image.getHeight()),
        //        new RectF((float)X, (float)Y, (float)(X+W), (float)(Y+H)),
        //        null);
        //}
        //else
//.
        if (B.Bmp.getWidth() == W && B.Bmp.getHeight() == H)
        {
            G.drawBitmap(B.Bmp, (float)X, (float)Y, null);
        }
        else
        {
            G.drawBitmap(B.Bmp,
            	new Rect(0, 0, B.Bmp.getWidth(), B.Bmp.getHeight()),
            	new RectF((float)X, (float)Y, (float)(X+W), (float)(Y+H)),
            	null);
        }
    }
    
    //----------------------------------------------- {Icon}
    
    /*
    
	FUN Capture_screen_rectangle_to_bitmap(
		INT B|itmap_handle, REAL X|_coordinate, REAL Y|_coordinate), UNCOOKED: .
		
    FUN Get_bitmap_from_clipboard(
	INT B|itmap_handle) ==> BOOL, UNCOOKED : .
	
    FUN Put_bitmap_to_clipboard(
	INT B|itmap_handle) ==> BOOL, UNCOOKED : .
	
    // Create Icon object, return descriptor.
    //-------------------------------------------
    
    FUN Create_icon({Icon} Icon|_object) ==> INT, NATIVE :
        "    RESULT = AL4_drawing.Do_Create_bitmap(X_Icon, 32);" .
    */ 

    
    
    /*
    // Load icon from stream
    //----------------------
    
    FUN Load_icon_from_stream(
    INT B|itmap_handle, {Stream} In|_stream), NATIVE :
        "     AL4_drawing.Do_load_icon(X_B, X_In);" .
        
   	FUN Load_icon_from_file(
   	INT B|itmap_handle, STR Path|_icon)
   		:
		{File_stream} fs|tream_temp = Read_file_stream(Path)
		Load_icon_from_stream(B, fs) .
    */
    public static void Do_load_icon(int X_B, Ref_obj X_In)
    {
        //DEBUG:
            //AL4_system.WriteLine("Do_load_icon");
        BitmapEx B = (BitmapEx)AL4_form.All_descr.get(X_B).Jvc;
        try
        {
            C_Stream CS = (C_Stream)(X_In.Obj);
            //DEBUG:
                //AL4_system.WriteLine("CS.Size=" + CS.M_Size());
            InputStream S = NATIVE_Stream_on_C_Stream
            	.NATIVE_input_Stream_on_C_Stream.Stream_from_C_Stream(CS);
            B.Ico = BitmapFactory.decodeStream(S);
            //DEBUG:
                //AL4_system.WriteLine("B.Ico=" + B.Ico);
        }
        catch (Exception e)
        {
            AL4_form.report(e);
            AL4_system.Any_exception(e);
            B.Ico = null;
            //AL4_system.WriteLine("*** error load_icon: " + e);
        }
    }
    
    //------------------------------------------------ {Bitmap_direct}
    
    /*
    // Lock bitmap pixels
    //----------------------
    
    FUN LockBitmapPixels(
    {Bitmap_direct} B|itmap_objects, INT Y|_position, 
    INT H|eight_rectangle, BOOL Read|_source), NATIVE :
		INT b|itmap_handle = B.Handle
    NATIVE @
        "   AL4_drawing.Do_LockBitmapPixels(X_b, X_Y, X_H, X_Read);" .
    */
    
    public static final int sizeOfInt = 4;
    
    public static void Do_LockBitmapPixels(int X_B, int X_Y, int X_H, 
           Boolean X_Read)
    {
//? {Bitmap_direct}
		BitmapEx B = (BitmapEx)AL4_form.All_descr.get(X_B).Jvc;
        B.Pixel_data = ByteBuffer.allocateDirect(
        	B.Bmp.getWidth() * X_H * sizeOfInt)
        	.order(ByteOrder.nativeOrder());
        B.locked_Y = X_Y;
        B.locked_H = X_H;
        C_Bitmap_direct D = (C_Bitmap_direct)B.Tag.Obj;
        
        if (D.F_Row_width == 0) D.F_Row_width = B.Bmp.getWidth();
        D.F_Pixels = new FixAByte(B.Pixel_data.array(), 0,
        	D.F_Row_width * X_H * sizeOfInt);
        	
//AL4_system.WriteLine("~~~~~~~~~~~ LockBitmapPixels " + X_B + " y=" + X_Y +
//" h=" + X_H + " read=" + X_Read + " D=" + D + " D.F_Pixels=" + D.F_Pixels +
//" D.F_Row_width=" + D.F_Row_width + " sizeOfInt=" + sizeOfInt +
//" B.Pixel_data=" + B.Pixel_data );

        if (X_Read) {
//AL4_system.WriteLine("reading...");

			try {
			int[] pixels = new int[B.Bmp.getWidth() * B.locked_H];
            //for (int y=B.locked_Y; y < B.locked_Y+B.locked_H; y++) {
            //    B.Bmp.getPixels(pixels,
            //        y * B.Bmp.getWidth(), B.Bmp.getWidth() // stride
            //        , 0, y, B.Bmp.getWidth()
            //        , 1);
            //    AL4_system.WriteLine(y + ": " + Integer.toHexString(
            //        pixels[y*B.Bmp.getWidth() + y]));
            //}
            B.Bmp.getPixels(pixels,
                0, B.Bmp.getWidth() // stride
                , 0, B.locked_Y, B.Bmp.getWidth()
                , B.locked_H);
            for (int i = 0; i < pixels.length; i++) {
                int x = (pixels[i] >> 16) & 0xff | ((pixels[i] & 0xff) << 16);
                pixels[i] = (pixels[i] & 0xFF00FF00) | x;
            //}
            //for (int y=B.locked_Y; y < B.locked_Y+B.locked_H; y++) {
            //    AL4_system.WriteLine(y + ": " + Integer.toHexString(
            //        pixels[y*B.Bmp.getWidth() + y]));
			}
            B.Pixel_data.asIntBuffer().put(pixels);
            B.Pixel_data.position(0);
			} catch (Exception ex) {
				AL4_system.WriteLine("\n....exception=" + ex.getMessage());
			}
        		
            //try {
//AL4_system.WriteLine("array pixels got(as int): " + B.Pixel_data.asIntBuffer());
//AL4_system.WriteLine("pixels got(as int): " + B.Pixel_data.asIntBuffer().get(10000) +
//"," + B.Pixel_data.asIntBuffer().get(62500) + "," +
//B.Pixel_data.asIntBuffer().get(65535));
            //} catch (Exception ex) {
            //    AL4_system.WriteLine("\n...exception=" + ex.getMessage());
            //}

//AL4_system.WriteLine("pixels got: " + B.Pixel_bytes[10000*4] + " " +
//B.Pixel_bytes[10000*4+1] + " " + B.Pixel_bytes[10000*4+2] + B.Pixel_bytes[10000*4+3] +
//"," + B.Pixel_bytes[62500*4] + " " + B.Pixel_bytes[62500*4+1] + " " +
//B.Pixel_bytes[62500*4+2] + B.Pixel_bytes[62500*4+3] + "," +
//B.Pixel_bytes[65535*4] + " " + B.Pixel_bytes[65535*4+1] + " " +
//B.Pixel_bytes[65535*4+2] + " " + B.Pixel_bytes[65535*4+3]);
        }
//.
    }
    
    /*
    // Unlock bitmap pixels
    //----------------------
    
    FUN UnlockBitmapPixels(
    {Bitmap_direct} B|itmap_object, BOOL Write|_back), NATIVE :
		INT b|itmap_handle = B.Handle
	NATIVE @
        "   AL4_drawing.Do_UnlockBitmapPixels(X_b, X_Write);" .
    */
    public static void Do_UnlockBitmapPixels(int X_B, Boolean X_Write)
    {
//? {Bitmap_direct}
		BitmapEx B = (BitmapEx)AL4_form.All_descr.get(X_B).Jvc;
        C_Bitmap_direct D = (C_Bitmap_direct)B.Tag.Obj;
        if (X_Write) {
            //B.Bmp.setPixels(B.Pixel_data.asIntBuffer().array(),
            //    0, B.Bmp.getWidth() // stride
            //    , 0, B.locked_Y, B.Bmp.getWidth(), B.locked_H);
        }
        D.F_Pixels = new FixAByte(0);
        B.Pixel_data = null;
        //B.Pixel_bytes = null;
//.
    }
    
    
	
	
	//----------------------------------------------------- C A N V A S

	/*
	
	FUN Need_erase_paintbox_1|st_time_buffer_created ==> BOOL := FALSE .
	
    FUN Get_mask_RGB({Bitmap_direct}.{rgb} C|omponent) ==> INT :
		RESULT = (0xFF).ShiftL(Get_shift_RGB(C)) .
    
    FUN Get_shift_RGB(
    {Bitmap_direct}.{rgb} C|omponent) ==> INT
    	:
		CASE C ?
		['R']: RESULT = 0
		['G']: RESULT = 8
		['B']: RESULT = 16
		['A']: RESULT = 24 ; .
    
	
	// Create Canvas object, return descriptor.
	//-------------------------------------------
	
    FUN Create_bitmap_canvas({Canvas} C|anvas_object,
    {Bitmap} B|itmap_object) ==> INT
        :
		RESULT = do_Create_bitmap_canvas(C, B) .
    FUN do_Create_bitmap_canvas({Canvas} C|anvas_object,
    {Bitmap} B|itmap_object) ==> INT, NATIVE :
    	INT bHandle||bitmap_Handle = B.Handle
    	NATIVE @
		"      RESULT = AL4_drawing.Do_Create_canvas(X_C, X_bHandle);" .
	
    //FUN Create_canvas({Canvas} C|anvas_object,
    //    INT Bitmap|_handle) ==> INT, NATIVE :
    //    "      RESULT = AL4_drawing.Do_Create_canvas(X_C, X_Bitmap);" .
        
    FUN Direct_draw_canvas(
    INT H|andle_control_dummy) ==> BOOL := FALSE .

    FUN Create_control_canvas(
        {Canvas} Canv|as_object_dummy, 
        {Control} Cont|rol_object_dummy) ==> INT RESULT|Handle
        := 0 .
	*/
	public static int Do_Create_canvas(Ref_obj X_C, int X_Bitmap)
	{
		return X_Bitmap;
	}

	/*
	
   	FUN Create_printer_canvas(
   		{Canvas} C|anvas_object,
		INT Printer_handle) ==> INT, UNCOOKED : .
	
	
	// Destroy Canvas object (actually not needed, still it is owned by TBitmapEx)
	//---------------------------------------------------------------------------
	
	FUN Destroy_canvas(INT C|anvas_handle), NATIVE :
		"    AL4_drawing.Do_Destroy_canvas(X_C);" .
	*/
	public static void Do_Destroy_canvas(int X_C) {
	}
	
	public static int Android_color(int AL4_color) {
		int Result = (AL4_color & 0xFF)<<16
				   | (AL4_color & 0xFF00FF00)
				   | (AL4_color & 0xFF0000)>>16 ;
		return Result;
	}

	public static C_Canvas C_Canvas_from_Bitmap(BitmapEx B)	{
        try {
            Ref_obj BRef = (Ref_obj)B.Tag;
            C_Bitmap CBmp = (C_Bitmap)BRef.Obj;
            C_Canvas CCanv = (C_Canvas)CBmp.M_Canvas().Obj;
            return CCanv;
        }
        catch(Exception ex) {
            return C_Canvas.NoneObj_C_Canvas;
        }
	}

	public static Paint Setup_pen(int X_C, Paint p)	{
		Paint result = p;
		if (p == null)
			result = new Paint(Paint.ANTI_ALIAS_FLAG|Paint.LINEAR_TEXT_FLAG);
		result.setStyle(Paint.Style.STROKE);
		BitmapEx B = (BitmapEx)AL4_form.All_descr.get(X_C).Jvc;
		C_Canvas CCanv = C_Canvas_from_Bitmap(B);
		result.setColor(Android_color(CCanv.F_Foreground));
        result.setStrokeWidth((float)AL4_form.Pixels((int)(CCanv.F_Pen_width * 10))
        	/ 10);
        return result;
	}

	public static Paint Setup_brush(int X_C, Paint p)	{
		Paint result = p;
		if (p == null)
			result = new Paint(Paint.ANTI_ALIAS_FLAG);
		result.setStyle(Paint.Style.FILL);
		BitmapEx B = (BitmapEx)AL4_form.All_descr.get(X_C).Jvc;
		C_Canvas CCanv = C_Canvas_from_Bitmap(B);
		result.setColor(Android_color(CCanv.F_Background));
        return result;
	}
	
	

	public static Paint Setup_font(int X_C, Paint p)
	{
		Paint result = p; 
		if (p == null)
			result = new TextPaint(Paint.ANTI_ALIAS_FLAG);
		BitmapEx B = (BitmapEx)AL4_form.All_descr.get(X_C).Jvc;
		C_Canvas CCanv = C_Canvas_from_Bitmap(B);
		C_Font CFont = (C_Font)CCanv.F_Font.Obj;
		FontEx Fex = (FontEx)AL4_form.All_descr.get(CFont.F_Handle).Jvc;
		if (Fex != null) {
			if (!Fex.Facename.equals(CFont.F_Face) || 
				Fex.Bold != CFont.F_Bold ||
				Fex.Italic != CFont.F_Italic) {
				
				Fex.Facename = CFont.F_Face;
				Fex.Bold = CFont.F_Bold;
				Fex.Italic = CFont.F_Italic;
				
				int style = Typeface.NORMAL;
				if (Fex.Bold) style |= Typeface.BOLD;
				if (Fex.Italic) style |= Typeface.ITALIC;
				
    //AL4_system.WriteLine("set font: " + Fex.Facename + ", " +
    //    "Bold=" + Fex.Bold + " Italic=" + Fex.Italic);
    			
    			Typeface tf = Typeface.DEFAULT;
    			switch (Fex.Facename) {
    			case "Arial": case "Droid Sans Serif":
    				tf = Typeface.SANS_SERIF; break;
    			case "Courier": case "Courier New": case "Droid Sans Mono":
    				tf = Typeface.MONOSPACE; break;
    			default:
					tf = Typeface.SERIF; break;
    			}
				Fex.Face = Typeface.create(tf, style);
			}
    //String to_show = "get font ---- " + Fex.Facename + ", " +
    //"Bold=" + Fex.Bold + " Italic=" + Fex.Italic;
    //if (!to_show.equals(Fex.face_size)) { Fex.face_size = to_show;
    //AL4_system.WriteLine(to_show);
    //    try {
    //        throw new Exception();
    //    }
    //    catch (Exception Ex) {
    //    AL4_system.WriteLine(AL4_form.stack_trace(Ex));
    //    }
    //}
    		result.setTypeface(Fex.Face);
			result.setTextSize(AL4_form.Points_float(CFont.F_Size));
		}
		return result;
	}

	public static float UnitsToPixels(C_Canvas C_C, double Value_units)
	{
		   switch (C_C.F_Units)
		   {
			  case 0: return AL4_form.Points_float(Value_units);
			  case 1: return (float)Value_units;
			  case 2: return AL4_form.Points_float(Value_units * AL4_form.LOGPIXELSX);
			  case 3: return AL4_form.Points_float(Value_units / 2.54 * AL4_form.LOGPIXELSX);
			  default: return 0;
		   }
	}

	public static double UnitsFromPixels(C_Canvas C_C, double Value_pixels)
	{
		   switch (C_C.F_Units)
		   {
			  case 0: return AL4_form.Pixels(Value_pixels);
			  case 1: return Value_pixels;
			  case 2: return AL4_form.Pixels(Value_pixels) / AL4_form.LOGPIXELSX;
			  case 3: return AL4_form.Pixels(Value_pixels) * 2.54 / AL4_form.LOGPIXELSX;
			  default: return 0;
		   }
	}

	/*
	// Erase rect
	//-----------
	
	FUN Erase_rect(
	{Canvas} C|anvas_object, {rect} R|ectangle):
	//DEBUG: << "ERASE " R.Srect #NL ;
		do_erase(C, R.Loc.X, R.Loc.Y, R.Width, R.Height) .
    FUN do_erase({Canvas} C|anvas_object, REAL X|_rectangle, REAL Y|_rectangle,
    REAL W|idth_rectangle, REAL H|eight_rectangle), NATIVE :
    	INT h|andle_of_canvas = C.Handle
    	NATIVE @
		"      AL4_drawing.Do_Erase_rect(X_h, X_X, X_Y, X_W, X_H);" .
	*/
	public static void Do_Erase_rect(int X_C,
	double X, double Y, double W, double H)
	{
		BitmapEx B = (BitmapEx)AL4_form.All_descr.get(X_C).Jvc;
		Canvas G = graphics_from_bitmap(B);
		C_Canvas C = C_Canvas_from_Bitmap(B);
		RectF R = new RectF(
			UnitsToPixels(C, X),
			UnitsToPixels(C, Y),
			UnitsToPixels(C, X + W),
			UnitsToPixels(C, Y + H));
		Paint br = Setup_brush(X_C, null);
		if (C.F_Brush != 1)
			G.drawRect(R, br);
	}

	/*
	// Paint rect
	//-----------
	
	FUN Paint_rect(
	{Canvas} C|anvas_object, {rect} R|ectangle)
		:
	//DEBUG: << "do_paint_rect: " R.Srect #NL ;
		do_paint_rect(C, R.Loc.X, R.Loc.Y, R.Width, R.Height) .
    FUN do_paint_rect({Canvas} C|anvas_object, REAL X|_rectangle,
    REAL Y|_rectangle, REAL W|idth_rectangle, REAL H|eight_rectangle), NATIVE :
		INT h|andle_canvas = C.Handle
		NATIVE @
		"AL4_drawing.Do_Paint_rect(X_h, X_X, X_Y, X_W, X_H);" .
	*/
	public static void Do_Paint_rect(int X_C,
	double X, double Y, double W, double H)
	{
		BitmapEx B = (BitmapEx)AL4_form.All_descr.get(X_C).Jvc;
		Canvas G = graphics_from_bitmap(B);
		C_Canvas C = C_Canvas_from_Bitmap(B);
		
		RectF R = new RectF(
			UnitsToPixels(C, X),
			UnitsToPixels(C, Y),
			UnitsToPixels(C, X + W),
			UnitsToPixels(C, Y + H));
        Paint brush = Setup_brush(X_C, null);
        if (C.F_Brush != 1) {
			brush.setStyle(Paint.Style.FILL);
	        G.drawRect(R, brush);
        }
        
        Setup_pen(X_C, brush);
		brush.setStyle(Paint.Style.STROKE);        
        G.drawRect(R, brush);
	}

	/*
	/// Paint line
	//-----------
	
	FUN Paint_line(
	{Canvas} C|anvas_object, {Geometry}.{point} P1|_point, {point} P2|_point)
		:
		do_paint_line(C, P1.X, P1.Y, P2.X, P2.Y) .
    FUN do_paint_line({Canvas} C|anvas_object, REAL X1|_point, REAL Y1|_point,
    REAL X2|_point, REAL Y2|_point), NATIVE :
		INT h|andle_canvas = C.Handle
		NATIVE @
		"      AL4_drawing.Do_Paint_line(X_h, X_X1, X_Y1, X_X2, X_Y2);" .
	*/
	public static void Do_Paint_line(int X_C, double X1, double Y1,
	double X2, double Y2)
	{
		BitmapEx B = (BitmapEx)AL4_form.All_descr.get(X_C).Jvc;
		Canvas G = graphics_from_bitmap(B);
		C_Canvas C = C_Canvas_from_Bitmap(B);
        Paint pen = Setup_pen(X_C, null);
        pen.setStyle(Paint.Style.STROKE);
        G.drawLine(
            UnitsToPixels(C, X1), UnitsToPixels(C, Y1),
            UnitsToPixels(C, X2), UnitsToPixels(C, Y2),
            pen);
	}

	/*
	// Paint ellipse
	//--------------
	
	FUN Paint_ellipse(
	{Canvas} C|anvas_object, {rect} R|ectangle_ellipse)
		:
		do_paint_ellipse(C, R.Loc.X, R.Loc.Y, R.Width, R.Height) .
    FUN do_paint_ellipse({Canvas} C|anvas_object,
    REAL X|_rectangle, REAL Y|_rectangle, REAL W|idth_rectangle,
    REAL H|eight_rectangle), NATIVE :
		INT h|andle_canvas = C.Handle
		NATIVE @
		"     AL4_drawing.Do_Paint_ellipse(X_h, X_X, X_Y, X_W, X_H);" .
	*/
	public static void Do_Paint_ellipse(int X_C,
	double X, double Y, double W, double H)
	{
		BitmapEx B = (BitmapEx)AL4_form.All_descr.get(X_C).Jvc;
		Canvas G = graphics_from_bitmap(B);
	//AL4_system.WriteLine("Matrix before Ellipse: " + B.Transform);
		C_Canvas C = C_Canvas_from_Bitmap(B);
		RectF R = new RectF(
			UnitsToPixels(C, X),
			UnitsToPixels(C, Y),
			UnitsToPixels(C, X + W),
			UnitsToPixels(C, Y + H));
        Paint brush = Setup_brush(X_C, null);
        if (C.F_Brush != 1) {
	        brush.setStyle(Paint.Style.FILL);
	        G.drawOval(R, brush);
        }
        Setup_pen(X_C, brush);
        brush.setStyle(Paint.Style.STROKE);
        G.drawOval(R, brush);
	}

	/*
	// Paint arc/pie
	//--------------
	
	FUN Paint_arc(
	{Canvas} C|anvas_object, {Canvas}.{arc} A|rc_information)
		:
        {arc} ark|_info = A.Clone
        REAL fr|om_angle = A.Angles.X
        REAL to|_angle = A.Angles.Y
        //DEBUG: << "fr=" fr.S " to=" to.S #NL ;
        ark.Angles.X = correct_arc_ray_angle(ark, fr)
        ark.Angles.Y = correct_arc_ray_angle(ark, to)
        CASE ark.Angles.Y < 0 && ark.Angles.X >= 0 ?
             ark.Angles.Y += 360 ;
        //DEBUG: << "corrected fr=" ark.A.X.S ", to=" ark.A.Y.S #NL ; 
        Call_Paint_arc(C, ark.R.Loc.X, ark.R.Loc.Y, ark.R.Width, ark.R.Height,
        	ark.A.X, ark.A.Y - ark.A.X, ark.Sided) .
        
    FUN correct_arc_ray_angle(
    {arc} A|rc_information, REAL Fi|_angle) ==> REAL
    	:
        ---------------------- 'java draws elliptic arc for angles of a circle'
        REAL a|_big_semi_axis = A.Rect.Width / 2
        REAL b|_small_semiaxis = A.Rect.Height / 2
        REAL s|inus_of_Fi = Sin(Fi * PI / 180)
        REAL c|osinus_Fi = Cos(Fi * PI / 180)
        CASE Abs(s) > 0.5 ?
             REAL y|_of_point_on_ellipsis = a * b / Sqrt(a*a + b*b * c*c / s/s)
             CASE s < 0 ? y = -y ;
             REAL x|_of_point = y * c/s
             CASE c * x < 0 ? x = -x ;
        ELSE x = a * b / Sqrt(b*b + a*a * s*s / c/c)
             CASE c < 0 ? x = -x ;
             y = x * s / c ;
        REAL y1|_corrected = y * a / b
        RESULT = Arctan2(y1, x) * 180 / PI
        .
        

    FUN Call_Paint_arc(
    {Canvas} C|anvas_object, REAL X|_left_of_rect, REAL Y|_top_of_rect,
    REAL W|idth_of_rect, REAL H|eight_of_rect,
    REAL AFrom|_angle, REAL ASweep|_angle, BOOL Sided|_arc),
		NATIVE :
		INT h|andle_canvas = C.Handle
		//DEBUG: << "Draw ARC from " AFrom.S " to " (AFrom + ASweep).S 
		//		  " degrees" #NL ; 
		NATIVE @
       "AL4_drawing.BitmapEx B = (AL4_drawing.BitmapEx)"
       "		AL4_form.All_descr.get(X_h).Jvc;"
       "Canvas G = AL4_drawing.graphics_from_bitmap(B);"
       "C_Canvas C = AL4_drawing.C_Canvas_from_Bitmap(B);"
       "RectF R = new RectF("
           "AL4_drawing.UnitsToPixels(C, X_X),"
           "AL4_drawing.UnitsToPixels(C, X_Y),"
           "AL4_drawing.UnitsToPixels(C, X_X + X_W),"
           "AL4_drawing.UnitsToPixels(C, X_Y + X_H));"
       "double angle1 = X_AFrom;"
       "double angle2 = X_ASweep;"
       "Paint brush = AL4_drawing.Setup_brush(X_h, null);"
       "if (X_Sided && C.F_Brush != 1) {"
	       "brush.setStyle(Paint.Style.FILL);"
	       "G.drawArc(R, (float)angle1, (float)angle2, X_Sided, brush);"
       "}"
       "AL4_drawing.Setup_pen(X_h, brush);"
       "brush.setStyle(Paint.Style.STROKE);"
       "G.drawArc(R, (float)angle1, (float)angle2, X_Sided, brush);"
       .
           
	// Paint text
	//--------------
	
	FUN Paint_text(
	{Canvas} C|anvas_object, {point} P|oint_top_left, STR Text|_draw)
		:
		PUSH C.Background = C.Foreground :
			 do_paint_text(C, P.X, P.Y, Text) ; .
    FUN do_paint_text({Canvas} C|anvas_object,
    REAL X|_of_point, REAL Y|_of_point, STR Text|_draw), NATIVE :
		INT h|andle_canvas = C.Handle
		NATIVE @
		"     AL4_drawing.Do_Paint_text(X_h, X_X, X_Y, X_Text);" .
	*/
	public static void Do_Paint_text(int X_C, double X, double Y, String X_Text)
	{
        //try	{
			BitmapEx B = (BitmapEx)AL4_form.All_descr.get(X_C).Jvc;
			Canvas G = graphics_from_bitmap(B);
			C_Canvas C = C_Canvas_from_Bitmap(B);
	
			TextPaint brush = (TextPaint)Setup_font(X_C, null);
			Setup_pen(X_C, brush);
        	//brush.setTextAlign(Paint.Align.LEFT);
        	Rect bounds  = new Rect();
          	brush.getTextBounds("|" + X_Text + "|", 0, X_Text.length()+2, bounds);
          	brush.setStyle(Paint.Style.FILL);
            G.drawText(X_Text, 
                (float)UnitsToPixels(C, X), 
                (float)(UnitsToPixels(C, Y)) + (bounds.bottom - bounds.top), brush);
            //AL4_system.WriteLine("written text [" + X_Text + "] at " + X + ", " + Y +
            //", bounds=[" + bounds.left + "/" + bounds.top + "/" + bounds.right +
            //"/" + bounds.bottom + "]=" + bounds.toString());
        //}
        //catch (Exception e) {
            //AL4_system.WriteLine("*** error paint text: " + e.getMessage() +
            //'\n' + AL4_form.stack_trace(e));
            ////e.printStackTrace();
        //}
	}

	/*
	// Paint poly
	//--------------
	
	FUN Paint_poly(
	{Canvas} C|anvas_object, {point} P|oints_array[*], BOOL Closed|_polygon)
		:
		REAL pts|_coordinates[]
		FOR i IN [0 TO P[].Count-1] :
		    {point} p|_at_i_index = P[i].Clone
		    CASE i == P[].Count-1 && !Closed ? BREAK i ;
		    pts[] << p.X << p.Y
		    CASE i < P[].Count-1 ?
		         pts[] << P[i+1].X << P[i+1].Y
		    ELSE pts[] << P[0].X << P[0].Y ;
		;
		do_paint_poly(C, pts[0 TO *], Closed) .
    FUN do_paint_poly({Canvas} C|anvas_object,
    REAL Pts|_X_Y_coordinates[*],
    BOOL Closed|_polygon), NATIVE :
		INT h|andle_canvas = C.Handle
		INT background = C.Background
		NATIVE @
		"     AL4_drawing.Do_Paint_poly(X_h, X_Pts, X_Closed, X_background);" .
	*/
	public static void Do_Paint_poly(int X_C, ArrayReal X_Pts, 
        boolean X_Closed, int X_background) {
		if (X_Pts.Count() ==0) return;
		BitmapEx B = (BitmapEx)AL4_form.All_descr.get(X_C).Jvc;
		Canvas G = graphics_from_bitmap(B);
		C_Canvas C = C_Canvas_from_Bitmap(B);
        Paint brush = Setup_brush(X_C, null);
        Setup_pen(X_C, brush);
		if (X_Pts.Count() <= 2) {
            RectF R = new RectF(
                UnitsToPixels(C, X_Pts.Get_item(0)), 
                UnitsToPixels(C, X_Pts.Get_item(1)), 1, 1);
            G.drawRect(R, brush);
		}
		else {
            float[] pts = new float[X_Pts.Count()];
			for (int i = 0; i < X_Pts.Count(); i++) {
				pts[i] = UnitsToPixels(C, X_Pts.Get_item(i));
            }
            
			if (X_Closed) {
				Paint polyPaint = new Paint();
				polyPaint.setColor(Android_color(X_background));
				polyPaint.setStyle(Paint.Style.FILL);
				
				Path polyPath = new Path();
				polyPath.moveTo(pts[0], pts[1]);
				for (int i = 2; i < X_Pts.Count(); i += 2) {
					polyPath.lineTo(pts[i], pts[i+1]);
	            }
	            polyPath.lineTo(pts[0], pts[1]);
				G.drawPath(polyPath, polyPaint); 
			}
		
			G.drawLines(pts, brush);
		//AL4_system.WriteLine("draw poly count pts=" + pts.length);
		}
	}


	/*
	// Reset transforms
	//-----------------
	
	FUN Canvas_transform_reset(
	{Canvas} C|anvas_object), NATIVE :
		INT h|andle_canvas = C.Handle
		NATIVE @
		"     AL4_drawing.Do_transform_reset(X_h);" .
	*/
	public static void Do_transform_reset(int X_C)
	{
		BitmapEx B = (BitmapEx)AL4_form.All_descr.get(X_C).Jvc;
		Canvas G = graphics_from_bitmap(B);
        B.Transform = new Matrix();
        ///G.setMatrix(B.Transform);
	}

	/*
	// Offset transformation
	//----------------------
	
	FUN Canvas_transform_offset(
	{Canvas} C|anvas_object, REAL Dx|_delta, REAL Dy|_delta), NATIVE :
		INT h|andle_canvas = C.Handle
		NATIVE @
		"     AL4_drawing.Do_transform_offset(X_h, X_Dx, X_Dy);" .
	*/
	public static void Do_transform_offset(int X_C, double Dx, double Dy)
	{
		BitmapEx B = (BitmapEx)AL4_form.All_descr.get(X_C).Jvc;
		Canvas G = graphics_from_bitmap(B);
        C_Canvas C_C = C_Canvas_from_Bitmap(B);
        B.Transform.preTranslate(
        	UnitsToPixels(C_C, Dx), 
        	UnitsToPixels(C_C, Dy));
        ///G.setMatrix(B.Transform);
	}

	/*
	// Rotation transformation
	//------------------------
	
	FUN Canvas_transform_rotate(
	{Canvas} C|anvas_object, REAL Angle|_degrees), NATIVE :
		INT h|andle_canvas = C.Handle
		NATIVE @
		"     AL4_drawing.Do_transform_rotate(X_h, X_Angle);" .
	*/
	public static void Do_transform_rotate(int X_C, double Angle)
	{
		BitmapEx B = (BitmapEx)AL4_form.All_descr.get(X_C).Jvc;
		Canvas G = graphics_from_bitmap(B);
		B.Transform.preRotate((float)Angle, 0, 0);
        ///G.setMatrix(B.Transform);
	}

	/*
	// Scale transformation
	//---------------------
	
	FUN Canvas_transform_scale(
	{Canvas} C|anvas_object, REAL Scale_x|_value, REAL Scale_y|_value), NATIVE :
		INT h|andle_canvas = C.Handle
		NATIVE @
		"     AL4_drawing.Do_transform_scale(X_h, X_Scale_x, X_Scale_y);" .
	*/
	public static void Do_transform_scale(int X_C, double Sx, double Sy)
	{
		BitmapEx B = (BitmapEx)AL4_form.All_descr.get(X_C).Jvc;
		Canvas G = graphics_from_bitmap(B);
        B.Transform.preScale((float)Sx, (float)Sy);
        ///G.setMatrix(B.Transform);
	}
    
    // get Canvas, set its attributes
    public static Canvas graphics_from_bitmap(
    	BitmapEx B, Boolean transforms) {
    	
        Canvas Result = new Canvas(B.Bmp);
        if (transforms && B.Transform != null)
            Result.setMatrix(B.Transform);
        return Result;
    }
    
    public static Canvas graphics_from_bitmap(
    	BitmapEx B) {
    	return graphics_from_bitmap(B, true);
    }
    /*
    // String measure for current font
    //---------------------------------
    
    FUN String_measure(
    {Canvas} C|anvas_object, STR S|tring_to_measure) ==> {rect}
    	:
    	REAL m|easure_values[]
    	do_string_measure(C, S, m[])
    	RESULT = {rect}(Width = m[2], Height = m[3]) .
    FUN do_string_measure({Canvas} C|anvas_object, STR S|tring_to_measure,
    REAL M|easured_values_W_H[]), NATIVE :
        INT h|andle_canvas = C.Handle
        NATIVE @
		"	AL4_drawing.Do_String_measure(X_h, X_S, X_M);" .
		
    //FUN String_bounds(
    //{Canvas} C|anvas_object, STR S|tring_to_measure) ==> {rect}
    //	:
    //	REAL m|easure_values[]
    //	do_string_measure(C, S, m[])
    //	RESULT = {rect}(Loc = Pt(m[0], m[1]), Width = m[2], Height = m[3]) 
    //	DEBUG:
    //	      CASE m[0] < 0 || m[1] < 0 ?
    //	           << "string bounds for [" S "]=" RESULT.S #NL ;
    //	; 
    //	.
	*/	
	public static void Do_String_measure(int X_C, String X_S, ArrayReal W_H) {
          AL4_drawing.BitmapEx B = (AL4_drawing.BitmapEx)
                 AL4_form.All_descr.get(X_C).Jvc;
          Canvas G = graphics_from_bitmap(B);
          C_Canvas C_C = C_Canvas_from_Bitmap(B);
          TextPaint p = (TextPaint)Setup_font(X_C, null);
          Rect bounds  = new Rect();
          
          p.getTextBounds("X" + X_S + "x", 0, X_S.length() + 2, bounds);
          double w = p.measureText("X" + X_S + "x");
          //bounds.width();
          
          //Rect bounds_xx  = new Rect();
          //p.getTextBounds("Xx", 0, 2, bounds_xx);
          w = w - p.measureText("Xx"); 
          //bounds_xx.width();

		  Paint.FontMetrics fm = p.getFontMetrics();
		  
    //AL4_system.WriteLine("measure[" + X_S + "]: " + bounds.top + " " +
    //bounds.bottom + ", " + bounds.left + " " + bounds.right + " " +
    //"ascent=" + fm.ascent + " descent=" + fm.descent + " bottom=" + fm.bottom);
    	  W_H.Add(AL4_drawing.UnitsFromPixels(C_C, bounds.left));
    	  W_H.Add(AL4_drawing.UnitsFromPixels(C_C, bounds.top));
          W_H.Add(AL4_drawing.UnitsFromPixels(C_C, w));
          float h = -fm.ascent + fm.bottom;
          if (h < bounds.bottom - bounds.top)
          	  h = bounds.bottom - bounds.top;
          W_H.Add(AL4_drawing.UnitsFromPixels(C_C, h));
	}
	
	/*
	
    FUNCTION Set_canvas_quality(
    	{Canvas} C|anvas_object, {Canvas}.{quality} Q|uality_set) : .
    	
    FUN End_paint_of_canvas(
	{Canvas} C|anvas_object_dummy): .
	*/
}


/*
END
*/
