1b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk/*
2b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk * Copyright (C) 2012 The Android Open Source Project
3b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk *
4b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk * Licensed under the Apache License, Version 2.0 (the "License");
5b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk * you may not use this file except in compliance with the License.
6b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk * You may obtain a copy of the License at
7b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk *
8b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk *      http://www.apache.org/licenses/LICENSE-2.0
9b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk *
10b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk * Unless required by applicable law or agreed to in writing, software
11b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk * distributed under the License is distributed on an "AS IS" BASIS,
12b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk * See the License for the specific language governing permissions and
14b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk * limitations under the License.
15b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk */
16b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk
17b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunkpackage com.android.gallery3d.filtershow.imageshow;
18b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk
19b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunkimport android.graphics.Bitmap;
20b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunkimport android.graphics.Canvas;
21b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunkimport android.graphics.Matrix;
22b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunkimport android.graphics.Paint;
23b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunkimport android.graphics.Rect;
24b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunkimport android.graphics.RectF;
25ec1f915006e5ec2db8be633b5d3d73e568951da4John Hofordimport android.util.Log;
26b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk
279f452e09889199a28a86d9bd8f8fdaa8508ca0c1nicolasroardimport com.android.gallery3d.filtershow.cache.BitmapCache;
28b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunkimport com.android.gallery3d.filtershow.cache.ImageLoader;
29b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunkimport com.android.gallery3d.filtershow.filters.FilterCropRepresentation;
30b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunkimport com.android.gallery3d.filtershow.filters.FilterMirrorRepresentation;
31b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunkimport com.android.gallery3d.filtershow.filters.FilterMirrorRepresentation.Mirror;
32b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunkimport com.android.gallery3d.filtershow.filters.FilterRepresentation;
33b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunkimport com.android.gallery3d.filtershow.filters.FilterRotateRepresentation;
34b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunkimport com.android.gallery3d.filtershow.filters.FilterRotateRepresentation.Rotation;
35b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunkimport com.android.gallery3d.filtershow.filters.FilterStraightenRepresentation;
36b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunkimport com.android.gallery3d.filtershow.pipeline.ImagePreset;
37b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk
38b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunkimport java.util.Collection;
39b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunkimport java.util.Iterator;
40b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk
41b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunkpublic final class GeometryMathUtils {
42ec1f915006e5ec2db8be633b5d3d73e568951da4John Hoford    private static final String TAG = "GeometryMathUtils";
43ec1f915006e5ec2db8be633b5d3d73e568951da4John Hoford    public static final float SHOW_SCALE = .9f;
44ec1f915006e5ec2db8be633b5d3d73e568951da4John Hoford
45b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk    private GeometryMathUtils() {};
46b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk
47b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk    // Holder class for Geometry data.
48b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk    public static final class GeometryHolder {
49b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        public Rotation rotation = FilterRotateRepresentation.getNil();
50b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        public float straighten = FilterStraightenRepresentation.getNil();
51b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        public RectF crop = FilterCropRepresentation.getNil();
52b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        public Mirror mirror = FilterMirrorRepresentation.getNil();
53b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk
54b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        public void set(GeometryHolder h) {
55b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk            rotation = h.rotation;
56b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk            straighten = h.straighten;
57b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk            crop.set(h.crop);
58b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk            mirror = h.mirror;
59b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        }
60b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk
61b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        public void wipe() {
62b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk            rotation = FilterRotateRepresentation.getNil();
63b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk            straighten = FilterStraightenRepresentation.getNil();
64b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk            crop = FilterCropRepresentation.getNil();
65b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk            mirror = FilterMirrorRepresentation.getNil();
66b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        }
67b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk
68b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        public boolean isNil() {
69b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk            return rotation == FilterRotateRepresentation.getNil() &&
70b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk                    straighten == FilterStraightenRepresentation.getNil() &&
71b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk                    crop.equals(FilterCropRepresentation.getNil()) &&
72b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk                    mirror == FilterMirrorRepresentation.getNil();
73b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        }
74b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk
75b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        @Override
76b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        public boolean equals(Object o) {
77b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk            if (this == o) {
78b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk                return true;
79b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk            }
80b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk            if (!(o instanceof GeometryHolder)) {
81b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk                return false;
82b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk            }
83b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk            GeometryHolder h = (GeometryHolder) o;
84b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk            return rotation == h.rotation && straighten == h.straighten &&
85b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk                    ((crop == null && h.crop == null) || (crop != null && crop.equals(h.crop))) &&
86b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk                    mirror == h.mirror;
87b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        }
88b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk
89b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        @Override
90b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        public String toString() {
91b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk            return getClass().getSimpleName() + "[" + "rotation:" + rotation.value()
92b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk                    + ",straighten:" + straighten + ",crop:" + crop.toString()
93b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk                    + ",mirror:" + mirror.value() + "]";
94b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        }
95b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk    }
96b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk
97b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk    // Math operations for 2d vectors
98b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk    public static float clamp(float i, float low, float high) {
99b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        return Math.max(Math.min(i, high), low);
100b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk    }
101b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk
102b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk    public static float[] lineIntersect(float[] line1, float[] line2) {
103b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        float a0 = line1[0];
104b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        float a1 = line1[1];
105b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        float b0 = line1[2];
106b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        float b1 = line1[3];
107b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        float c0 = line2[0];
108b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        float c1 = line2[1];
109b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        float d0 = line2[2];
110b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        float d1 = line2[3];
111b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        float t0 = a0 - b0;
112b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        float t1 = a1 - b1;
113b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        float t2 = b0 - d0;
114b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        float t3 = d1 - b1;
115b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        float t4 = c0 - d0;
116b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        float t5 = c1 - d1;
117b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk
118b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        float denom = t1 * t4 - t0 * t5;
119b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        if (denom == 0)
120b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk            return null;
121b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        float u = (t3 * t4 + t5 * t2) / denom;
122b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        float[] intersect = {
123b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk                b0 + u * t0, b1 + u * t1
124b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        };
125b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        return intersect;
126b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk    }
127b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk
128b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk    public static float[] shortestVectorFromPointToLine(float[] point, float[] line) {
129b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        float x1 = line[0];
130b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        float x2 = line[2];
131b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        float y1 = line[1];
132b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        float y2 = line[3];
133b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        float xdelt = x2 - x1;
134b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        float ydelt = y2 - y1;
135b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        if (xdelt == 0 && ydelt == 0)
136b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk            return null;
137b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        float u = ((point[0] - x1) * xdelt + (point[1] - y1) * ydelt)
138b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk                / (xdelt * xdelt + ydelt * ydelt);
139b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        float[] ret = {
140b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk                (x1 + u * (x2 - x1)), (y1 + u * (y2 - y1))
141b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        };
142b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        float[] vec = {
143b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk                ret[0] - point[0], ret[1] - point[1]
144b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        };
145b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        return vec;
146b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk    }
147b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk
148b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk    // A . B
149b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk    public static float dotProduct(float[] a, float[] b) {
150b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        return a[0] * b[0] + a[1] * b[1];
151b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk    }
152b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk
153b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk    public static float[] normalize(float[] a) {
1548a55d3ae7486b798e4c26eeb91993916145f3cefNeil Fuller        float length = (float) Math.hypot(a[0], a[1]);
155b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        float[] b = {
156b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk                a[0] / length, a[1] / length
157b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        };
158b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        return b;
159b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk    }
160b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk
161b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk    // A onto B
162b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk    public static float scalarProjection(float[] a, float[] b) {
1638a55d3ae7486b798e4c26eeb91993916145f3cefNeil Fuller        float length = (float) Math.hypot(b[0], b[1]);
164b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        return dotProduct(a, b) / length;
165b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk    }
166b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk
167b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk    public static float[] getVectorFromPoints(float[] point1, float[] point2) {
168b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        float[] p = {
169b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk                point2[0] - point1[0], point2[1] - point1[1]
170b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        };
171b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        return p;
172b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk    }
173b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk
174b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk    public static float[] getUnitVectorFromPoints(float[] point1, float[] point2) {
175b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        float[] p = {
176b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk                point2[0] - point1[0], point2[1] - point1[1]
177b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        };
1788a55d3ae7486b798e4c26eeb91993916145f3cefNeil Fuller        float length = (float) Math.hypot(p[0], p[1]);
179b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        p[0] = p[0] / length;
180b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        p[1] = p[1] / length;
181b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        return p;
182b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk    }
183b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk
184b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk    public static void scaleRect(RectF r, float scale) {
185b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        r.set(r.left * scale, r.top * scale, r.right * scale, r.bottom * scale);
186b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk    }
187b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk
188b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk    // A - B
189b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk    public static float[] vectorSubtract(float[] a, float[] b) {
190b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        int len = a.length;
191b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        if (len != b.length)
192b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk            return null;
193b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        float[] ret = new float[len];
194b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        for (int i = 0; i < len; i++) {
195b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk            ret[i] = a[i] - b[i];
196b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        }
197b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        return ret;
198b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk    }
199b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk
200b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk    public static float vectorLength(float[] a) {
2018a55d3ae7486b798e4c26eeb91993916145f3cefNeil Fuller        return (float) Math.hypot(a[0], a[1]);
202b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk    }
203b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk
204b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk    public static float scale(float oldWidth, float oldHeight, float newWidth, float newHeight) {
205b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        if (oldHeight == 0 || oldWidth == 0 || (oldWidth == newWidth && oldHeight == newHeight)) {
206b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk            return 1;
207b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        }
208b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        return Math.min(newWidth / oldWidth, newHeight / oldHeight);
209b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk    }
210b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk
211b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk    public static Rect roundNearest(RectF r) {
212b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        Rect q = new Rect(Math.round(r.left), Math.round(r.top), Math.round(r.right),
213b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk                Math.round(r.bottom));
214b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        return q;
215b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk    }
216b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk
2179f452e09889199a28a86d9bd8f8fdaa8508ca0c1nicolasroard    private static void concatMirrorMatrix(Matrix m, GeometryHolder holder) {
2189f452e09889199a28a86d9bd8f8fdaa8508ca0c1nicolasroard        Mirror type = holder.mirror;
2199f452e09889199a28a86d9bd8f8fdaa8508ca0c1nicolasroard        if (type == Mirror.HORIZONTAL) {
2209f452e09889199a28a86d9bd8f8fdaa8508ca0c1nicolasroard            if (holder.rotation.value() == 90
2219f452e09889199a28a86d9bd8f8fdaa8508ca0c1nicolasroard                    || holder.rotation.value() == 270) {
2229f452e09889199a28a86d9bd8f8fdaa8508ca0c1nicolasroard                type = Mirror.VERTICAL;
2239f452e09889199a28a86d9bd8f8fdaa8508ca0c1nicolasroard            }
2249f452e09889199a28a86d9bd8f8fdaa8508ca0c1nicolasroard        } else if (type == Mirror.VERTICAL) {
2259f452e09889199a28a86d9bd8f8fdaa8508ca0c1nicolasroard            if (holder.rotation.value() == 90
2269f452e09889199a28a86d9bd8f8fdaa8508ca0c1nicolasroard                    || holder.rotation.value() == 270) {
2279f452e09889199a28a86d9bd8f8fdaa8508ca0c1nicolasroard                type = Mirror.HORIZONTAL;
2289f452e09889199a28a86d9bd8f8fdaa8508ca0c1nicolasroard            }
2299f452e09889199a28a86d9bd8f8fdaa8508ca0c1nicolasroard        }
230b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        if (type == Mirror.HORIZONTAL) {
231b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk            m.postScale(-1, 1);
232b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        } else if (type == Mirror.VERTICAL) {
233b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk            m.postScale(1, -1);
234b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        } else if (type == Mirror.BOTH) {
235b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk            m.postScale(1, -1);
236b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk            m.postScale(-1, 1);
237b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        }
238b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk    }
239b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk
240b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk    private static int getRotationForOrientation(int orientation) {
241b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        switch (orientation) {
242b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk            case ImageLoader.ORI_ROTATE_90:
243b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk                return 90;
244b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk            case ImageLoader.ORI_ROTATE_180:
245b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk                return 180;
246b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk            case ImageLoader.ORI_ROTATE_270:
247b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk                return 270;
248b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk            default:
249b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk                return 0;
250b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        }
251b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk    }
252b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk
253b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk    public static GeometryHolder unpackGeometry(Collection<FilterRepresentation> geometry) {
254b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        GeometryHolder holder = new GeometryHolder();
255b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        unpackGeometry(holder, geometry);
256b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        return holder;
257b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk    }
258b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk
259b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk    public static void unpackGeometry(GeometryHolder out,
260b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk            Collection<FilterRepresentation> geometry) {
261b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        out.wipe();
262b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        // Get geometry data from filters
263b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        for (FilterRepresentation r : geometry) {
264b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk            if (r.isNil()) {
265b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk                continue;
266b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk            }
267b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk            if (r.getSerializationName() == FilterRotateRepresentation.SERIALIZATION_NAME) {
268b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk                out.rotation = ((FilterRotateRepresentation) r).getRotation();
269b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk            } else if (r.getSerializationName() ==
270b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk                    FilterStraightenRepresentation.SERIALIZATION_NAME) {
271b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk                out.straighten = ((FilterStraightenRepresentation) r).getStraighten();
272b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk            } else if (r.getSerializationName() == FilterCropRepresentation.SERIALIZATION_NAME) {
273b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk                ((FilterCropRepresentation) r).getCrop(out.crop);
274b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk            } else if (r.getSerializationName() == FilterMirrorRepresentation.SERIALIZATION_NAME) {
275b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk                out.mirror = ((FilterMirrorRepresentation) r).getMirror();
276b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk            }
277b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        }
278b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk    }
279b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk
280b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk    public static void replaceInstances(Collection<FilterRepresentation> geometry,
281b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk            FilterRepresentation rep) {
282b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        Iterator<FilterRepresentation> iter = geometry.iterator();
283b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        while (iter.hasNext()) {
284b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk            FilterRepresentation r = iter.next();
285b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk            if (ImagePreset.sameSerializationName(rep, r)) {
286b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk                iter.remove();
287b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk            }
288b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        }
289b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        if (!rep.isNil()) {
290b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk            geometry.add(rep);
291b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        }
292b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk    }
293b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk
294b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk    public static void initializeHolder(GeometryHolder outHolder,
295b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk            FilterRepresentation currentLocal) {
296b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        Collection<FilterRepresentation> geometry = MasterImage.getImage().getPreset()
297b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk                .getGeometryFilters();
298b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        replaceInstances(geometry, currentLocal);
299b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        unpackGeometry(outHolder, geometry);
300b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk    }
301b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk
30255d6abe58c3a7574a5d76c68ec5c21848848e399nicolasroard    public static Rect finalGeometryRect(int width, int height,
30355d6abe58c3a7574a5d76c68ec5c21848848e399nicolasroard                                         Collection<FilterRepresentation> geometry) {
30455d6abe58c3a7574a5d76c68ec5c21848848e399nicolasroard        GeometryHolder holder = unpackGeometry(geometry);
30555d6abe58c3a7574a5d76c68ec5c21848848e399nicolasroard        RectF crop = getTrueCropRect(holder, width, height);
30655d6abe58c3a7574a5d76c68ec5c21848848e399nicolasroard        Rect frame = new Rect();
30755d6abe58c3a7574a5d76c68ec5c21848848e399nicolasroard        crop.roundOut(frame);
30855d6abe58c3a7574a5d76c68ec5c21848848e399nicolasroard        return frame;
30955d6abe58c3a7574a5d76c68ec5c21848848e399nicolasroard    }
31055d6abe58c3a7574a5d76c68ec5c21848848e399nicolasroard
311b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk    private static Bitmap applyFullGeometryMatrix(Bitmap image, GeometryHolder holder) {
312b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        int width = image.getWidth();
313b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        int height = image.getHeight();
314b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        RectF crop = getTrueCropRect(holder, width, height);
315b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        Rect frame = new Rect();
316b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        crop.roundOut(frame);
317b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        Matrix m = getCropSelectionToScreenMatrix(null, holder, width, height, frame.width(),
318b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk                frame.height());
3199f452e09889199a28a86d9bd8f8fdaa8508ca0c1nicolasroard        BitmapCache bitmapCache = MasterImage.getImage().getBitmapCache();
320ec1e009a7faea0478e361bc2d48d856ab48a0209nicolasroard        Bitmap temp = bitmapCache.getBitmap(frame.width(),
321ec1e009a7faea0478e361bc2d48d856ab48a0209nicolasroard                frame.height(), BitmapCache.UTIL_GEOMETRY);
322b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        Canvas canvas = new Canvas(temp);
323b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        Paint paint = new Paint();
324b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        paint.setAntiAlias(true);
325b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        paint.setFilterBitmap(true);
326b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        paint.setDither(true);
327b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        canvas.drawBitmap(image, m, paint);
328b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        return temp;
329b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk    }
330b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk
331b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk    public static Matrix getImageToScreenMatrix(Collection<FilterRepresentation> geometry,
332b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk            boolean reflectRotation, Rect bmapDimens, float viewWidth, float viewHeight) {
333b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        GeometryHolder h = unpackGeometry(geometry);
334b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        return GeometryMathUtils.getOriginalToScreen(h, reflectRotation, bmapDimens.width(),
335b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk                bmapDimens.height(), viewWidth, viewHeight);
336b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk    }
337b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk
338430e46b06f8e7ee1ca3e7ecdcef3e0a978637c03nicolasroard    public static Matrix getPartialToScreenMatrix(Collection<FilterRepresentation> geometry,
339430e46b06f8e7ee1ca3e7ecdcef3e0a978637c03nicolasroard                                                  Rect originalBounds, float w, float h,
340430e46b06f8e7ee1ca3e7ecdcef3e0a978637c03nicolasroard                                                  float pw, float ph) {
341430e46b06f8e7ee1ca3e7ecdcef3e0a978637c03nicolasroard        GeometryHolder holder = unpackGeometry(geometry);
342430e46b06f8e7ee1ca3e7ecdcef3e0a978637c03nicolasroard        RectF rCrop = new RectF(0, 0, originalBounds.width(), originalBounds.height());
343430e46b06f8e7ee1ca3e7ecdcef3e0a978637c03nicolasroard        float angle = holder.straighten;
344430e46b06f8e7ee1ca3e7ecdcef3e0a978637c03nicolasroard        int rotation = holder.rotation.value();
345430e46b06f8e7ee1ca3e7ecdcef3e0a978637c03nicolasroard
346430e46b06f8e7ee1ca3e7ecdcef3e0a978637c03nicolasroard        ImageStraighten.getUntranslatedStraightenCropBounds(rCrop, angle);
347430e46b06f8e7ee1ca3e7ecdcef3e0a978637c03nicolasroard        float dx = (w - pw) / 2f;
348430e46b06f8e7ee1ca3e7ecdcef3e0a978637c03nicolasroard        float dy = (h - ph) / 2f;
349430e46b06f8e7ee1ca3e7ecdcef3e0a978637c03nicolasroard        Matrix compensation = new Matrix();
350430e46b06f8e7ee1ca3e7ecdcef3e0a978637c03nicolasroard        compensation.postTranslate(dx, dy);
351430e46b06f8e7ee1ca3e7ecdcef3e0a978637c03nicolasroard        float cScale = originalBounds.width() / rCrop.width();
352430e46b06f8e7ee1ca3e7ecdcef3e0a978637c03nicolasroard        if (rCrop.width() < rCrop.height()) {
353430e46b06f8e7ee1ca3e7ecdcef3e0a978637c03nicolasroard            cScale = originalBounds.height() / rCrop.height();
354430e46b06f8e7ee1ca3e7ecdcef3e0a978637c03nicolasroard        }
355430e46b06f8e7ee1ca3e7ecdcef3e0a978637c03nicolasroard        float scale = w / pw;
356430e46b06f8e7ee1ca3e7ecdcef3e0a978637c03nicolasroard        if (w < h) {
357430e46b06f8e7ee1ca3e7ecdcef3e0a978637c03nicolasroard            scale = h / ph;
358430e46b06f8e7ee1ca3e7ecdcef3e0a978637c03nicolasroard        }
359430e46b06f8e7ee1ca3e7ecdcef3e0a978637c03nicolasroard        scale = scale * cScale;
360430e46b06f8e7ee1ca3e7ecdcef3e0a978637c03nicolasroard        float cx = w / 2f;
361430e46b06f8e7ee1ca3e7ecdcef3e0a978637c03nicolasroard        float cy = h / 2f;
362430e46b06f8e7ee1ca3e7ecdcef3e0a978637c03nicolasroard
363430e46b06f8e7ee1ca3e7ecdcef3e0a978637c03nicolasroard        compensation.postScale(scale, scale, cx, cy);
364430e46b06f8e7ee1ca3e7ecdcef3e0a978637c03nicolasroard        compensation.postRotate(angle, cx, cy);
365430e46b06f8e7ee1ca3e7ecdcef3e0a978637c03nicolasroard        compensation.postRotate(rotation, cx, cy);
366430e46b06f8e7ee1ca3e7ecdcef3e0a978637c03nicolasroard        compensation.postTranslate(-cx, -cy);
3679f452e09889199a28a86d9bd8f8fdaa8508ca0c1nicolasroard        concatMirrorMatrix(compensation, holder);
368430e46b06f8e7ee1ca3e7ecdcef3e0a978637c03nicolasroard        compensation.postTranslate(cx, cy);
369430e46b06f8e7ee1ca3e7ecdcef3e0a978637c03nicolasroard        return compensation;
370430e46b06f8e7ee1ca3e7ecdcef3e0a978637c03nicolasroard    }
371430e46b06f8e7ee1ca3e7ecdcef3e0a978637c03nicolasroard
372b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk    public static Matrix getOriginalToScreen(GeometryHolder holder, boolean rotate,
373b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk            float originalWidth,
374b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk            float originalHeight, float viewWidth, float viewHeight) {
375b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        int orientation = MasterImage.getImage().getZoomOrientation();
376b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        int rotation = getRotationForOrientation(orientation);
377b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        Rotation prev = holder.rotation;
378b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        rotation = (rotation + prev.value()) % 360;
379b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        holder.rotation = Rotation.fromValue(rotation);
380b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        Matrix m = getCropSelectionToScreenMatrix(null, holder, (int) originalWidth,
381b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk                (int) originalHeight, (int) viewWidth, (int) viewHeight);
382b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        holder.rotation = prev;
383b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        return m;
384b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk    }
385b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk
386b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk    public static Bitmap applyGeometryRepresentations(Collection<FilterRepresentation> res,
387b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk            Bitmap image) {
388b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        GeometryHolder holder = unpackGeometry(res);
389b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        Bitmap bmap = image;
390b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        // If there are geometry changes, apply them to the image
391b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        if (!holder.isNil()) {
392b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk            bmap = applyFullGeometryMatrix(bmap, holder);
3939f452e09889199a28a86d9bd8f8fdaa8508ca0c1nicolasroard            if (bmap != image) {
3949f452e09889199a28a86d9bd8f8fdaa8508ca0c1nicolasroard                BitmapCache cache = MasterImage.getImage().getBitmapCache();
3959f452e09889199a28a86d9bd8f8fdaa8508ca0c1nicolasroard                cache.cache(image);
3969f452e09889199a28a86d9bd8f8fdaa8508ca0c1nicolasroard            }
397b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        }
398b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        return bmap;
399b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk    }
400b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk
401b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk    public static RectF drawTransformedCropped(GeometryHolder holder, Canvas canvas,
402b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk            Bitmap photo, int viewWidth, int viewHeight) {
403b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        if (photo == null) {
404b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk            return null;
405b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        }
406b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        RectF crop = new RectF();
407b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        Matrix m = getCropSelectionToScreenMatrix(crop, holder, photo.getWidth(), photo.getHeight(),
408b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk                viewWidth, viewHeight);
409b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        canvas.save();
410b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        canvas.clipRect(crop);
411b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        Paint p = new Paint();
412b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        p.setAntiAlias(true);
413b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        canvas.drawBitmap(photo, m, p);
414b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        canvas.restore();
415b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        return crop;
416b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk    }
417b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk
418b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk    public static boolean needsDimensionSwap(Rotation rotation) {
419b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        switch (rotation) {
420b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk            case NINETY:
421b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk            case TWO_SEVENTY:
422b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk                return true;
423b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk            default:
424b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk                return false;
425b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        }
426b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk    }
427b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk
428b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk    // Gives matrix for rotated, straightened, mirrored bitmap centered at 0,0.
429b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk    private static Matrix getFullGeometryMatrix(GeometryHolder holder, int bitmapWidth,
430b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk            int bitmapHeight) {
431b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        float centerX = bitmapWidth / 2f;
432b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        float centerY = bitmapHeight / 2f;
433b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        Matrix m = new Matrix();
434b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        m.setTranslate(-centerX, -centerY);
435b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        m.postRotate(holder.straighten + holder.rotation.value());
4369f452e09889199a28a86d9bd8f8fdaa8508ca0c1nicolasroard        concatMirrorMatrix(m, holder);
437b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        return m;
438b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk    }
439b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk
440b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk    public static Matrix getFullGeometryToScreenMatrix(GeometryHolder holder, int bitmapWidth,
441b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk            int bitmapHeight, int viewWidth, int viewHeight) {
442ec1f915006e5ec2db8be633b5d3d73e568951da4John Hoford        int bh = bitmapHeight;
443ec1f915006e5ec2db8be633b5d3d73e568951da4John Hoford        int bw = bitmapWidth;
444ec1f915006e5ec2db8be633b5d3d73e568951da4John Hoford        if (GeometryMathUtils.needsDimensionSwap(holder.rotation)) {
445ec1f915006e5ec2db8be633b5d3d73e568951da4John Hoford            bh = bitmapWidth;
446ec1f915006e5ec2db8be633b5d3d73e568951da4John Hoford            bw = bitmapHeight;
447ec1f915006e5ec2db8be633b5d3d73e568951da4John Hoford        }
448ec1f915006e5ec2db8be633b5d3d73e568951da4John Hoford        float scale = GeometryMathUtils.scale(bw, bh, viewWidth, viewHeight);
449ec1f915006e5ec2db8be633b5d3d73e568951da4John Hoford        scale *= SHOW_SCALE;
450ec1f915006e5ec2db8be633b5d3d73e568951da4John Hoford        float s = Math.min(viewWidth / (float) bitmapWidth, viewHeight / (float) bitmapHeight);
451b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        Matrix m = getFullGeometryMatrix(holder, bitmapWidth, bitmapHeight);
452b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        m.postScale(scale, scale);
453b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        m.postTranslate(viewWidth / 2f, viewHeight / 2f);
454b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        return m;
455b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk    }
456b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk
457b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk    public static RectF getTrueCropRect(GeometryHolder holder, int bitmapWidth, int bitmapHeight) {
458b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        RectF r = new RectF(holder.crop);
459b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        FilterCropRepresentation.findScaledCrop(r, bitmapWidth, bitmapHeight);
460b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        float s = holder.straighten;
461b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        holder.straighten = 0;
462b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        Matrix m1 = getFullGeometryMatrix(holder, bitmapWidth, bitmapHeight);
463b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        holder.straighten = s;
464b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        m1.mapRect(r);
465b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        return r;
466b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk    }
467b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk
468b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk    public static Matrix getCropSelectionToScreenMatrix(RectF outCrop, GeometryHolder holder,
469b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk            int bitmapWidth, int bitmapHeight, int viewWidth, int viewHeight) {
470b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        Matrix m = getFullGeometryMatrix(holder, bitmapWidth, bitmapHeight);
471b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        RectF crop = getTrueCropRect(holder, bitmapWidth, bitmapHeight);
472b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        float scale = GeometryMathUtils.scale(crop.width(), crop.height(), viewWidth, viewHeight);
473b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        m.postScale(scale, scale);
474b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        GeometryMathUtils.scaleRect(crop, scale);
475b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        m.postTranslate(viewWidth / 2f - crop.centerX(), viewHeight / 2f - crop.centerY());
476b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        if (outCrop != null) {
477b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk            crop.offset(viewWidth / 2f - crop.centerX(), viewHeight / 2f - crop.centerY());
478b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk            outCrop.set(crop);
479b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        }
480b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        return m;
481b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk    }
482b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk
483b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk    public static Matrix getCropSelectionToScreenMatrix(RectF outCrop,
484b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk            Collection<FilterRepresentation> res, int bitmapWidth, int bitmapHeight, int viewWidth,
485b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk            int viewHeight) {
486b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        GeometryHolder holder = unpackGeometry(res);
487b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk        return getCropSelectionToScreenMatrix(outCrop, holder, bitmapWidth, bitmapHeight,
488b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk                viewWidth, viewHeight);
489b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk    }
490b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk}
491