1/*
2 * Copyright (C) 2013 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 com.android.gallery3d.filtershow.filters;
18
19import android.graphics.Color;
20import android.graphics.Path;
21import android.graphics.PathMeasure;
22import android.util.JsonReader;
23import android.util.JsonWriter;
24import android.util.Log;
25
26import com.android.gallery3d.R;
27import com.android.gallery3d.filtershow.controller.BasicParameterInt;
28import com.android.gallery3d.filtershow.controller.BasicParameterStyle;
29import com.android.gallery3d.filtershow.controller.Parameter;
30import com.android.gallery3d.filtershow.controller.ParameterBrightness;
31import com.android.gallery3d.filtershow.controller.ParameterColor;
32import com.android.gallery3d.filtershow.controller.ParameterHue;
33import com.android.gallery3d.filtershow.controller.ParameterOpacity;
34import com.android.gallery3d.filtershow.controller.ParameterSaturation;
35import com.android.gallery3d.filtershow.editors.EditorDraw;
36
37import java.io.IOException;
38import java.util.Arrays;
39import java.util.Iterator;
40import java.util.Vector;
41
42public class FilterDrawRepresentation extends FilterRepresentation {
43    private static final String LOGTAG = "FilterDrawRepresentation";
44
45    public static final int PARAM_SIZE = 0;
46    public static final int PARAM_STYLE = 1;
47    public static final int PARAM_COLOR = 2;
48    private BasicParameterInt mParamSize = new BasicParameterInt(PARAM_SIZE, 30, 2, 300);
49    private BasicParameterStyle mParamStyle = new BasicParameterStyle(PARAM_STYLE, 5);
50    public static int DEFAULT_MENU_COLOR1 = Color.RED & 0x80FFFFFF;
51    public static int DEFAULT_MENU_COLOR2 = Color.GREEN & 0x80FFFFFF;
52    public static int DEFAULT_MENU_COLOR3 = Color.BLUE & 0x80FFFFFF;
53    public static int DEFAULT_MENU_COLOR4 = Color.BLACK & 0x80FFFFFF;
54    public static int DEFAULT_MENU_COLOR5 = Color.WHITE & 0x80FFFFFF;
55    ParameterColor mParamColor = new ParameterColor(PARAM_COLOR,DEFAULT_MENU_COLOR1);
56    int mParamMode;
57    Parameter mCurrentParam = mParamSize;
58    private static final String SERIAL_COLOR = "color";
59    private static final String SERIAL_RADIUS = "radius";
60    private static final String SERIAL_TYPE = "type";
61    private static final String SERIAL_POINTS_COUNT = "point_count";
62    private static final String SERIAL_POINTS = "points";
63    private static final String SERIAL_PATH =  "path";
64
65
66    private Parameter[] mAllParam = {
67            mParamSize,
68            mParamStyle,
69            mParamColor
70    };
71
72    public void setPramMode(int mode) {
73        mParamMode = mode;
74        mCurrentParam = mAllParam[mParamMode];
75    }
76
77    public int getParamMode() {
78        return mParamMode;
79    }
80
81    public Parameter getCurrentParam() {
82        return  mAllParam[mParamMode];
83    }
84
85    public Parameter getParam(int type) {
86        return  mAllParam[type];
87    }
88
89    public static class StrokeData implements Cloneable {
90        public byte mType;
91        public Path mPath;
92        public float mRadius;
93        public int mColor;
94        public int noPoints = 0;
95        public float[] mPoints = new float[20];
96
97        public StrokeData() {
98        }
99
100        public StrokeData(StrokeData copy) {
101            mType = copy.mType;
102            mPath = new Path(copy.mPath);
103            mRadius = copy.mRadius;
104            mColor = copy.mColor;
105            noPoints = copy.noPoints;
106            mPoints = Arrays.copyOf(copy.mPoints, copy.mPoints.length);
107        }
108
109        @Override
110        public boolean equals(Object o) {
111            if (!(o instanceof StrokeData)) {
112                return false;
113            }
114            StrokeData sd = (StrokeData) o;
115            if (mType != sd.mType
116                    || mRadius != sd.mRadius
117                    || noPoints != sd.noPoints
118                    || mColor != sd.mColor) {
119                return false;
120            }
121            return mPath.equals(sd.mPath);
122        }
123
124        @Override
125        public String toString() {
126            return "stroke(" + mType + ", path(" + (mPath) + "), " + mRadius + " , "
127                    + Integer.toHexString(mColor) + ")";
128        }
129
130        @Override
131        public StrokeData clone() throws CloneNotSupportedException {
132            return (StrokeData) super.clone();
133        }
134    }
135
136    static String colorHexString(int val) {
137        String str = "00000000" + Integer.toHexString(val);
138        str = "0x" + str.substring(str.length() - 8);
139        return str;
140    }
141
142    public String getValueString() {
143        int val;
144        switch (mParamMode) {
145            case PARAM_COLOR:
146                val = ((ParameterColor) mAllParam[mParamMode]).getValue();
147                return "";
148            case PARAM_SIZE:
149                val = ((BasicParameterInt) mAllParam[mParamMode]).getValue();
150                return ((val > 0) ? " +" : " ") + val;
151            case PARAM_STYLE:
152                return "";
153        }
154        return "";
155    }
156
157    private Vector<StrokeData> mDrawing = new Vector<StrokeData>();
158    private StrokeData mCurrent; // used in the currently drawing style
159
160    public FilterDrawRepresentation() {
161        super("Draw");
162        setFilterClass(ImageFilterDraw.class);
163        setSerializationName("DRAW");
164        setFilterType(FilterRepresentation.TYPE_VIGNETTE);
165        setTextId(R.string.imageDraw);
166        setEditorId(EditorDraw.ID);
167        setOverlayId(R.drawable.filtershow_drawing);
168        setOverlayOnly(true);
169    }
170
171    @Override
172    public String toString() {
173        return getName() + " : strokes=" + mDrawing.size()
174                + ((mCurrent == null) ? " no current "
175                : ("draw=" + mCurrent.mType + " " + mCurrent.noPoints));
176    }
177
178    public Vector<StrokeData> getDrawing() {
179        return mDrawing;
180    }
181
182    public StrokeData getCurrentDrawing() {
183        return mCurrent;
184    }
185
186    @Override
187    public FilterRepresentation copy() {
188        FilterDrawRepresentation representation = new FilterDrawRepresentation();
189        copyAllParameters(representation);
190        return representation;
191    }
192
193    @Override
194    protected void copyAllParameters(FilterRepresentation representation) {
195        super.copyAllParameters(representation);
196        representation.useParametersFrom(this);
197    }
198
199    @Override
200    public boolean isNil() {
201        return getDrawing().isEmpty();
202    }
203
204    @Override
205    public void useParametersFrom(FilterRepresentation a) {
206        if (a instanceof FilterDrawRepresentation) {
207            FilterDrawRepresentation representation = (FilterDrawRepresentation) a;
208            mParamColor.copyPalletFrom(representation.mParamColor);
209            try {
210                if (representation.mCurrent != null) {
211                    mCurrent = (StrokeData) representation.mCurrent.clone();
212                } else {
213                    mCurrent = null;
214                }
215                if (representation.mDrawing != null) {
216                    mDrawing = new Vector<StrokeData>();
217                    for (Iterator<StrokeData> elem = representation.mDrawing.iterator(); elem.hasNext(); ) {
218                        StrokeData next =  elem.next();
219                        mDrawing.add(new StrokeData(next));
220                    }
221                } else {
222                    mDrawing = null;
223                }
224
225            } catch (CloneNotSupportedException e) {
226                e.printStackTrace();
227            }
228        } else {
229            Log.v(LOGTAG, "cannot use parameters from " + a);
230        }
231    }
232
233    @Override
234    public boolean equals(FilterRepresentation representation) {
235        if (!super.equals(representation)) {
236            return false;
237        }
238        if (representation instanceof FilterDrawRepresentation) {
239            FilterDrawRepresentation fdRep = (FilterDrawRepresentation) representation;
240            if (fdRep.mDrawing.size() != mDrawing.size())
241                return false;
242            if (fdRep.mCurrent == null ^ (mCurrent == null || mCurrent.mPath == null)) {
243                return false;
244            }
245
246
247            if (fdRep.mCurrent != null && mCurrent != null && mCurrent.mPath != null) {
248                if (fdRep.mCurrent.noPoints == mCurrent.noPoints) {
249                    return true;
250                }
251                return false;
252            }
253
254        int n = mDrawing.size();
255        for (int i = 0; i < n; i++) {
256            StrokeData a = mDrawing.get(i);
257            StrokeData b = mDrawing.get(i);
258            if (!a.equals(b)){
259                return false;
260            }
261        }
262        return true;
263        }
264        return false;
265    }
266
267    private int computeCurrentColor(){
268        return mParamColor.getValue();
269    }
270
271    public void fillStrokeParameters(StrokeData sd){
272        byte type = (byte) mParamStyle.getSelected();
273        int color = computeCurrentColor();
274        float size = mParamSize.getValue();
275        sd.mColor = color;
276        sd.mRadius = size;
277        sd.mType = type;
278    }
279
280    public void startNewSection(float x, float y) {
281        mCurrent = new StrokeData();
282        fillStrokeParameters(mCurrent);
283        mCurrent.mPath = new Path();
284        mCurrent.mPath.moveTo(x, y);
285        mCurrent.mPoints[0] = x;
286        mCurrent.mPoints[1] = y;
287        mCurrent.noPoints = 1;
288    }
289
290    public void addPoint(float x, float y) {
291        int len = mCurrent.noPoints * 2;
292        mCurrent.mPath.lineTo(x, y);
293        if ((len+2) > mCurrent.mPoints.length) {
294            mCurrent.mPoints = Arrays.copyOf(mCurrent.mPoints, mCurrent.mPoints.length * 2);
295        }
296        mCurrent.mPoints[len] = x;
297        mCurrent.mPoints[len + 1] = y;
298        mCurrent.noPoints++;
299    }
300
301    public void endSection(float x, float y) {
302        addPoint(x, y);
303        mDrawing.add(mCurrent);
304        mCurrent = null;
305    }
306
307    public void clearCurrentSection() {
308        mCurrent = null;
309    }
310
311    public void clear() {
312        mCurrent = null;
313        mDrawing.clear();
314    }
315
316    @Override
317    public void serializeRepresentation(JsonWriter writer) throws IOException {
318        writer.beginObject();
319        int len = mDrawing.size();
320        int count = 0;
321        float[] mPosition = new float[2];
322        float[] mTan = new float[2];
323
324        PathMeasure mPathMeasure = new PathMeasure();
325        for (int i = 0; i < len; i++) {
326            writer.name(SERIAL_PATH + i);
327            writer.beginObject();
328            StrokeData mark = mDrawing.get(i);
329            writer.name(SERIAL_COLOR).value(mark.mColor);
330            writer.name(SERIAL_RADIUS).value(mark.mRadius);
331            writer.name(SERIAL_TYPE).value(mark.mType);
332            writer.name(SERIAL_POINTS_COUNT).value(mark.noPoints);
333            writer.name(SERIAL_POINTS);
334
335            writer.beginArray();
336            int npoints = mark.noPoints * 2;
337            for (int j = 0; j < npoints; j++) {
338                writer.value(mark.mPoints[j]);
339            }
340            writer.endArray();
341            writer.endObject();
342        }
343        writer.endObject();
344    }
345
346    @Override
347    public void deSerializeRepresentation(JsonReader sreader) throws IOException {
348        sreader.beginObject();
349        Vector<StrokeData> strokes = new Vector<StrokeData>();
350
351        while (sreader.hasNext()) {
352            sreader.nextName();
353            sreader.beginObject();
354            StrokeData stroke = new StrokeData();
355
356            while (sreader.hasNext()) {
357                String name = sreader.nextName();
358                if (name.equals(SERIAL_COLOR)) {
359                    stroke.mColor = sreader.nextInt();
360                } else if (name.equals(SERIAL_RADIUS)) {
361                    stroke.mRadius = (float) sreader.nextDouble();
362                } else if (name.equals(SERIAL_TYPE)) {
363                    stroke.mType = (byte) sreader.nextInt();
364                } else if (name.equals(SERIAL_POINTS_COUNT)) {
365                    stroke.noPoints = sreader.nextInt();
366                } else if (name.equals(SERIAL_POINTS)) {
367
368                    int count = 0;
369                    sreader.beginArray();
370                    while (sreader.hasNext()) {
371                        if ((count + 1) > stroke.mPoints.length) {
372                            stroke.mPoints = Arrays.copyOf(stroke.mPoints, count * 2);
373                        }
374                        stroke.mPoints[count++] = (float) sreader.nextDouble();
375                    }
376                    stroke.mPath = new Path();
377                    stroke.mPath.moveTo(stroke.mPoints[0], stroke.mPoints[1]);
378                    for (int i = 0; i < count; i += 2) {
379                        stroke.mPath.lineTo(stroke.mPoints[i], stroke.mPoints[i + 1]);
380                    }
381                    sreader.endArray();
382                    strokes.add(stroke);
383                } else {
384                    sreader.skipValue();
385                }
386            }
387            sreader.endObject();
388        }
389
390        mDrawing = strokes;
391
392        sreader.endObject();
393    }
394}
395