//http://java.sch.bme.hu/doc/java/hackjava/ch4.htm#DisplayingOtherImageFormats
//http://www.eg3.com/WebID/dsp/
//http://www.nauticom.net/www/jdtaft/ java filtravimas
import java.applet.Applet;
import java.applet.*;
import java.awt.Graphics;
import java.awt.Image;
import javax.swing.*;
import java.awt.image.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import java.io.*;
import javax.swing.JFileChooser;
import javax.swing.filechooser.FileSystemView;
//import java.Object.Color;
import java.awt.Toolkit;

// This is a simple example applet that loads an image and
// displays it.


public class DrawImage extends Applet{
	public static boolean autocor=false;
	public static boolean trytracing=false;
	public static String ImageFile = "";
	public static String ext = ".gif";
	public static String pirshtai = "E:\\EBastys\\Java\\Image\\pirshtai\\";
	public static String kelias = "E:/bastys/Java/Image/";
	public static Image image,image1,image2,image3,imagec,imagem;
	public static int[] data;
	public static int w,W,h,H,wh;
	public static double trsh=0.70;
	public static int umidle=80;
	public static Component cmp;
	private static byte[] A, B; // Skeletonizavimui
	static byte[] uk; //ka*ka ishdidintas vaizdas
	static byte[] bminmax; // suzymeti minimumo ir maksimumo taskai
	static byte[] ua,uae,ue; // autokoreliacinis ir erozinis
	static int utrshld=8; // slenkstis u reiksmiu kurios yra reiksmines
	static int ka=1; // ishdidinimo koeficientas
	static int na=8; // krypciu skaicius
	static int La=16; // pusilgis viena kryptimi
	public static int K=8; // keliu tasku atstumo ieskoti minimumo ir maksimumo
	public static int KR=10; // kokiu spinduliu daryti tracinga
	public static int[] xR,yR; // artimiausi KR spinduliui indeksai
	static int K2pi=15; // K2pi>2*pi - KR ir paraboles tasku daugiklis
	public static int RF=6; // kokiu spinduliu daryti Furje transformacija
	public static int[] xF,yF; // Artimiausi FR spinduliui indeksai
	public static int KF=32; // Apskritime Furje tasku skaicius
	public static int Krypciu=36; // Keliomis kryptimis paleisti bangas
	public static int maxAtstumas=16; // maksimalus atstumas duota bangos kryptimi
	public static int[][] banga = new int[Krypciu][];
	static double[] c,s; // krypciu cos ir sin
	static int[][] posl,posw,posh;  // poslinkiu indeksai
	int[] dmin, dmax, dir;
	static double pi = Math.PI;
	Color[] spalva;
	int[] spalvaA,spalvaR,spalvaG,spalvaB;
	int S;
	int iwidth = 520, iheight = 740;
	static int invstep=8;
	boolean handledir = false;

 public void init(){
		
	initcs(); initposl(); initcolors(); initAB();
	initxRyR(); initxFyF();

	cmp = this;
	openImage(getParameter("failas"));

//	uk=upsample(uk,W,H); W=2*W-1; H=2*H-1; ka*=2;
//	uk=upsample(uk,W,H); W=2*W-1; H=2*H-1; ka*=2;
//	image2 = createImage(new MemoryImageSource(W,H,byte2int(upsample(int2byte(data),w,h)), 0, W));
//	image = createImage(new MemoryImageSource(W,H,byte2int(uk), 0, W));

	if (handledir){
		getDirection();
		image1 = createImage(new MemoryImageSource(w,h,dmin, 0, w));
		image2 = createImage(new MemoryImageSource(w,h,dmax, 0, w));
		image3 = createImage(new MemoryImageSource(w,h,dir, 0, w));
	}
	if (autocor) ua = fft.autocorr2(int2byte(data), w, h, 2*La, 127);
		else ua = my.move(int2byte(data));
	image1 = createImage(new MemoryImageSource(w,h,byte2int(ua), 0, w));
//	image2 = Toolkit.getDefaultToolkit().createImage(data);

	Frame flt = new Frame("Filtravimas");
	flt.setSize(iheight, iheight);
//	rtcanvas = new RTCanvas(ekg, ptca);
//        flt.getContentPane().add(rtcanvas);
	Canvas fltCanvas = new Canvas();
	fltPanel fltpanel = new fltPanel(fltCanvas);
//	fltCanvas.add(fltpanel);
	flt.add(fltpanel);
	flt.setVisible(true); flt.show();     
 }

 public void paint(Graphics g){
	g.drawImage(image, 10, 10, this); 
	int oh = (int) ((h+10)/2), ow = (int) ((w+10)/2);
	for (int i=0; i<na; i++){
//		my.print(i,"i");
		g.setColor(spalva[i%S]);
		for (int l=1; l<2*La; l++){
//			int[] ii = new int[]{ow+(posl[i][l-1]%W)/ka,oh+(posl[i][l-1]/W)/ka,
//					ow+(posl[i][l]%W)/ka,oh+(posl[i][l]/W)/ka};
			int[] ii = new int[]{ow+posw[i][l-1]/ka,oh+posh[i][l-1]/ka,
					ow+posw[i][l]/ka,oh+posh[i][l]/ka};
//			my.print(ii,"ii");
//			g.drawLine(ii[0],ii[1],ii[2],ii[3]);
		}
	}
	g.drawImage(image1, 10*2+w, 10, this);
	g.drawImage(image2, 10, 10*2+h, this);
	g.drawImage(image3, 10*2+w, 10*2+h, this);
	if (handledir){
		g.drawImage(image2, 10, 10*2+h, this);
		g.drawImage(image3, 10*2+w, 10*2+h, this);
	}
 }

