import std.stdio,std.conv, std.algorithm, std.container,std.array,std.range,std.string,std.typecons,std.numeric,std.math,std.random,std.functional;

void main(){
	for(int w,h ; read(w,h),w|h ; ){		
		auto land = readAciiMap(w,h);
		int B,W;
		land.DFS!(char)( DPos , '.'
			,(Pos p,ref char state){
				if(land[p] == 'W' || land[p] == 'B'){
					if(state == '.') state = land[p];
					else if(state != land[p] ) state = 'X';
					return false;
				} 
				return true;
			},(Pos[] set,char state){
				if(state == 'B') B += set.length;
				if(state == 'W') W += set.length;
				return true;
			});
		writeln(B," ",W);
	}
}


int   read(){int n;read(n);return n;}
void  read(T...)  (auto ref T args){mixin(readMixin);}
void  readArray(T)(auto ref T args){mixin(readMixin);}
const readMixin = q{ 
	auto line = readln().split();
	foreach(i,ref arg; args){ 
		if (i >= line.length) return;
		arg = line[i].to!(typeof(arg));
	}
};
void times(int n,void delegate() stmt){ foreach(i;0..n)stmt();}
void Swap(T)(ref T a,ref T b){T t = a;a = b;b = t;}

const DPos = [Pos(1,0),Pos(0,1),Pos(-1,0),Pos(0,-1)];
struct Pos{ 
	int x,y,z;
	bool inRange(int w,int h,int Z=1){
		return x>=0 && y >= 0 && z >=0 && x < w && y < h && z < Z;
	}
	Pos opBinary(string op)(Pos rhs) {
		return Pos ( mixin( "x"~op~"rhs.x" ),
					 mixin( "y"~op~"rhs.y" ),
					mixin( "z"~op~"rhs.z" ));
	}
	void opOpAssign(string op)(Pos lhs) {
		mixin("x"~op~"=lhs.x;");
		mixin("y"~op~"=lhs.y;");
		mixin("z"~op~"=lhs.z;");
	}
}

struct Matrix(T){
	import std.conv;
	private T[][] data;
	this(int w,int h){ data = new T[][](w,h);}
	this(ref T[][] data_){ data = data_;}
	T opIndexAssign(T v, size_t x, size_t y){return data[x][y] = v;}
	T opIndexAssign(T v, Pos p){return opIndexAssign(v,p.x,p.y);}
	ref T opIndex(size_t x, size_t y){return data[x][y];}
	ref T opIndex(Pos p){return opIndex(p.x,p.y);}
	ref T opCall(Pos p){return data[p.x][p.y];}
	@property int W()const{return cast(int)data.length;}	
	@property int H()const{return cast(int)data[0].length;}	
	auto X(int x){T[]res;foreach(y;0..H())res ~= data[x][y];return res; }
	auto Y(int y){T[]res;foreach(x;0..W())res ~= data[x][y];return res; }
	void setX(T[] vec, int x){foreach(int y,v;vec)data[x][y] = v; }
	void setY(T[] vec, int y){foreach(int x,v;vec)data[x][y] = v; }
	void toString(scope void delegate(const(char)[]) sink) const{
		foreach(y;0..H()) {
			foreach(x;0..W())sink(data[x][y].to!string);
			if(y != H()-1)sink("\n");
		}
    }
	bool DFS(S)(const Pos[] nexts, S initState
				,bool delegate(Pos,ref S) dfsContinue
				,bool delegate(Pos[],S) searchNextSet){
		auto seeked = Matrix!bool(W(),H());
		void dfs(Pos p,ref Pos[] set,ref S state){ 
			if(!p.inRange(W(),H())) return;
			if(seeked[p]) return;
			if(!dfsContinue(p,state))return;
			set ~= p;
			seeked[p] = true;
			foreach(n;nexts) dfs(p+n,set,state);
		}
		foreach(y;0..H()){
			foreach(x;0..W()){
				Pos[] set;
				S state = initState;
				dfs(Pos(x,y),set,state);
				if(!searchNextSet(set,state)) return false;
			}
		}
		return true;
	}
}

auto  readAciiMap(int w,int h){ 
	import std.stdio,std.string;
	auto M=new char[][](w,h);
	foreach(y;0..h) foreach(int x,c;readln().chomp) M[x][y] = c;		
	return Matrix!(char)(M);
}

auto  readMatrix(int w,int h){
	import std.stdio,std.string,std.conv;
	auto M=new int[][](w,h);
	foreach(y;0..h) foreach(int x,c;readln().split()) M[x][y] = c.to!int;
	return Matrix!(int)(M);
}