1dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford/*
2dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford * Copyright (C) 2015 The Android Open Source Project
3dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford *
4dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford * Licensed under the Apache License, Version 2.0 (the "License");
5dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford * you may not use this file except in compliance with the License.
6dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford * You may obtain a copy of the License at
7dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford *
8dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford *      http://www.apache.org/licenses/LICENSE-2.0
9dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford *
10dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford * Unless required by applicable law or agreed to in writing, software
11dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford * distributed under the License is distributed on an "AS IS" BASIS,
12dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford * See the License for the specific language governing permissions and
14dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford * limitations under the License.
15dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford */
16dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford
17dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hofordpackage rs.example.android.com.healingbrush;
18dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford
19dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hofordimport android.graphics.Bitmap;
20dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hofordimport android.graphics.Canvas;
21dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hofordimport android.graphics.Color;
22dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hofordimport android.graphics.Paint;
23dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hofordimport android.graphics.Path;
24dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hofordimport android.graphics.Rect;
25dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hofordimport android.support.v8.renderscript.Allocation;
26dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hofordimport android.support.v8.renderscript.Element;
27dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hofordimport android.support.v8.renderscript.RenderScript;
28dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hofordimport android.support.v8.renderscript.Script;
29dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hofordimport android.support.v8.renderscript.Type;
30dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hofordimport android.util.Log;
31dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford
32dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hofordimport com.example.android.rs.sample.ScriptC_healing;
33dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford
34dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hofordpublic class Healing {
35dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford    private static final String TAG = "Healing";
36dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford    Rect mRoiBounds; // bounding box of the ROI
37dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford    float[] mPointsXY; // polygon point in original image coordnates
38dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford    int mCutOffsetX; // image coords of the cut  (mPointsXY - mPasteOffX + mCutOffsetX)
39dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford    int mCutOffsetY; // image coords of the cut (mPointsXY - mPasteOffY + mCutOffsetY)
40dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford    Bitmap mUndoBitmap;
41dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford
42dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford    public Healing(Rect roiBounds, float[] pointsXY, int cutOffsetX, int cutOffsetY) {
43dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford        mRoiBounds = roiBounds;
44dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford        mPointsXY = pointsXY;
45dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford        mCutOffsetX = cutOffsetX;
46dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford        mCutOffsetY = cutOffsetY;
47dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford    }
48dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford
49dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford    private static Bitmap buildMask(Rect rec, float[] xy) {
50dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford        Bitmap bitmap = Bitmap.createBitmap(rec.width(), rec.height(), Bitmap.Config.ALPHA_8);
51dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford
52dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford        Canvas c = new Canvas(bitmap);
53dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford        Paint paint = new Paint();
54dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford        paint.setStyle(Paint.Style.FILL);
55dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford        paint.setColor(Color.BLACK);
56dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford        Path path = new Path();
57dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford        for (int i = 0; i < xy.length; i += 2) {
58dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford            if (i == 0) {
59dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford                path.moveTo(xy[i] - rec.left, xy[i + 1] - rec.top);
60dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford            } else {
61dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford                path.lineTo(xy[i] - rec.left, xy[i + 1] - rec.top);
62dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford            }
63dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford        }
64dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford        path.close();
65dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford        c.drawPath(path, paint);
66dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford        return bitmap;
67dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford    }
68dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford
69dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford    /**
70dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford     * This function only assumes mPointsXY, mPasteOffX, mPasteOffY
71dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford     *
72dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford     * @param healing
73dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford     * @param rs
74dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford     * @param image
75dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford     */
76dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford    public void heal(ScriptC_healing healing, RenderScript rs, Bitmap image, Bitmap output) {
77dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford        long time = System.nanoTime();
78dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford        Type.Builder floatImage = new Type.Builder(rs, Element.F32_3(rs));
79dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford        floatImage.setX(mRoiBounds.width());
80dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford        floatImage.setY(mRoiBounds.height());
81dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford
82dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford
83dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford        Bitmap maskBitmap = buildMask(mRoiBounds, mPointsXY);
84dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford
85dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford        Allocation dest1 = Allocation.createTyped(rs, floatImage.create());
86dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford        Allocation dest2 = Allocation.createTyped(rs, floatImage.create());
87dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford        healing.set_dest1(dest1);
88dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford        healing.set_dest2(dest2);
89dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford
90dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford        Bitmap destBitmap = createMutableBitmap(image, mRoiBounds.left, mRoiBounds.top,
91dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford                mRoiBounds.width(), mRoiBounds.height());
92dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford        Allocation dest_uc4 = Allocation.createFromBitmap(rs, destBitmap);
93dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford        healing.forEach_convert_to_f(dest_uc4, dest1);
94dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford
95dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford        Bitmap src = createMutableBitmap(image, mCutOffsetX, mCutOffsetY,
96dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford                mRoiBounds.width(), mRoiBounds.height());
97dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford        Allocation src_f3 = Allocation.createTyped(rs, floatImage.create());
98dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford        Allocation src_uc4 = Allocation.createFromBitmap(rs, src);
99dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford        healing.forEach_convert_to_f(src_uc4, src_f3);
100dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford        healing.set_src(src_f3);
101dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford
102dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford        Allocation mask = Allocation.createFromBitmap(rs, maskBitmap);
103dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford        healing.set_mask(mask);
104dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford
105dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford        Allocation laplace_f3 = Allocation.createTyped(rs, floatImage.create());
106dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford        healing.set_laplace(laplace_f3);
107dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford
108dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford        Script.LaunchOptions options = new Script.LaunchOptions();
109dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford        options.setX(1, mRoiBounds.width() - 1);
110dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford        options.setY(1, mRoiBounds.height() - 1);
111dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford        healing.forEach_laplacian(laplace_f3, options);
112dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford        healing.forEach_copyMasked(mask, dest1);
113dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford        int area = calcMaskArea(mask);
114dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford
115dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford        int steps = (int) Math.sqrt(area);
116dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford
117dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford        for (int i = 0; i < steps; i++) {
118dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford            healing.forEach_solve1(mask, dest2);
119dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford            healing.forEach_solve2(mask, dest1);
120dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford        }
121dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford
122dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford        healing.forEach_convert_to_uc(dest1, dest_uc4);
123dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford        rs.finish();
124dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford
125dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford        healing.forEach_alphaMask(dest_uc4, dest_uc4);
126dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford        rs.finish();
127dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford
128dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford        dest_uc4.copyTo(destBitmap);
129dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford        rs.finish();
130dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford        destBitmap.setHasAlpha(true);
131dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford        rs.finish();
132dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford        // build the undo
133dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford        mUndoBitmap = Bitmap.createBitmap(mRoiBounds.width(), mRoiBounds.height(),
134dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford                Bitmap.Config.ARGB_8888);
135dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford        Canvas undoCanvas = new Canvas(mUndoBitmap);
136dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford        Rect undoRect = new Rect(0, 0, mRoiBounds.width(), mRoiBounds.height());
137dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford        undoCanvas.drawBitmap(output, mRoiBounds, undoRect, null);
138dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford
139dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford        Canvas c = new Canvas(output);
140dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford        c.drawBitmap(image, 0, 0, null);
141dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford        c.drawBitmap(destBitmap, mRoiBounds.left, mRoiBounds.top, null);
142dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford        Log.v(TAG, " time to smart paste = " + (System.nanoTime() - time) / 1E6f + "ms");
143dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford    }
144dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford
145dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford    Bitmap createMutableBitmap(Bitmap image, int x, int y, int width, int height) {
146dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford        Bitmap ret = Bitmap.createBitmap(image, x, y, width, height);
147dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford        return ret.copy(Bitmap.Config.ARGB_8888, true);
148dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford    }
149dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford
150dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford    private static int calcMaskArea(Allocation mask) {
151dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford        int w = mask.getType().getX();
152dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford        int h = mask.getType().getY();
153dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford        byte[] data = new byte[w * h];
154dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford        mask.copyTo(data);
155dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford        int count = 0;
156dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford        int val = data[0];
157dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford        for (int i = 0; i < data.length; i++) {
158dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford            if (data[i] != val) {
159dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford                count++;
160dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford            }
161dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford        }
162dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford        return count;
163dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford    }
164dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford
165dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford    Bitmap getmUndoBitmap() {
166dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford        return mUndoBitmap;
167dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford    }
168dbf58070bd1a228fc817ea5ddb5c08fe717aecd2hoford}
169