 public static void openImage(String vardas){
	int tsk = vardas.indexOf(ext);
	if (tsk>0) vardas = vardas.substring(0,tsk);
	MediaTracker mt;
	mt= new MediaTracker(cmp);
	my.println(pirshtai+vardas+ext);
	image = Toolkit.getDefaultToolkit().getImage(pirshtai+vardas+ext);
        mt.addImage(image,0);
        try { mt.waitForAll();} 
	catch (InterruptedException ie) { ie.printStackTrace();}
        w = image.getWidth(cmp); h = image.getHeight(cmp); wh=w*h;
	initBangas(w);

//	Iskiriame viena kontura
	int wh=w*h;
	data = getPixels(image);

	fltPanel1.w = w; fltPanel1.h = h; fltPanel1.wh = wh;
//	my.print(data.length,"L"); my.print(w,"w"); my.print(h,"h");
	uk=int2byte(data);
	W=w; H=h;
	if (autocor) ua = fft.autocorr2(int2byte(data), w, h, 2*La, 127);
		else ua=my.move(int2byte(data));
	image1 = cmp.createImage(new MemoryImageSource(w,h,byte2int(ua), 0, w));
	ue = erosion(int2byte(data), w, h, 2*La);
	image2 = cmp.createImage(new MemoryImageSource(w,h,byte2int(ue), 0, w));
	image3 = cmp.createImage(new MemoryImageSource(w,h,byte2int(uae), 0, w));
	fltPanel1.umex = fft.mexHatflt(int2byte(data),w,h);
	imagec = makeImagec(fltPanel1.umex,w,h);

	byte[] bu=new byte[wh];
	for (int i=0; i<wh; i++) bu[i]=(byte)((fltPanel1.umex[i]&(255<<16))>>16);
	byte[] l = new byte[wh];
	if (trytracing){
		maxlinetracing(l, bu, w, h, w/2, h/2);
		maxlinetracing(l, bu, w, h, w/2+w/8, h/2);
		maxlinetracing(l, bu, w, h, w/2-w/8, h/2);
		maxlinetracing(l, bu, w, h, w/2, h/2+h/8);
		maxlinetracing(l, bu, w, h, w/2, h/2-h/8);
		minlinetracing(l, bu, w, h, w/2, h/2);
		minlinetracing(l, bu, w, h, w/2+w/8, h/2);
		minlinetracing(l, bu, w, h, w/2-w/8, h/2);
		minlinetracing(l, bu, w, h, w/2, h/2+h/8);
		minlinetracing(l, bu, w, h, w/2, h/2-h/8);
	}
	int AR=(255<<24)|(255<<16),AB=(255<<24)|(255);
	int[] kl=new int[2];
	for (int i=0; i<wh; i++) if (l[i]==127){ data[i]=AR; kl[0]++;}
		 else if (l[i]==-128){ data[i]=AB; kl[1]++;}
	my.print(kl,"kl");
	image=cmp.createImage(new MemoryImageSource(w,h,data, 0, w));

	if (autocor) fltPanel1.bmex = fft.autocorr2(fltPanel1.umex, w, h, 2*La);
		else fltPanel1.bmex = my.move(int2byte(fltPanel1.umex));
//	fltPanel1.uekst = pazymekekstr(fltPanel1.umex,w,h);
	bminmax = new byte[w*h];
	fltPanel1.uekst = pazymekmax(true,fltPanel1.umex,w,h);
	fltPanel1.uekst = pazymekmax(!true,fltPanel1.umex,w,h);
	image3 = cmp.createImage(new MemoryImageSource(w,h,byte2int(bminmax), 0, w));

	fltPanel1.uDif = new float[wh];
	for (int i=0; i<wh; i++) fltPanel1.uDif[i]=((float)(data[i]&0xff))-128;
	fltPanel1.imageDif = cmp.createImage(new MemoryImageSource(w,h,float2int(fltPanel1.uDif),0,w));
	fltPanel1.ubw = new byte[wh];
	int sle=(int)(255*trsh-128);
	for (int i=0; i<wh; i++) if (fltPanel1.uDif[i]>sle) fltPanel1.ubw[i] = 127;
		else fltPanel1.ubw[i] = -128;
	if (autocor) fltPanel1.uSkel = getSkeleton(fltPanel1.ubw,w,h);
	ImageFile = vardas;
	if (fltPanel1.vardas!=null) fltPanel1.vardas.setText(vardas);
 }

public static byte[] erosion(byte[] u, int w, int h, int L){
 double a=2.0-Math.sqrt(2.0), bc=(Math.sqrt(2.0)-1.0)/2;
 double umean=maxaverage(u,w,h,L);
 int wh=w*h, L2=L/2, L4=L2/2;
 byte[] b = new byte[wh];
 int[] U = new int[wh], e = new int[wh];
 int p,umx,umi,s,S;
 umx=127-(int)u[0]; umi=umx;
 for (int i=0; i<wh; i++) {
	U[i]=127-(int)u[i];
	if (umx<U[i]) umx=U[i];	else if (umi>U[i]) umi=U[i];
	e[i]=U[i];
 }
 my.print(new int[]{umx,umi},"umx,umi");
 int w1=w+1, w2=wh-w-1;
 if (umean>umidle) for (int i=w1; i<w2; i++) {
/* circle mode
	p = Math.min(U[i],Math.min(Math.min(U[i+1],U[i-1]),Math.min(U[i+w],U[i-w])));
	for (int ii=-1; ii<=1; ii+=2) for (int jj=-1; jj<=1; jj+=2)
		if (U[i+ii+jj*w]<p) p=Math.min(p,(int)(a*U[i+ii+jj*w]+bc*(U[i+jj*w]+U[i+ii])));
*/
	p = Math.min(Math.min(U[i],U[i+1]),Math.min(U[i+w],U[i+w+1]));
	e[i]=p;
 }
 else for (int i=w1; i<w2; i++) {
/* circle mode
	p = Math.max(U[i],Math.max(Math.max(U[i+1],U[i-1]),Math.max(U[i+w],U[i-w])));
	for (int ii=-1; ii<=1; ii+=2) for (int jj=-1; jj<=1; jj+=2)
		if (U[i+ii+jj*w]>p) p=Math.max(p,(int)(a*U[i+ii+jj*w]+bc*(U[i+jj*w]+U[i+ii])));
*/
	p = Math.max(Math.max(U[i],U[i+1]),Math.max(U[i+w],U[i+w+1]));
	e[i]=p;
 }
 for (int i=0; i<wh; i++) b[i]=(byte)(127-e[i]);
 double newumean=maxaverage(b,w,h,L);
 double alfa=Math.max(0,Math.min(1,
		(umidle-umean)/(newumean-umean))), alfa1=1-alfa;
 my.print(alfa,"alfa");
 for (int i=0; i<wh; i++) b[i]=(byte)(alfa1*u[i]+alfa*b[i]);
 data = byte2int(b);
 if (autocor) uae = fft.autocorr2(b, w, h, L, 127);
		else uae=my.move(b);
 return b;
}

// uzlopyk lokalius maksimumus ir minimumus
public static byte[] uzlopyk(byte[] u, int w, int h){

 double a=2.0-Math.sqrt(2.0), bc=(Math.sqrt(2.0)-1.0)/2;
 int wh=w*h,p,P,o;
 byte[] b = new byte[wh];
 int[] U = new int[wh];
 for (int i=0; i<wh; i++){ U[i]=(int)u[i]; b[i]=u[i];}
 int w1=w+1, w2=wh-w-1,m=0,M=0;
 for (int i=w1; i<w2; i++) {
	o = U[i];
	p = Math.min(Math.min(U[i+1],U[i-1]),Math.min(U[i+w],U[i-w]));
	P = Math.max(Math.max(U[i+1],U[i-1]),Math.max(U[i+w],U[i-w]));
	if (o<p){
		for (int ii=-1; ii<=1; ii+=2) for (int jj=-1; jj<=1; jj+=2)
			if (U[i+ii+jj*w]<o) p=Math.min(p,(int)(a*U[i+ii+jj*w]+bc*(U[i+jj*w]+U[i+ii])));
		if (o<p){ b[i]=(byte)p; m+=1;}
	}
		else if (o>P){
		for (int ii=-1; ii<=1; ii+=2) for (int jj=-1; jj<=1; jj+=2)
			if (U[i+ii+jj*w]>o) P=Math.max(P,(int)(a*U[i+ii+jj*w]+bc*(U[i+jj*w]+U[i+ii])));
		if (o>P) {b[i]=(byte)P;  M+=1;}
	}
 }
 my.print(new int[]{m,M},"m,M");
 return b;
}

// pazymÓk lokalius maksimumus ir minimumus
public static int[] pazymekekstr(int[] u, int w, int h){
 int A=255<<24, G=255<<8, B=255, j;
/* Meksikietishkas filtras
	{ 0,  0, -1, -1, -1,  0,  0},
	{ 0, -1, -3, -3, -3, -1,  0},
	{-1, -3,  0, +7,  0, -3, -1},
	{-1, -3, +7, 24, +7, -3, -1},
	{-1, -3,  0, +7,  0, -3, -1},
	{ 0, -1, -3, -3, -3, -1,  0},
	{ 0,  0, -1, -1, -1,  0,  0},
*/
 int n=3;
// int[] dr = new int[]{0,1,1+w,w,w-1,-1,-1-w,-w,-w+1,-w+2,2,2+w,2*w+1,2*w,2*w-1,-2+w,-2,-w-2,-1-2*w,-2*w,-2*w+1};
 int[] dr = new int[(2*n+1)*(2*n+1)-4*3];
 int Dr=0;
 for (int i=-n; i<=n; i++) for (j=-n; j<=n; j++)
	if (Math.abs(i)+Math.abs(j)<2*n-1) { dr[Dr]=i+j*w; Dr+=1;}
 if (Dr!=dr.length) my.print_exit("Dr!=dr.length"); 
 	
 int[] kr = new int[]{1,2+w,1+w,1+2*w,w,2*w-1,w-1,w-2,0,0,0,0,0,0,0,0},
	S = new int[kr.length];
 int Kr = kr.length, K2=Kr/2;
 for (int k=0; k<K2; k++) kr[k+K2] = -kr[k];
 if (K2!=8) my.print_exit("K2!=8");

 double a=2.0-Math.sqrt(2.0), bc=(Math.sqrt(2.0)-1.0)/2;
 int wh=w*h,p,P,o;
 byte[] b = new byte[wh];
 int[] U = new int[wh];
 for (int i=0; i<wh; i++){
	j = u[i]; if ((j&G)==G)	U[i]=(j&B)-B;
		else U[i]=B-(j&B);
 }
 int w1=(n+2)*(w+1), w2=wh-w1, m=0,M=0;
 for (int i=w1; i<w2; i++) {
	o = U[i];
	p = Math.min(Math.min(U[i+1],U[i-1]),Math.min(U[i+w],U[i-w]));
	P = Math.max(Math.max(U[i+1],U[i-1]),Math.max(U[i+w],U[i-w]));
	P = Math.max(utrshld,P);
/* minimumus atjun
	if (o<p){
		for (int ii=-1; ii<=1; ii+=2) for (int jj=-1; jj<=1; jj+=2)
			if (U[i+ii+jj*w]<o) p=Math.min(p,(int)(a*U[i+ii+jj*w]+bc*(U[i+jj*w]+U[i+ii])));
		if (o<p){ b[i]=true; m+=1;}
	}
		else
*/
	if (o>P){
		for (int ii=-1; ii<=1; ii+=2) for (int jj=-1; jj<=1; jj+=2)
			if (U[i+ii+jj*w]>o) P=Math.max(P,(int)(a*U[i+ii+jj*w]+bc*(U[i+jj*w]+U[i+ii])));
		if (o>P) {
/*	
			if (U[i+1]+U[i-1]>U[i+w]+U[i-w]) { p=U[i+1]+U[i-1]; b[i]=1; }
				else { p=U[i+w]+U[i-w]; b[i]=3; }
			if (U[i+1+w]+U[i-1-w]>p) { p=U[i+1+w]+U[i-1-w]; b[i]=2;}
			if (U[i-1+w]+U[i+1-w]>p) b[i]=4;
*/
			for (int k=0; k<Kr; k++) {
				p=0; j=i+kr[k];
				for (int d=0; d<Dr; d++) p+=U[i+dr[d]]*U[j+dr[d]];
				S[k]=p;
			}
			b[i]=1; p=S[0]+S[K2];
			for (int k=1; k<K2; k++) if (p<S[k]+S[k+K2]) {p=S[k]+S[k+K2]; b[i]=(byte)(k+1);}
			M+=1;
		}
	}
 }
 my.print(new int[]{m,M},"m,M");
 for (int i=0; i<wh; i++) U[i]=u[i];
 for (int i=w1; i<w2; i++) if (b[i]!=0) for (int k=0; k<K2; k++)
	if (b[i]==k+1) {U[i]=A|B; U[i+kr[k]]=A|B; U[i-kr[k]]=A|B; break;}

 return U;
}

// pazymÓk lokalius maksimumus, duomenys: -128<=U<128
public static int[] pazymekmax(boolean minmode,int[] u, int w, int h){
 final int A=255<<24, R=255<<16, G=255<<8, B=255,
	AB=A|B, AR=A|R, AG=A|G, Y=A|R|G, W=A|R|G|B,
	wp1=w+1, wm1=w-1;
 int n=8, Kr=3; // Kr nulinamu kraste tsk. skaicius
 int[] d = new int[]{1,1+w,w,w-1,-1,-1-w,-w,-w+1},
		D = new int[]{3,2+2*w,3*w,2*w-2,-3,-2-2*w,-3*w,-2*w+2};
 int wh=w*h,w1=Kr*w,w2=w*h-Kr*w,p,P,o,O,OD;
 byte[] b = new byte[wh];
 int[] U = new int[wh], S, M=new int[4];
 boolean didz,lygus;
// for (int i=w1; i<w2; i++) if ((u[i]&G)==G) U[i]=255-((u[i]&R)>>16);
 if (!minmode){ for (int i=w1; i<w2; i++) if ((u[i]&R)==R) U[i]=255-((u[i]&G)>>8);}
	else {for (int i=w1; i<w2; i++) if ((u[i]&G)==G) U[i]=255-((u[i]&R)>>16);}
// nukertame Kr vertikalu krasta (horizontaliu pasirupina w1,w2)
 for (int j=0; j<h; j++) for (int k=0; k<Kr; k++) {U[j*w+k]=0;U[j*w+w-1-k]=0;}
 for (int i=w1; i<w2; i++) if (U[i]>utrshld) {
	o=U[i];
	P = Math.max(Math.max(U[i+1],U[i-1]),Math.max(U[i+w],U[i-w]));
	P = Math.max(Math.max(Math.max(U[i+wp1],U[i-wp1]),Math.max(U[i+wm1],U[i-wm1])),P);
	if (o>P) {b[i]=1; M[0]++;} else if (o==P){
		didz=true; lygus=false;
		S=new int[n]; //uznuliname S
		O=0; for (int j=0; j<n; j++) O+=U[i+d[j]];		
		for (int j=0; j<n; j++){
			if (U[i]==U[i+d[j]]) {
				o=0; for (int k=0; k<n; k++) o+=U[i+d[j]+d[k]];
				S[j]=o;
				if (o>O) didz=false; else if (o==O) lygus=true;
			}
			if (!didz) break;
		}
		if (didz)
			if (!lygus) {b[i]=2; M[1]++;} else{ //prapleciame rata
				didz=true; lygus=false;
				OD=0; for (int j=0; j<n; j++) OD+=U[i+D[j]];
				for (int j=0; j<n; j++){
					if (O==S[j]) {
						o=0; for (int k=0; k<n; k++) o+=U[i+d[j]+D[k]];
						if (o>OD) didz=false; else if (o==OD) lygus=true;
					}
					if (!didz) break;
				}
				if (didz) if (!lygus) {b[i]=3; M[2]++;}
						else {b[i]=-1; M[3]++;}
			} //end platesnio rato			
	} //o==P
 } //[U[i]>utrshld
 my.print(M,"max: M");
 int[] v = my.move(u);
 for (int i=w1; i<w2; i++) if (b[i]!=0){
	O=A;o=A;
	switch (b[i]){
		case 1: O=AB; o=AB; break; //lokalus maksimumas
		case 2: O=AG; o=AB; v[i+1+w]=Y; v[i-1-w]=Y; v[i-1+w]=Y; v[i+1-w]=Y; break; // po pirmo vidurkinimo ekstrem
		case 3: O=Y; o=Y; v[i+1+w]=AB; v[i-1-w]=AB; v[i-1+w]=AB; v[i+1-w]=AB; break;// po antro vidurkinimo ekstrem
		case -1: O=Y; o=Y; v[i+1+w]=o; v[i-1-w]=o; v[i-1+w]=o; v[i+1-w]=o; break;
	}
	v[i]=O; v[i+1]=o; v[i-1]=o; v[i+w]=o; v[i-w]=o;
 }
//for (int k=0; k<K2; k++)
//	if (b[i]==k+1) {U[i]=AB; U[i+kr[k]]=AB; U[i-kr[k]]=AB; break;}
 
 byte mima=0; if (minmode) mima=-128; else mima=127;
 for (int k=0; k<wh; k++) if (b[k]>0) for (int i=0; i<n; i++) bminmax[k+d[i]]=mima;

 int[] vm=my.move(v);
 imagem = cmp.createImage(new MemoryImageSource(w,h,vm, 0, w)); //kad galetume palyginti grupavimo rezultata
 v=grupuokekstr(b,U,v,w,h);
 return v;
}

// sugrupuok lokalius ekstremumus b - ekstr.zymes, u - duomenys, v u su pazymetais ekstr
public static int[] grupuokekstr(byte[] b, int[] u, int[] v, int w, int h){
 final int A=(255<<24),R=(255<<16),Gr=(255<<8),B=255,
	Y=A|R|Gr,AB=A|B,AM=A|Gr|B,AR=A|R,
	 C1=-(255<<6), C2=-(255<<7), C3=-(255<<8);
 int N=w*h;
// kryziaus formos aplinka
 int[] d=new int[]{1,w,-1,-w}; final int nd=4; if (nd!=d.length) my.error("nd!=d.length");
 int[] bnxt=new int[N],bprv=new int[N],	wnxt=new int[N], ii,jj;
 int[] c =new int[N],CC=new int[N], U = my.move(u), Vi=new int[N], cc; // c - grupes taskams, U - u kopija
 int C,G,l,kont=0,i0,j0,i1,j1,di,dj,L,Cl;
 int b0_=0,b_=b0_,w0_,w_,w1_;
 Vector V1=new Vector(),V2=new Vector();
 boolean saka1;
//	sudarome lokaliu ekstremumu grandine
 for (int i=1; i<N; i++) if (b[i]>0) {
	if (b0_==0) {b0_=i; b_=i;}
	bnxt[b_]=i; bprv[i]=b_; b_=i;
 }
 System.out.println("visi C>1:");
 b_=b0_; while (b_!=0){ // grupuojame aplink b_ ekstremuma
// paleidziame ish b_ banga
   c[0]=b_; U[c[0]]=C1; C=1;
   w0_=c[0]; w_=w0_; w1_=w_;
   while (w_!=0){
	for (int k=0; k<nd; k++){
	   l=w_+d[k];
	   if (U[l]>utrshld){
		U[l]=C1;
		wnxt[w1_]=l; w1_=l; //papildome banga
		if (b[l]>0){
			c[C]=l; C++;
			bnxt[bprv[l]]=bnxt[l]; bprv[bnxt[l]]=bprv[l]; //ismetame nuorodas l ekstremumo taska
		}
	   }
	} //for ... k=0;
	w_=wnxt[w_];
   } //w_!=0
   saka1=true;
   if (C>2){ // patikriname ar grupes taskai grupuojasi i neissisakojancia kreive
	//pradzioje ishsitriname banga, kad paleisti nauja
//	my.println("Triname..");
	w_=w0_; while (wnxt[w_]!=0) {w1_=wnxt[w_];wnxt[w_]=0;w_=w1_;}
	// paleidziame ish paskutinio ekstremumo tasko banga
	c[0]=c[C-1]; w0_=c[0]; if (U[w0_]!=C1) my.error("U[w0_]!=C1");
	U[w0_]=C2; G=1; w_=w0_; w1_=w_;
//	my.println("Banga ish galo ...");
	while (w_!=0){
	   for (int k=0; k<nd; k++){
        	l=w_+d[k];
        	if (U[l]==C1){
     		   U[l]=C2;
     		   wnxt[w1_]=l; w1_=l; //papildome banga
     		   if (b[l]>0){
     			c[G]=l; G++;
     	   	   }
        	}
	   } //for ... k=0;
   	   w_=wnxt[w_];
   	} //w_!=0
   	if (G!=C) my.error("G!=C");
	//vel ishsitriname banga, kad netrugdytu naujai
	w_=w0_; while (wnxt[w_]!=0) {w1_=wnxt[w_];wnxt[w_]=0;w_=w1_;}
	// tikriname ar tiketinas jungimasis i viena saka leisdami banga is priesingo galo
	w0_=c[G-1]; if (U[w0_]!=C2) my.error("U[w0_]!=C2");
	U[w0_]=C3; C=1; w_=w0_; w1_=w_;
//	my.println("Atvirkscia ...");
	while (w_!=0){
	   for (int k=0; k<nd; k++){
        	l=w_+d[k];
        	if (U[l]==C2){
     		   U[l]=C3;
     		   wnxt[w1_]=l; w1_=l; //papildome banga
     		   if (b[l]>0){	C++; if (c[G-C]!=l) saka1=false;}
        	}
	   } //for ... k=0;
   	   w_=wnxt[w_];
   	} //w_!=0
	//del viso pikto ishsitriname banga
	w_=w0_; while (wnxt[w_]!=0) {w1_=wnxt[w_];wnxt[w_]=0;w_=w1_;}
   	if (G!=C) my.error("saka1: G!=C");
	if (!saka1) C=-C;
   } //C>2
   CC[kont]=C; kont++;   
   b_=bnxt[b_];
   System.out.print(" "+C);
   if (C<0) C=-C;
   double[] aps;
// Vi neigiamais pazymime galinius taskus
   if (C==1) {V1.addElement(new int[]{c[0]}); Vi[c[0]]=-1; }
	else
     { //C>1 sujungiame lauzte
	cc=new int[C]; for (int k=0; k<C; k++) { cc[k]=c[k]; Vi[c[k]]=C;}
//	if ((saka1)&(C==2)){
	if (saka1){
	   V2.addElement(my.move(cc));
	   Vi[c[0]]=-Vi[c[0]]; Vi[c[C-1]]=-Vi[c[C-1]];
	   pazAtkarpa(v,AB,Math.min(1+C/5,2),AR,c,C,w);
//	   my.print(cc,"C=2?");
	   if (C>3){
//		aps=apskr.raskR(cc,C,w);
		aps=krastas.raskR2(cc,C,w);
		if (aps!=null){
		   int[] ar = apskr.arc(aps,cc[0],cc[C-1],w);
		   for (int ij=0; ij<ar.length; ij++)
			if ((ar[ij]>0)&(ar[ij]<wh)) v[ar[ij]]=my.Y;
		}
	   }
	}
	else {pazAtkarpa(v,AB,3,Y,c,C,w);//	   my.print(cc,"C>2?");
	}
   } //C>1
//   if (C>1)  System.out.print(" "+C);
 } //b_!=0
   System.out.println();
// meginame pratesti konturus

 int[] vc=my.move(v);
 imagec = cmp.createImage(new MemoryImageSource(w,h,vc, 0, w)); //kad galetume palyginti pratesimo darba

 int kiekPratesta=-1;
 int k0,k1,k2,kk,kc;
 int[] kur = new int[N];
 while (kiekPratesta!=0){ // kol prasitesia
   kiekPratesta=0;
   int N2=V2.size(); boolean[] galimaPratesti=new boolean[N2];
// pirma pasizymime kur galima pratesti
   for (int k=0; k<N2; k++) galimaPratesti[k]=pratesk(Vi,kur,(int[])(V2.elementAt(k)),w,h);
// kol kas prijungiu tik pavienius taskus, nes nepavienius nemoku apjungti
   for (int k=0; k<N2; k++) if (galimaPratesti[k]){
	k0=((int[])V2.elementAt(k))[0];
	if (kur[k0]>0) if (Vi[kur[k0]]==-1)
		{ papildyk1(Vi,V2,k,0,kur[k0],w,v); kiekPratesta++;}
	k1=((int[])(V2.elementAt(k))).length-1;
	k0=((int[])(V2.elementAt(k)))[k1];
	if (kur[k0]>0) if (Vi[kur[k0]]==-1) 
		{ papildyk1(Vi,V2,k,k1,kur[k0],w,v); kiekPratesta++;}
   }     
 }

 return v;
} //grupuokekstr

// papildome k-aji kontura 1-u kur
private static void papildyk1(int[] Vi, Vector V,int k,int k0, int kur, int W, int[] vv){
 int[] v=my.move((int[])(V.elementAt(k)));
 int n=v.length;
 int[] w = new int[n+1];
 if (k0>0) {
	for (int i=0; i<n; i++) w[i]=v[i]; w[n]=kur; Vi[w[n-1]]=n+1; Vi[w[n]]=-(n+1);
	pazAtkarpa(vv,my.AR,1,my.AG,w[n-1],w[n],W);
 }
  else {
	for (int i=1; i<=n; i++) w[i]=v[i-1]; w[0]=kur; Vi[w[1]]=n+1; Vi[w[0]]=-(n+1);
	pazAtkarpa(vv,my.AR,1,my.AG,w[1],w[0],W);
 }
 V.setElementAt(w,k);
} //papildyk1

// pazymek atkarpa v masyve nuo k0 iki k1 tasko C spalva
// jei R>0 - R spindulio atkarpos aplinkoje piesiama CR spalva
public static void pazAtkarpa(int[] v, int C, int R, int CR, int k0, int k1, int w){
 int i0=k0%w,j0=(k0-i0)/w,i1=k1%w,j1=(k1-i1)/w,di=i1-i0,dj=j1-j0,
	L=(int)Math.sqrt(di*di+dj*dj)+1,N=v.length,k;
 if (R>0){
 	for (int l=0; l<L; l++) {
	   for (int j=-R; j<=R; j++) if (j!=0) {
		k=i0+j+(l*di)/L+(j0+j+(l*dj)/L)*w;
		if ((k>=0)&(k<N)) v[k]=CR;
		k=k-2*j;
		if ((k>=0)&(k<N)) v[k]=CR;
	   }
	}
  }
 for (int l=0; l<L; l++) v[i0+(l*di)/L+(j0+(l*dj)/L)*w]=C;
 for (int l=-1; l<2; l++) {
	v[i0+l+(j0+l)*w]=my.AB; v[i0-l+(j0+l)*w]=my.AB;
	v[i1+l+(j1+l)*w]=my.AB; v[i1-l+(j1+l)*w]=my.AB;
	v[i0+(j0+l)*w]=my.AB; v[i0+l+j0*w]=my.AB;
	v[i1+(j1+l)*w]=my.AB; v[i1+l+j1*w]=my.AB;
 }
} //pazAtkarpa

// pazymek lauzte v masyve nuo c[0] iki c[end-1] tasko C spalva
// jei R>0 - R spindulio lauztes aplinkoje piesiama CR spalva
public static void pazAtkarpa(int[] v, int C, int R, int CR, int[] c, int Nc, int w){
 for (int k=0; k<Nc-1; k++) pazAtkarpa(v,C,R,CR,c[k],c[k+1],w);
} //pazAtkarpa

/* grazina true, jei bent ish vieno galo galima pratesti
 kuur[] masyve zymima reiksme kur galima pratesti is c konturo galu zymimu neigiamomis V[i] reiksmemis
 naudoja int [][] banga masyva
*/
public static boolean pratesk(int[] Vi, int[] kur, int[] c, int w, int h){
 boolean galimaPratesti=false;
 int n=c.length, l,k;
// uznuliname kur, kad atsinaujintu pratestinumo tikrinimas
// pvz., kai konturas vietoje 2 pasidare ilgesnis kur gali pakisti
 kur[c[0]]=0; kur[c[n-1]]=0;
 if (n<3) return galimaPratesti;
 k=c[0]; if (Vi[k]<0) {
	l=pratesk01(c[1],k,w,h,Vi);
	if (n>2) if (pratesk01(c[2],k,w,h,Vi)!=l) l=-1;
	if (l>0) {kur[k]=l; galimaPratesti=true;}
 }
 k=c[n-1]; if (Vi[k]<0) {
	l=pratesk01(c[n-2],k,w,h,Vi);
	if (n>2) if (pratesk01(c[n-3],k,w,h,Vi)!=l) l=-1;
	if (l>0) {kur[k]=l; galimaPratesti=true;}
 }
 return  galimaPratesti;
}

/* grazina:
 pratesiama ish k0 i k1
 nr indekso,  jei pasieke neigiama Vi
 -1 - jei pirma atsimuse - teigiama Vi
 -2 - jei nerado visai
 naudoja int [][] banga masyva
*/
public static int pratesk01(int k0, int k1, int w, int h, int[] Vi){
 int i0=k0%w,i1=k1%w,j0=(k0-i0)/w,j1=(k1-i1)/w, 
	wh=w*h, ij,l;
 if (Math.max(Math.abs(i1-i0),Math.abs(j1-j0))<=2) return -1;
 double fi=Math.atan2(j1-j0,i1-i0);
 int krpt=(int)Math.round(Krypciu*fi/(2*Math.PI)); if (krpt<0) krpt+=Krypciu;
 int n=banga[krpt].length;
 l=0; ij=-1;
 for (int k=0; k<n; k++){
   ij=k1+banga[krpt][k]; if ((ij>0)&(ij<wh)) l=Vi[ij];
   if (l!=0) break;
 }
 if (l>0) l=-1; else {if (l==0) l=-2; else l=ij;}
// if (l>0) my.print(new int[]{i0,j0,i1,j1,l%w,(l-l%w)/w},"pratsk:");
 return l;
} //pratesk01

public static double maxaverage(byte[] u, int w, int h, int L){
// u bloku L/2xL/2 127-u maksimalus vidurkis
 int wh=w*h, L2=L/2, L4=L2/2;
 double p,umx,umi,s,S;
 boolean[][] B = new boolean[L][L];
 int iB = 0, R2=L2*L2;
 for (int i=0; i<L; i++) for (int j=0; j<L; j++)
	if (((i-L2)*(i-L2)+(j-L2)*(j-L2))<=R2) { iB+=1; B[i][j]=true;} else B[i][j]=false;
 int I,J=(h%L)/2;
 S = 0;
 while (J+L<=h){
  I=(w%L)/2;
  while (I+L<=w) {
	umx=127-(int)u[I+L2+(L2+J)*w]; umi=umx; s=0;
	for (int i=0; i<L; i++) for (int j=0; j<L; j++)	if (B[i][j]){
		p = 127-(int)u[I+i+(j+J)*w];
		s+=p;
		if (umx<p) umx=p; else if (umi>p) umi=p;
	}
	s/=iB; if (S<s) S=s;
	I+=L;
  }
 J+=L; 
 }
 my.print((int) S,"maxaverage");
 return S;
}

// line tracking
public static byte[] maxlinetracing(byte[] l, byte[] u, int w, int h, int i0, int j0){
 
 int[] ii=new int[]{-w,-w+1,+1,w+1,w,w-1,-1,-w-1};
 int Ni=ii.length; int ic=i0+j0*w;
 if (Ni!=8) my.error("Ni!=8");

// susirandame lokalu ekstremuma shalia (i0,j0) tashko
 byte c=u[ic]; int im,im1,imo,im2;
 for (int k=0; k<K; k++){ //K kartu rekurentishkai ieskoti ekstr
	im=-1;
	for (int i=0; i<Ni; i++) if (c<u[ic+ii[i]]) { c=u[ic+ii[i]]; im=i;} 
	if (im==-1) break;
	ic=ic+ii[im];
 }

/*
// surandame greta lokalaus ekstremumo du tashku duodancius maksimalia suma
 int Ck=-1,ck,i2;
 for (int k=0; k<Ni; k++){
	ck=-256; 
	for (int i=3; i<6; i++) if (ck<b[ic+ii[(k+i)%Ni]]) // imame "priesingus" taskus
		{ i2=(k+i)%Ni; ck=b[ic+ii[i2]];}
	if (Ck<(ck+b[ic+ii[k]])) {Ck=ck+b[ic+ii[k]]; im1=k; im2=i2;}
 }
*/

// surandame lokalius maksimumus KR spindulio apskritime
 int kr=xR.length,M=0;
 int[] xy = new int[kr]; // lokaliu maksimumu taskai
 im1=ic+xR[kr-2]+yR[kr-2]*w; im=ic+xR[kr-1]+yR[kr-1]*w;
 int ko;
 for (int k=0; k<kr; k++){
	im2=ic+xR[k]+yR[k]*w;
	if ((u[im1]<u[im])&(u[im2]<=u[im])) {
		imo=im2; ko=k;
		while ((u[imo]==u[im])&(ko<k+kr)) { //prasukame lygius
			ko++; imo=ic+xR[ko%kr]+yR[ko%kr]*w;
		}
		ko=(k+kr-1+(ko-k)/2)%kr;
		xy[M]=ko;l[ic+xR[ko]+yR[ko]*w]=-128;
		M++;
	}
	im1=im; im=im2;
 }

// randame geriausia tasku pora apskritime;
 i0=ic%w; j0=(ic-i0)/w;
 int s,s1,s2,L, KR2=KR*KR, x0,y0,x1,y1,dx,dy;
 float t0,t,t1,D,C=(float)-256.0;
 im1=-1; im2=-1;
// my.print(M,"maxlinetracing M");
 for (int k=0; k<M; k++) {
    x0=i0+xR[xy[k]]; y0=j0+yR[xy[k]];
    for (int m=0; m<M; m++){
	dx=xR[xy[k]]-xR[xy[m]];dy=yR[xy[k]]-yR[xy[m]];
	if ((dx*dx+dy*dy)>KR2) // analizuojame ne per daug astrius tasku trejetus
 	   {
		x1=i0+xR[xy[m]]; y1=j0+yR[xy[m]];
		L=(int)(2*(Math.sqrt(xR[xy[k]]*xR[xy[k]]+yR[xy[k]]*yR[xy[k]])+
			Math.sqrt(xR[xy[m]]*xR[xy[m]]+yR[xy[m]]*yR[xy[m]])));
		D=(float)L;
		s1=256;s2=256;
		for (int i=0; i<=(L+1)/2; i++){
			t0=i/D; t=(float)0.5-t0; t1=1-t0;
			dx=(int)(2*(x0*t*t1+2*i0*t0*t1-x1*t0*t));
			dy=(int)(2*(y0*t*t1+2*j0*t0*t1-y1*t0*t));
//			s+=u[dx+dy*w];
			if (s1>u[dx+dy*w]) s1=u[dx+dy*w];
			t0=(L-i)/D; t=(float)0.5-t0; t1=1-t0;
			dx=(int)(2*(x0*t*t1+2*i0*t0*t1-x1*t0*t));
			dy=(int)(2*(y0*t*t1+2*j0*t0*t1-y1*t0*t));
//			s+=u[dx+dy*w];
			if (s2>u[dx+dy*w]) s2=u[dx+dy*w];
		}
		s=Math.max(s1,s2);
//		if (C<s/L) { C=s/L; im1=k; im2=m;}
		if (C<s) { C=s; im1=k; im2=m;}
	}
    }
 }
 if (im1!=-1) for (int k=im1; k<=im1; k++) {
    x0=i0+xR[xy[k]]; y0=j0+yR[xy[k]];
    for (int m=im2; m<=im2; m++){
	dx=xR[xy[k]]-xR[xy[m]];dy=yR[xy[k]]-yR[xy[m]];
	x1=i0+xR[xy[m]]; y1=j0+yR[xy[m]];
	L=(int)(2*(Math.sqrt(xR[xy[k]]*xR[xy[k]]+yR[xy[k]]*yR[xy[k]])+
		Math.sqrt(xR[xy[m]]*xR[xy[m]]+yR[xy[m]]*yR[xy[m]])));
//	my.print(M,"maxlinetracing L");
	D=(float)L;
	s=0;
	for (int i=0; i<=L; i++){
		t0=i/D; t=(float)0.5-t0; t1=1-t0;
		dx=(int)(2*(x0*t*t1+2*i0*t0*t1-x1*t0*t));
		dy=(int)(2*(y0*t*t1+2*j0*t0*t1-y1*t0*t));
		l[dx+dy*w]=-128;
	}
    }
   l[ic]=-128;l[ic+1]=-128;l[ic-1]=-128;l[ic+w]=-128;l[ic-w]=-128;
 } 
 return l;
} //maxlinetracing

public static byte[] minlinetracing(byte[] l, byte[] u, int w, int h, int i0, int j0){
 
 int[] ii=new int[]{-w,-w+1,+1,w+1,w,w-1,-1,-w-1};
 int Ni=ii.length; int ic=i0+j0*w;
 if (Ni!=8) my.error("Ni!=8");

// susirandame lokalu ekstremuma shalia (i0,j0) tashko
 byte c=u[ic]; int im,im1,imo,im2;
 for (int k=0; k<K; k++){ //K kartu rekurentishkai ieskoti ekstr
	im=-1;
	for (int i=0; i<Ni; i++) if (c>u[ic+ii[i]]) { c=u[ic+ii[i]]; im=i;} 
	if (im==-1) break;
	ic=ic+ii[im];
 }

/*
// surandame greta lokalaus ekstremumo du tashku duodancius maksimalia suma
 int Ck=-1,ck,i2;
 for (int k=0; k<Ni; k++){
	ck=-256; 
	for (int i=3; i<6; i++) if (ck<b[ic+ii[(k+i)%Ni]]) // imame "priesingus" taskus
		{ i2=(k+i)%Ni; ck=b[ic+ii[i2]];}
	if (Ck<(ck+b[ic+ii[k]])) {Ck=ck+b[ic+ii[k]]; im1=k; im2=i2;}
 }
*/

// surandame lokalius minimumus KR spindulio apskritime
 int kr=xR.length,M=0;
 int[] xy = new int[kr]; // lokaliu maksimumu taskai
 im1=ic+xR[kr-2]+yR[kr-2]*w; im=ic+xR[kr-1]+yR[kr-1]*w;
 int ko;
 for (int k=0; k<kr; k++){
	im2=ic+xR[k]+yR[k]*w;
	if ((u[im1]>u[im])&(u[im2]>=u[im])) {
		imo=im2; ko=k;
		while ((u[imo]==u[im])&(ko<k+kr)) { //prasukame lygius
			ko++; imo=ic+xR[ko%kr]+yR[ko%kr]*w;
		}
		ko=(k+kr-1+(ko-k)/2)%kr;
		xy[M]=ko;l[ic+xR[ko]+yR[ko]*w]=127;
		M++;
	}
	im1=im; im=im2;
 }

// randame geriausia tasku pora apskritime;
 i0=ic%w; j0=(ic-i0)/w;
 int s,s1,s2,L, KR2=KR*KR, x0,y0,x1,y1,dx,dy;
 float t0,t,t1,D,C=(float)256.0;
 im1=-1; im2=-1;
// my.print(M,"minlinetracing M");
 for (int k=0; k<M; k++) {
    x0=i0+xR[xy[k]]; y0=j0+yR[xy[k]];
    for (int m=0; m<M; m++){
	dx=xR[xy[k]]-xR[xy[m]];dy=yR[xy[k]]-yR[xy[m]];
	if ((dx*dx+dy*dy)>KR2) // analizuojame ne per daug astrius tasku trejetus
 	   {
		x1=i0+xR[xy[m]]; y1=j0+yR[xy[m]];
		L=(int)(2*(Math.sqrt(xR[xy[k]]*xR[xy[k]]+yR[xy[k]]*yR[xy[k]])+
			Math.sqrt(xR[xy[m]]*xR[xy[m]]+yR[xy[m]]*yR[xy[m]])));
		D=(float)L;
//		s=0;
		s1=-256;s2=-256;
		for (int i=0; i<=(L+1)/2; i++){
			t0=i/D; t=(float)0.5-t0; t1=1-t0;
			dx=(int)(2*(x0*t*t1+2*i0*t0*t1-x1*t0*t));
			dy=(int)(2*(y0*t*t1+2*j0*t0*t1-y1*t0*t));
//			s+=u[dx+dy*w];
			if (s1<u[dx+dy*w]) s1=u[dx+dy*w];
			t0=(L-i)/D; t=(float)0.5-t0; t1=1-t0;
			dx=(int)(2*(x0*t*t1+2*i0*t0*t1-x1*t0*t));
			dy=(int)(2*(y0*t*t1+2*j0*t0*t1-y1*t0*t));
//			s+=u[dx+dy*w];
			if (s2<u[dx+dy*w]) s2=u[dx+dy*w];
		}
		s=Math.min(s1,s2);
//		if (C>s/L) { C=s/L; im1=k; im2=m;}
		if (C>s) { C=s; im1=k; im2=m;}
	}
    }
 }
 if (im1!=-1) for (int k=im1; k<=im1; k++) {
    x0=i0+xR[xy[k]]; y0=j0+yR[xy[k]];
    for (int m=im2; m<=im2; m++){
	dx=xR[xy[k]]-xR[xy[m]];dy=yR[xy[k]]-yR[xy[m]];
	x1=i0+xR[xy[m]]; y1=j0+yR[xy[m]];
	L=(int)(2*(Math.sqrt(xR[xy[k]]*xR[xy[k]]+yR[xy[k]]*yR[xy[k]])+
		Math.sqrt(xR[xy[m]]*xR[xy[m]]+yR[xy[m]]*yR[xy[m]])));
//	my.print(M,"minlinetracing L");
	D=(float)L;
	s=0;
	for (int i=0; i<=L; i++){
		t0=i/D; t=(float)0.5-t0; t1=1-t0;
		dx=(int)(2*(x0*t*t1+2*i0*t0*t1-x1*t0*t));
		dy=(int)(2*(y0*t*t1+2*j0*t0*t1-y1*t0*t));
		l[dx+dy*w]=127;
	}
    }
   l[ic]=127;l[ic+1]=127;l[ic-1]=127;l[ic+w]=127;l[ic-w]=127;
 } 
 return l;
} //minlinetracing

