package com.akjava.gwt.inpaint.client;
import static com.google.common.base.Preconditions.checkArgument;
import java.util.List;
import com.akjava.gwt.html5.client.file.Uint8Array;
import com.akjava.gwt.inpaint.client.InPaint;
import com.akjava.gwt.lib.client.CanvasUtils;
import com.akjava.gwt.lib.client.ImageElementUtils;
import com.akjava.gwt.lib.client.LogUtils;
import com.akjava.gwt.lib.client.experimental.ImageDataUtils;
import com.akjava.lib.common.utils.Benchmark;
import com.akjava.lib.common.utils.ColorUtils;
import com.google.gwt.canvas.client.Canvas;
import com.google.gwt.canvas.dom.client.ImageData;
import com.google.gwt.dom.client.ImageElement;
public class InpaintEngine {
//do async by yourself
private Canvas sharedCanvas;
public InpaintEngine(){
sharedCanvas=Canvas.createIfSupported();
}
public static interface InpaintListener{
public void createMixedImage(ImageData imageData);
public void createInpaintImage(ImageData imageData);//cutted mask
public void createGreyScaleMaks(ImageData imageData);
public void createInpainteMaks(ImageData imageData);
}
public static int INPAINT_MARGIN=2;
public void doInpaint(ImageElement element,int radius,final ImageElement maskImage,InpaintListener listener){
checkArgument(element.getWidth()!=0 && element.getHeight()!=0,"doInpaint: image element maybe need load");
checkArgument(maskImage.getWidth()!=0 && maskImage.getHeight()!=0,"doInpaint: maskImage element maybe need load");
Benchmark.start("total");
//resultPanel.add(new Image(element.getSrc()));
//use edge case
//INPAINT_MARGIN = 2;//there are inpaint problem.so it's better expand
Benchmark.start("expand");
//canvas and sharedCanvas is same
ImageElementUtils.copytoCanvasWithMargin(element, sharedCanvas,true,INPAINT_MARGIN,true);
ImageData expandedImageData=CanvasUtils.getImageData(sharedCanvas);
Benchmark.endAndLog("expand");
Uint8Array array=null;
//Uint8Array grayByte=null;
//created by maskData
//Uint8Array merged=createMaskData(sharedCanvas,maskDatas);
/*
Uint8Array expanded=InPaint.expandMaskByte(merged, imageData.getWidth(),data.getExpand());
createAndInsertImage(expanded,resultPanel);
grayByte=InPaint.expandMaskByteAsGray(expanded, imageData.getWidth(),data.getFade());
*/
ImageElementUtils.copytoCanvasWithMargin(maskImage, sharedCanvas,true,INPAINT_MARGIN,true);
Canvas grayscale=CanvasUtils.convertToGrayScale(sharedCanvas, null);
CanvasUtils.copyTo(grayscale, sharedCanvas);
ImageData maskData=CanvasUtils.getImageData(sharedCanvas);
//CanvasUtils.copyTo(maskData,sharedCanvas);
//String dataUrl=sharedCanvas.toDataUrl();
listener.createGreyScaleMaks(maskData);
Uint8Array newByte=InPaint.createMaskByColor(maskData, 0,0,0,false);//not 0 is mask
//return 0 or 1
array=newByte;
Benchmark.start("inpaint");
InPaint.inpaint(expandedImageData, array, radius);
//somehow(maybe transparent problem) expanded to result should keep same size
sharedCanvas.setCoordinateSpaceWidth(expandedImageData.getWidth()-4);
sharedCanvas.setCoordinateSpaceHeight(expandedImageData.getHeight()-4);
sharedCanvas.getContext2d().putImageData(expandedImageData,-2,-2);
//CanvasUtils.copyTo(imageData,sharedCanvas);
//CanvasUtils.copyTo(imageData,sharedCanvas);
String inpaintDataUrl=sharedCanvas.toDataUrl();
listener.createInpaintImage(ImageDataUtils.copyFrom(sharedCanvas));
//createAndInsertImage use sizes
sharedCanvas.setCoordinateSpaceWidth(expandedImageData.getWidth());
sharedCanvas.setCoordinateSpaceHeight(expandedImageData.getHeight());
//for support mergin
//create grayscale later
Uint8Array drawByte=Uint8Array.createUint8(newByte.length());
//better to do last ?
for(int i=0;i<newByte.length();i++){
drawByte.set(i, newByte.get(i)*255);
}
ImageData maskData2=CanvasUtils.createSameSizeImageData(sharedCanvas);
//LogUtils.log(maskByte.length()+","+maskData.getWidth()+"x"+maskData.getHeight());
//InPaint.createImageDataFromMask(maskData,maskByte,0,0,0,255,true);//for black & white
InPaint.createImageDataFromMaskAsGray(maskData2,drawByte);
CanvasUtils.copyTo(maskData2,sharedCanvas);
listener.createInpainteMaks(ImageDataUtils.copyFrom(sharedCanvas));
//createAndInsertImage(drawByte,inpaintMaskPanel);
Benchmark.endAndLog("inpaint");
Benchmark.start("mix");
CanvasUtils.copyTo(expandedImageData,sharedCanvas);//sharedcanvas broken by last create-image
ImageData paintedData=CanvasUtils.getImageData(sharedCanvas);
CanvasUtils.drawImage(sharedCanvas,element,INPAINT_MARGIN,INPAINT_MARGIN);
CanvasUtils.copyFromCanvasRedToImageDataAlpha(grayscale,paintedData);
Canvas paintedCanvas=CanvasUtils.createCanvas(null, paintedData);
CanvasUtils.drawImage(sharedCanvas,paintedCanvas);
Benchmark.endAndLog("mix");
//cut off margin
//String lastImage=CanvasUtils.toDataUrl(sharedCanvas, sharedCanvas, margin, margin, sharedCanvas.getCoordinateSpaceWidth()-margin*2, sharedCanvas.getCoordinateSpaceHeight()-margin*2);
listener.createMixedImage(sharedCanvas.getContext2d().getImageData(INPAINT_MARGIN, INPAINT_MARGIN, sharedCanvas.getCoordinateSpaceWidth()-INPAINT_MARGIN*2, sharedCanvas.getCoordinateSpaceHeight()-INPAINT_MARGIN*2));
Benchmark.endAndLog("total");
}
/**
*
* @param element must loaded
* @param radius
* @param maskDatas
* @param listener
*/
public void doInpaint(ImageElement element,int radius,List<MaskData> maskDatas,InpaintListener listener){
checkArgument(element.getWidth()!=0 && element.getHeight()!=0,"doInpaint:0 image element maybe need load");
Benchmark.start("total");
//resultPanel.add(new Image(element.getSrc()));
//use edge case
int margin=2;
Benchmark.start("expand");
//canvas and sharedCanvas is same
ImageElementUtils.copytoCanvasWithMargin(element, sharedCanvas,true,margin,true);
ImageData expandedImageData=CanvasUtils.getImageData(sharedCanvas,true);
Benchmark.endAndLog("expand");
Uint8Array array=null;
//Uint8Array grayByte=null;
//created by maskData
Uint8Array mergedGrayScale=createMaskData(sharedCanvas,maskDatas);
/*
Uint8Array expanded=InPaint.expandMaskByte(merged, imageData.getWidth(),data.getExpand());
createAndInsertImage(expanded,resultPanel);
grayByte=InPaint.expandMaskByteAsGray(expanded, imageData.getWidth(),data.getFade());
*/
ImageData maskData=CanvasUtils.getImageData(sharedCanvas,false);
InPaint.createImageDataFromMaskAsGray(maskData,mergedGrayScale);
CanvasUtils.copyTo(maskData,sharedCanvas);
//String dataUrl=sharedCanvas.toDataUrl();
listener.createGreyScaleMaks(ImageDataUtils.copyFrom(sharedCanvas));
Uint8Array newByte=InPaint.createMaskByColor(maskData, 0,0,0,false);//not 0 is mask
//return 0 or 1
array=newByte;
Benchmark.start("inpaint");
InPaint.inpaint(expandedImageData, array, radius);
//somehow(maybe transparent problem) expanded to result should keep same size
sharedCanvas.setCoordinateSpaceWidth(expandedImageData.getWidth()-4);
sharedCanvas.setCoordinateSpaceHeight(expandedImageData.getHeight()-4);
sharedCanvas.getContext2d().putImageData(expandedImageData,-2,-2);
//CanvasUtils.copyTo(imageData,sharedCanvas);
//CanvasUtils.copyTo(imageData,sharedCanvas);
String inpaintDataUrl=sharedCanvas.toDataUrl();
listener.createInpaintImage(ImageDataUtils.copyFrom(sharedCanvas));
//createAndInsertImage use sizes
sharedCanvas.setCoordinateSpaceWidth(expandedImageData.getWidth());
sharedCanvas.setCoordinateSpaceHeight(expandedImageData.getHeight());
//for support mergin
//create grayscale later
Uint8Array drawByte=Uint8Array.createUint8(newByte.length());
//better to do last ?
for(int i=0;i<newByte.length();i++){
drawByte.set(i, newByte.get(i)*255);
}
ImageData maskData2=CanvasUtils.createSameSizeImageData(sharedCanvas);
//LogUtils.log(maskByte.length()+","+maskData.getWidth()+"x"+maskData.getHeight());
//InPaint.createImageDataFromMask(maskData,maskByte,0,0,0,255,true);//for black & white
InPaint.createImageDataFromMaskAsGray(maskData2,drawByte);
CanvasUtils.copyTo(maskData2,sharedCanvas);
listener.createInpainteMaks(ImageDataUtils.copyFrom(sharedCanvas));
//createAndInsertImage(drawByte,inpaintMaskPanel);
Benchmark.endAndLog("inpaint");
Benchmark.start("mix");
CanvasUtils.copyTo(expandedImageData,sharedCanvas);//sharedcanvas broken by last create-image
ImageData paintedData=CanvasUtils.getImageData(sharedCanvas, true);
CanvasUtils.drawImage(sharedCanvas,element,margin,margin);
if(mergedGrayScale!=null){
CanvasUtils.copyAlpha(paintedData,mergedGrayScale);
Canvas paintedCanvas=CanvasUtils.createCanvas(null, paintedData);
CanvasUtils.drawImage(sharedCanvas,paintedCanvas);
}
//cut off margin
//String lastImage=CanvasUtils.toDataUrl(sharedCanvas, sharedCanvas, margin, margin, sharedCanvas.getCoordinateSpaceWidth()-margin*2, sharedCanvas.getCoordinateSpaceHeight()-margin*2);
listener.createMixedImage(sharedCanvas.getContext2d().getImageData(margin, margin, sharedCanvas.getCoordinateSpaceWidth()-margin*2, sharedCanvas.getCoordinateSpaceHeight()-margin*2));
}
private Uint8Array createMaskData(final Canvas canvas,List<MaskData> maskDatas){
//LogUtils.log(maskData.toString());
//ImageElementUtils.copytoCanvas(element, sharedCanvas);
ImageData imageData=CanvasUtils.getImageData(canvas,true);
Uint8Array merged=null;
for(MaskData maskData:maskDatas){
Uint8Array bt;
Uint8Array grayByte;
if(maskData.isTransparent()){
bt=InPaint.createMaskByAlpha(imageData);
}else{
int rgb[]=ColorUtils.toRGB(maskData.getColor());
if(maskData.isSimilarColor()){
bt=InPaint.createMaskBySimilarColor(imageData, rgb[0], rgb[1], rgb[2],maskData.getMaxLength());
}else{
bt=InPaint.createMaskByColor(imageData, rgb[0], rgb[1], rgb[2]);
}
}
//LogUtils.log("bt:"+bt.length());
Uint8Array expanded=InPaint.expandMaskByte(bt, imageData.getWidth(),maskData.getExpand());
//LogUtils.log("expanded:"+expanded.length());
grayByte=InPaint.expandMaskByteAsGray(expanded, imageData.getWidth(),maskData.getFade());
//LogUtils.log("grayByte:"+grayByte.length());
if(merged==null){
merged=grayByte;
}else{
merge(merged,grayByte);
}
}
//LogUtils.log("merged:"+merged.length());
//createAndInsertImage(merged,resultPanel);
return merged;
}
public void merge(Uint8Array array1,Uint8Array array2){
if(array1.length()!=array2.length()){
throw new IllegalArgumentException("not same length");
}
for(int i=0;i<array1.length();i++){
int v1=array1.get(i);
int v2=array2.get(i);
if(v2>v1){
array1.set(i, v2);//over write
}
}
}
}