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