 public static byte[] getSkeleton(byte[] U, int w, int h){
// Hilditch's algorithm
// http://jeff.cs.mcgill.ca/~godfried/teaching/projects97/azar/skeleton.html#algorithm
// nespejau http://www.caip.rutgers.edu/~ksen/work/niblack/machine.ppt
  int[] ii=new int[]{-w,-w+1,+1,w+1,w,w-1,-1,-w-1};
  int il=ii.length;
  int wh=w*h,l,s,s2,s4,v=1;
  byte[] u = new byte[wh];
  boolean ism=true;  
  for (int i=w+1; i<wh-w-1; i++) u[i]=U[i];
//  my.print(u,"uin");
  while (ism){
   ism = false;
   for (int i=w+1; i<wh-w-2; i++) if (u[i]<0) {
	s=0;
	for (int j=0; j<il; j++) if (u[i+ii[j]]<0) s|=(v<<j);
	if ((1<B[s])&(B[s]<7)&(A[s]==1)){
		s2=0; s4=0;
		if (i>2*w) for (int j=0; j<il; j++) { if (u[i-w+ii[j]]<0) s2|=(v<<j);}
			else s2=1;
		for (int j=0; j<il; j++) if (u[i+1+ii[j]]<0) s4|=(v<<j);
		if ((((u[i+ii[0]]<0)&(u[i+ii[2]]<0)&(u[i+ii[6]]<0))|(s2!=1))&
			(((u[i+ii[0]]<0)&(u[i+ii[2]]<0)&(u[i+ii[4]]<0))|(s4!=1)))
			{
			u[i]=127; ism=true;
		}
	}
   }
//   my.println("skel");
 }
// my.print(u,"uout");
 return u;
} 

