summaryrefslogtreecommitdiffstats
path: root/tools/layoutlib/bridge/src/android/graphics/PathMeasure_Delegate.java
blob: dd2978f5c41468f2b6b38c9ac651e9401dcd4b90 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
/*
 * Copyright (C) 2015 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.graphics;

import com.android.ide.common.rendering.api.LayoutLog;
import com.android.layoutlib.bridge.Bridge;
import com.android.layoutlib.bridge.impl.DelegateManager;
import com.android.tools.layoutlib.annotations.LayoutlibDelegate;

import java.awt.geom.PathIterator;
import java.awt.geom.Point2D;

/**
 * Delegate implementing the native methods of {@link android.graphics.PathMeasure}
 * <p/>
 * Through the layoutlib_create tool, the original native methods of PathMeasure have been
 * replaced by
 * calls to methods of the same name in this delegate class.
 * <p/>
 * This class behaves like the original native implementation, but in Java, keeping previously
 * native data into its own objects and mapping them to int that are sent back and forth between it
 * and the original PathMeasure class.
 *
 * @see DelegateManager
 */
public final class PathMeasure_Delegate {
    // ---- delegate manager ----
    private static final DelegateManager<PathMeasure_Delegate> sManager =
            new DelegateManager<PathMeasure_Delegate>(PathMeasure_Delegate.class);

    // ---- delegate data ----
    // This governs how accurate the approximation of the Path is.
    private static final float PRECISION = 0.002f;

    /**
     * Array containing the path points components. There are three components for each point:
     * <ul>
     *     <li>Fraction along the length of the path that the point resides</li>
     *     <li>The x coordinate of the point</li>
     *     <li>The y coordinate of the point</li>
     * </ul>
     */
    private float mPathPoints[];
    private long mNativePath;

    private PathMeasure_Delegate(long native_path, boolean forceClosed) {
        mNativePath = native_path;
        if (forceClosed && mNativePath != 0) {
            // Copy the path and call close
            mNativePath = Path_Delegate.init2(native_path);
            Path_Delegate.native_close(mNativePath);
        }

        mPathPoints =
                mNativePath != 0 ? Path_Delegate.native_approximate(mNativePath, PRECISION) : null;
    }

    @LayoutlibDelegate
    /*package*/ static long native_create(long native_path, boolean forceClosed) {
        return sManager.addNewDelegate(new PathMeasure_Delegate(native_path, forceClosed));
    }

    @LayoutlibDelegate
    /*package*/ static void native_destroy(long native_instance) {
        sManager.removeJavaReferenceFor(native_instance);
    }

    @LayoutlibDelegate
    /*package*/ static boolean native_getPosTan(long native_instance, float distance, float pos[],
            float tan[]) {
        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
                "PathMeasure.getPostTan is not supported.", null, null);
        return false;
    }

    @LayoutlibDelegate
    /*package*/ static boolean native_getMatrix(long native_instance, float distance, long
            native_matrix, int flags) {
        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
                "PathMeasure.getMatrix is not supported.", null, null);
        return false;
    }

    @LayoutlibDelegate
    /*package*/ static boolean native_nextContour(long native_instance) {
        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
                "PathMeasure.nextContour is not supported.", null, null);
        return false;
    }

    @LayoutlibDelegate
    /*package*/ static void native_setPath(long native_instance, long native_path, boolean
            forceClosed) {
        PathMeasure_Delegate pathMeasure = sManager.getDelegate(native_instance);
        assert pathMeasure != null;

        if (forceClosed && native_path != 0) {
            // Copy the path and call close
            native_path = Path_Delegate.init2(native_path);
            Path_Delegate.native_close(native_path);
        }
        pathMeasure.mNativePath = native_path;
        pathMeasure.mPathPoints = Path_Delegate.native_approximate(native_path, PRECISION);
    }

    @LayoutlibDelegate
    /*package*/ static float native_getLength(long native_instance) {
        PathMeasure_Delegate pathMeasure = sManager.getDelegate(native_instance);
        assert pathMeasure != null;

        if (pathMeasure.mPathPoints == null) {
            return 0;
        }

        float length = 0;
        int nPoints = pathMeasure.mPathPoints.length / 3;
        for (int i = 1; i < nPoints; i++) {
            length += Point2D.distance(
                    pathMeasure.mPathPoints[(i - 1) * 3 + 1],
                    pathMeasure.mPathPoints[(i - 1) * 3 + 2],
                    pathMeasure.mPathPoints[i*3 + 1],
                    pathMeasure.mPathPoints[i*3 + 2]);
        }

        return length;
    }

    @LayoutlibDelegate
    /*package*/ static boolean native_isClosed(long native_instance) {
        PathMeasure_Delegate pathMeasure = sManager.getDelegate(native_instance);
        assert pathMeasure != null;

        Path_Delegate path = Path_Delegate.getDelegate(pathMeasure.mNativePath);
        if (path == null) {
            return false;
        }

        PathIterator pathIterator = path.getJavaShape().getPathIterator(null);

        int type = 0;
        float segment[] = new float[6];
        while (!pathIterator.isDone()) {
            type = pathIterator.currentSegment(segment);
            pathIterator.next();
        }

        // A path is a closed path if the last element is SEG_CLOSE
        return type == PathIterator.SEG_CLOSE;
    }

    @LayoutlibDelegate
    /*package*/ static boolean native_getSegment(long native_instance, float startD, float stopD,
            long native_dst_path, boolean startWithMoveTo) {
        if (startD < 0) {
            startD = 0;
        }

        if (startD >= stopD) {
            return false;
        }

        PathMeasure_Delegate pathMeasure = sManager.getDelegate(native_instance);
        assert pathMeasure != null;

        if (pathMeasure.mPathPoints == null) {
            return false;
        }

        float accLength = 0;
        boolean isZeroLength = true; // Whether the output has zero length or not
        int nPoints = pathMeasure.mPathPoints.length / 3;
        for (int i = 0; i < nPoints; i++) {
            float x = pathMeasure.mPathPoints[i * 3 + 1];
            float y = pathMeasure.mPathPoints[i * 3 + 2];
            if (accLength >= startD && accLength <= stopD) {
                if (startWithMoveTo) {
                    startWithMoveTo = false;
                    Path_Delegate.native_moveTo(native_dst_path, x, y);
                } else {
                    isZeroLength = false;
                    Path_Delegate.native_lineTo(native_dst_path, x, y);
                }
            }

            if (i > 0) {
                accLength += Point2D.distance(
                        pathMeasure.mPathPoints[(i - 1) * 3 + 1],
                        pathMeasure.mPathPoints[(i - 1) * 3 + 2],
                        pathMeasure.mPathPoints[i * 3 + 1],
                        pathMeasure.mPathPoints[i * 3 + 2]);
            }
        }

        return !isZeroLength;
    }
}