 private void initAB(){
  int n = 256, L=8, v=1, w0,w1; 
  A = new byte[n]; B = new byte[n];
  for (int i=0; i<n; i++){
	w0=i&(v << L-1);
	for (int l=0; l<L; l++){
		w1=i&(v << l);
		if (w1!=0) {B[i]+=1; if (w0==0) A[i]+=1;}
		w0=w1;
	} 	
 } 
// my.print(A,"A"); my.print(B,"B");
}

 public static int[] getPixels(Image i){
	int a=(255<<24);
	int iw=i.getWidth(cmp);
	int ih=i.getHeight(cmp);
	int L=iw*ih,p,pmx,pmi;
	int[] pix = new int[L];
	System.out.println(Integer.toString(iw));
	System.out.println(Integer.toString(ih));
	PixelGrabber pg = new PixelGrabber(i, 0, 0, iw, ih, pix, 0, iw);
	try { pg.grabPixels();} 
	catch (InterruptedException e) {
		System.err.println("interrupted waiting for pixels!");
		return null;
        }
	if ((pg.getStatus() & ImageObserver.ABORT) != 0) {
		System.err.println("image fetch aborted or errored");
		return null;
        }	
//	byte[] b = new byte[L];
//	int M=-0x80000000, m=0x7fffffff; int B;
	pmx=-256; pmi=+256;
	for (int j=0; j<L; j++){
		p = pix[j] & 0xff;
		if (pmx<p) pmx=p; else if (pmi>p) pmi=p;
		pix[j] = p;
	}
	if (pmx==pmi) {pmx+=1;pmi-=1;}
	double q=255.0/((double)(pmx-pmi));
	int iih=-1, jb=w*(h-1)-1;
	for (int j=0; j<L; j++){
		if ((j%w)==0) iih+=1;
		if ((j<=w)|(j>=jb)|(iih==0)|(iih==h-1)) pix[j]=pmx;
		p = (int)Math.round(q*(pix[j]-pmi));
		pix[j] = a | (p<<16) | (p<<8) | p;
	}
//	System.out.println(Integer.toString(m));
//	System.out.println(Integer.toString(M));
	pix = byte2int(uzlopyk(int2byte(pix),w,h));
        return pix;
 }
 public byte[] upsample(byte[] b, int w, int h){
// Ishdestymas eilutemis?
	int W=2*w-1, H=2*h-1;
	int l=w*h, L=W*H;
	my.print(new int[]{b.length,l,L},"b.length,l,L");
	if (l!=b.length) my.print_exit("(l!=b.length)");
	byte[] U = new byte[L];
	int[] u = new int[l];
	int I=0, J=0, IJ=0, ij=0, M=0, m=0;
	for (int i=0; i<l; i++)	u[i]=((int)b[i])+128;
	for (int i=0; i<h; i++){
		for (int j=0; j<w; j++){
			IJ=I+2*j; ij=J+j;
			U[IJ]=(byte)(u[ij]-128); if (M<u[ij]) M=u[ij]; if (m>u[ij]) m=u[ij];
			if (j<w-1) U[IJ+1] = (byte)((u[ij]+u[ij+1])/2-128);
			if (i<h-1) U[IJ+W] = (byte)((u[ij]+u[ij+w])/2-128);
			if ((j<w-1)&(i<h-1)) U[IJ+1+W]=(byte)((u[ij]+u[ij+1]+u[ij+w]+u[ij+1+w]+1)/4-128);
		}
		I+=2*W; J+=w; 
	}
//	int B; for (int i=0; i<L; i++) { B=U[i] & 0xff; U[i]=(255 << 24) | (B << 16) | (B << 8) | B;}
	my.print(m,"m"); my.print(M,"M");
//	return int2byte(U);
	return U;
}//upsample

public static byte[] int2byte(int[] u){
 int l=u.length; byte[] b = new byte[l];
 for (int i=0; i<l; i++) b[i]=(byte)((u[i] & 0xff)-128);
 return b;
} //int2byte

public static float[] int2float(int[] u){
 int R=255<<16;
 int l=u.length; float[] v = new float[l];
 for (int i=0; i<l; i++) if ((u[i]&R)==R) v[i]=(float)(255-u[i]&0xff);
				else v[i]=-(float)(255-u[i]&0xff);
 return v;
} //int2float

public static int[] byte2int(byte[] b){
 int l=b.length; int[] u = new int[l];
 int j,a=(255 << 24);
 for (int i=0; i<l; i++){ j=((int) b[i]) + 128; u[i] = a | (j << 16) | (j << 8) | j;}
 return u;
} //byte2int

public static int[] float2int(float[] b){
 int l=b.length; int[] u = new int[l];
 int j;
 for (int i=0; i<l; i++){ j=((int) Math.max(-128,Math.min(127,b[i]))) + 128; u[i] = (255 << 24) | (j << 16) | (j << 8) | j;}
 return u;
} //byte2int

public static Image makeImagec(int[] u, int w, int h){
 if (!autocor) return cmp.createImage(new MemoryImageSource(w,h,u, 0, w));
// Suformuoja spalvota pav. sudaryta is pirmuju triju apskritimu koef
int A=255<<24,B=255, N=w*h;
float[] v=int2float(u);
double[] C0=new double[N], C1=new double[N], C2=new double[N];
int[] U=new int[N];
int[] xy = new int[KF]; double[][] V = new double[KF][2];
for (int i=0; i<KF; i++) xy[i]=xF[i]+yF[i]*w;
int l,ij,w1=RF+1,w2=w-RF-1,h1=RF+1,h2=h-RF-1;
boolean iswhite;
for (int j=h1; j<h2; j++) for (int i=w1; i<w2; i++){
 iswhite=true; ij=i+j*w;
 for (int k=0; k<KF; k++){
	l=ij+xy[k];
	iswhite&=(u[l]&B)==B;
	V[k][0]=(double)v[l];
	V[k][1]=0.0;
	}
 if (!iswhite) {
	V=fft.fft_1d(V);
	C0[ij]=V[0][0];
	C1[ij]=Math.sqrt(V[1][0]*V[1][0]+V[1][1]*V[1][1]);
	C2[ij]=Math.sqrt(V[2][0]*V[2][0]+V[2][1]*V[2][1]);
 }
}
 double q,Q0,Q1,Q2;
 q=my.min(C0); Q0=my.max(C0); if (Q0<-q) Q0=-q; if (Q0<my.eps) Q0=my.eps;
 Q1=my.max(C1); if (Q1<my.eps) Q1=my.eps;
 Q2=my.max(C2); if (Q2<my.eps) Q2=my.eps;
 for (int i=0; i<N; i++) U[i]=A|(((int)(127*C0[i]/Q0)+128)<<16)|
		(((int)(255*C1[i]/Q1))<<8)|((int)(255*C2[i]/Q2));

 return cmp.createImage(new MemoryImageSource(w,h,U, 0, w));
} //makeImagec

public void getDirection(){
 int wh = w*h;
 dmin = new int[wh]; dmax = new int[wh]; dir = new int[wh];
 int e,q,Q,ik,jk,i0,i1,j0,j1,ij,ij0,ij1;
 for (int i=0; i<w; i++){ ik=i*ka;
	for (int j=0; j<h; j++){ jk=j*ka; q=2*La*255; Q=0; ij=i+j*w;
		for (int n=0; n<na; n++) { e=0;
			ij0=Math.min(Math.max(0,ik+posw[n][0]),W-1)
				+W*Math.min(Math.max(0,jk+posh[n][0]),H-1);
			for (int l=1; l<2*La; l++) {
				ij1=Math.min(Math.max(0,ik+posw[n][l]),W-1)
					+W*Math.min(Math.max(0,jk+posh[n][l]),H-1);
				e+=Math.abs(uk[ij0]-uk[ij1]);
				ij0=ij1;
			}
			if (Q<e) { Q=e; dmax[ij]=n; }
			if (q>e) { q=e; dmin[ij]=n; }
		}		
	}	
 }	 
// d[w/2]=3; d[w/2+w]=3;
 for (int i=0; i<wh; i++){
	i1 = (S+dmax[i]-dmin[i])%S;
	i0=dmin[i]%S; dmin[i] = (255 << 24) | (spalva[i0].getRGB() & 0xffffff);
	i0=dmax[i]%S; dmax[i] = (255 << 24) | (spalva[i0].getRGB() & 0xffffff);
	if ((2<i1)&(i1<6)) dir[i]=dmin[i];
 }
// return d;
}
private static void initcs(){
	c = new double[na]; s = new double[na];
	for (int i=0; i<na; i++) { c[i] = Math.cos(i*pi/na); s[i] = Math.sin(i*pi/na);}
//	my.print(c,"c"); my.print(s,"s");
}

// sugeneruojame yvairiomis kryptimis 30 laisniu plociu sklindancias bangas
private static void initBangas(int w){
 int M=maxAtstumas,N=M*M,n;
 int[] ii = new int[N], jj = new int[N], ind;
 double[] a=new double[N];
 double p,co,si,fi,x,y;
 for (int k=0; k<Krypciu; k++){
   fi=2*Math.PI/Krypciu; co=Math.cos(k*fi); si=Math.sin(k*fi);
   n=0;
   for (int i=-M; i<=M; i++)		
     for (int j=-M; j<=M; j++) if ((i*co+j*si)>0) {
	x=i*co+j*si; y=Math.abs(-i*si+j*co);
	p=(x*x+y*y)*(1+y/x); // daugiklis ishskiria pagrindine krypty
	if ((2*y<=x)&(p<=N)){
		a[n]=p;	ii[n]=i; jj[n]=j;
		n++;
	}
     }
   ind=my.sort(my.move(a,n));
//   my.print(new double[]{a[ind[0]],Math.sqrt(a[ind[n-1]])},"a");
   banga[k]=new int[n];
   for (int i=0; i<n; i++) banga[k][i]=ii[ind[i]]+jj[ind[i]]*w;
 } //k
} //initbangas
	      

private static void initxFyF(){
	xF = new int[KF]; yF = new int[KF];
	for (int i=0; i<KF; i++) {
		xF[i] = (int)Math.round(RF*Math.cos(i*2*pi/KF));
		yF[i] = (int)Math.round(RF*Math.sin(i*2*pi/KF));
	}
}
private static void initxRyR(){
	int K=KR*K2pi; // imame K su atsarga, nes pakanka K~2*pi
	int[] x=new int[K],y=new int[K];
	double d=2*Math.PI/K;
	x[0]=KR; y[0]=0; int kr=0,i,j;
	for (int a=1; a<K; a++) {
		i=(int)Math.round(KR*Math.cos(a*d));
		j=(int)Math.round(KR*Math.sin(a*d));
		if ((i!=x[kr])|(j!=y[kr])) {kr++;x[kr]=i;y[kr]=j;}
	}
	if ((x[kr]!=x[0])|(y[kr]!=y[0])) my.error("(x[kr]!=x[0])|(y[kr]!=y[0])");
	xR=new int[kr]; yR=new int[kr];
	for (int a=0; a<kr; a++) { xR[a]=x[a]; yR[a]=y[a];}
	my.print(kr,"xR,yR length");
}

public static void initposl(){
	posl = new int[na][2*La]; posw = new int[na][2*La]; posh = new int[na][2*La];
	for (int i=0; i<na; i++){
		double ca=c[i], sa=s[i];  
		for (int l=-La; l<La; l++){
			posw[i][l+La] = (int) Math.round(ka*l*ca);
			posh[i][l+La] = (int) Math.round(ka*l*sa);
			posl[i][l+La] = posw[i][l+La]+W*posh[i][l+La];
		}
	}
}
private void initcolors(){
	spalva = new Color[]{Color.red,Color.green,Color.blue,Color.orange,Color.yellow,Color.cyan,Color.magenta,Color.black};
	S = spalva.length;
	int[] spalvaA = new int[S], 
		spalvaR = new int[S], spalvaG = new int[S], spalvaB = new int[S];
	for (int i=0; i<S; i++) { Color c=spalva[i];
		spalvaA[i]=255; spalvaR[i]=c.getRed();
		spalvaG[i]=c.getGreen(); spalvaB[i]=c.getBlue();
	}
}

} //DrawImage

class fltPanel extends Panel{

    Checkbox[] c;
	fltPanelMain fltpanelm;
	fltPanel1 fltpanelr;
//	fltPanel2 fltpanels;
//	fltPanel3 fltsspanelr;
//	ClassControls classcontrols;
//	Choice ec;
	
//    public ClassPanel(EKGCanvas canvas, EKGParamPanel ekgparampanel) //throws IOException 
    public fltPanel(Canvas canvas) //throws IOException 
	{
//	this.canvas = canvas;
	setLayout(new BorderLayout(20,5));
//	add("Center", canvas);
//	add("West", fltpanelk = new fltPanel1());
	add("Center", fltpanelm = new fltPanelMain());
//	add("East", fltpanels = new fltPanel2());
	add("South", fltpanelr = new fltPanel1(fltpanelm));
//	classcontrols = new ClassControls(canvas, canvas.ekg, canvas.ptca,classpanelk,classpanels, classpanelr);
//	add("South", classcontrols);
//        c = classpanelk.c;
//        ec = classpanelk.ec;
   } //fltPanel
} //fltPanel

class fltPanel1 extends Panel implements ActionListener, ItemListener
	{
 fltPanelMain fltpanelmain;
 public static float[] uDif;
 public static byte[] ubw, uSkel, bmex;
 public static int[] umex,uekst;
 public static Image imageDif;
 public static int invstep;
 static int w,h,wh;
 boolean usechooser = true;
    TextField kelias;
    TextField rtkelias;
    Checkbox bs;

    Choice vpc;
    Choice vks;
    static Choice priesaga;
    public static TextField vardas;
    public static TextField La,dB;
    Button difuzija = null;
    Button idifuzija = null;
    Button piesk = null;
    Button Open = null;
    static Button kitas = null;
	JFileChooser chooser = null;
	ExampleFileFilter filter = null;
    public static Button autosegm = null;
    public fltPanel1(fltPanelMain fltpanelmain) {
	this.fltpanelmain = fltpanelmain;
	w = DrawImage.w; h = DrawImage.h; wh=w*h;
	invstep = DrawImage.invstep;
	piesk = new Button("Piesk");
	piesk.addActionListener(this); add(piesk);
	difuzija = new Button("Difuzija");
	difuzija.addActionListener(this); add(difuzija);
	idifuzija = new Button("Idifuzija");
	idifuzija.addActionListener(this); add(idifuzija);

	add(vardas = new TextField(DrawImage.ImageFile, 4));
	Open = new Button("Failas");
	Open.addActionListener(this);
//	if (!EKGConst.usechooser) Open.disable();
	add(Open);

	filter = new ExampleFileFilter();

	if (usechooser){	
		chooser = new JFileChooser(DrawImage.kelias,FileSystemView.getFileSystemView());
	}
    // Note: source for ExampleFileFilter can be found in FileChooserDemo,
    // under the demo/jfc directory in the Java 2 SDK, Standard Edition.

	filter = new ExampleFileFilter();
// isjungiau, nes pakimba
//		filter.addExtension("*.");
//	filter.addExtension(" ");
//	filter.addExtension(".");
			filter.addExtension("gif");
//	filter.addExtension("out");
	filter.setDescription("gif - pirshto antspaudo failas");

	if (usechooser){ chooser.setFileFilter(filter);}

//	add(La = new TextField(Double.toString(DrawImage.La),3));
	add(La = new TextField(Integer.toString(DrawImage.La),3));
	add(new Label("La"));
	add(dB = new TextField(Integer.toString(fft.DB),3));
	add(new Label("dB"));

    }

    public void actionPerformed(ActionEvent ev) {
	my.println("act");
	difuzija.disable();
	String label = ev.getActionCommand();
//	DrawImage.trsh = Double.parseDouble(trsh.getText());
	int newLa = Integer.parseInt(La.getText());
	if (newLa!=DrawImage.La){
		DrawImage.La=newLa; DrawImage.initposl();
	}

	fft.DB = Integer.parseInt(dB.getText());
	if (label.equalsIgnoreCase("difuzija")|label.equalsIgnoreCase("idifuzija")){
		boolean inverse=label.equalsIgnoreCase("idifuzija");
		my.println("Difuzija");
		float[] u = new float[wh];
		for (int i=0; i<wh; i++) u[i]=uDif[i];
		int l; float d;
		for (int i=1; i<w-1; i++) for (int j=1;j<h-1; j++){
			l = i+j*w;
//			if ((u[l]!=-128)&(u[l]!=127))
			{
				d = uDif[l-1]+uDif[l+1]+uDif[l-w]+uDif[l+w]-4*uDif[l];
				if (inverse) d=-d;
				u[l]=Math.max(-128,Math.min(127,uDif[l]+d/invstep));
			}				
		}
		for (int i=0; i<wh; i++) uDif[i]=u[i];
		imageDif = createImage(new MemoryImageSource(w,h,DrawImage.float2int(uDif),0,w));
		fltpanelmain.repaint();
	}
	if (label.equalsIgnoreCase("failas")){
		int returnVal = chooser.showOpenDialog(null);
		if (returnVal == JFileChooser.APPROVE_OPTION) {
			String S = chooser.getSelectedFile().getName();

//			if (S.indexOf(EKGConst.EKG_1priesaga)>=0)
//				priesaga.select(1);
//			else priesaga.select(0);
			int tsk = S.indexOf(".");
			if (tsk>0) S = S.substring(0,tsk);
			my.println("Selected file: "+S);
//			vardas.setText(S);
		DrawImage.openImage(S);
		fltpanelmain.repaint();
		}
	} // "failas"
	if (label.equalsIgnoreCase("piesk")){
		DrawImage.openImage(vardas.getText());
		fltpanelmain.repaint();
	} // "piesk"
	difuzija.enable();
    }
    public void itemStateChanged(ItemEvent ev) {
	my.println("statechng");
	int i=1;
    }

} //fltPanel1

class fltPanelMain extends Panel// implements ActionListener, ItemListener
{
 static int w,h;
 public fltPanelMain(){
	w = DrawImage.w; h = DrawImage.h;
 }
 public void paint(Graphics g){
	my.println("fltPanelMain.paint");
/*
	g.drawImage(DrawImage.cmp.createImage(new MemoryImageSource(w,h,fltPanel1.umex,0,w)), 10, 10, DrawImage.cmp);
	g.drawImage(DrawImage.cmp.createImage(new MemoryImageSource(w,h,DrawImage.byte2int(fltPanel1.bmex),0,w)), 20+w, 10, DrawImage.cmp);
//	g.drawImage(DrawImage.cmp.createImage(new MemoryImageSource(w,h,DrawImage.byte2int(fltPanel1.ubw),0,w)), 10, 20+h, DrawImage.cmp);
//	g.drawImage(DrawImage.cmp.createImage(new MemoryImageSource(w,h,DrawImage.byte2int(fltPanel1.uSkel),0,w)), 20+w, 20+h, DrawImage.cmp);
	g.drawImage(DrawImage.cmp.createImage(new MemoryImageSource(w,h,fltPanel1.uekst,0,w)), 10, 20+h, DrawImage.cmp);
	g.drawImage(fltPanel1.imageDif, 20+w, 20+h, this);
*/
	drawImage1(g,"umex",fltPanel1.umex,w,h,10,10,DrawImage.cmp);
	drawImage1(g,"bmex",fltPanel1.bmex,w,h,20+w,10,DrawImage.cmp);
	drawImage1(g,"uekst",fltPanel1.uekst,w,h,10,20+h,DrawImage.cmp);
	drawImage1(g,"imageDif",fltPanel1.imageDif,w,h,20+w,20+h,this);
	drawImage1(g,"imagec",DrawImage.imagec,w,h,30+2*w,10,this);
	drawImage1(g,"imagem",DrawImage.imagem,w,h,30+2*w,20+h,this);
 }
 public void drawImage1(Graphics g, String title,int[] u, int w, int h, int i0, int j0,Component c){
	g.drawImage(DrawImage.cmp.createImage(new MemoryImageSource(w,h,u,0,w)), i0, j0, c);
	drawImageTitle(g,title,w,h,i0,j0);
 }
 public void drawImage1(Graphics g, String title,byte[] u, int w, int h, int i0, int j0,Component c){
	g.drawImage(DrawImage.cmp.createImage(new MemoryImageSource(w,h,DrawImage.byte2int(u),0,w)), i0, j0, c);
	drawImageTitle(g,title,w,h,i0,j0);
 }
 public void drawImage1(Graphics g, String title,Image img, int w, int h, int i0, int j0,Component c){
	g.drawImage(img, i0, j0, c);
	drawImageTitle(g,title,w,h+10,i0,j0);
 }
 public void drawImageTitle(Graphics g, String title, int w, int h, int i0, int j0){
	g.drawString(title,i0,j0+h);
 }
} //fltPanelMain
