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 android.graphics;
18
19import android.os.Parcel;
20import android.os.Parcelable;
21
22/**
23 * Simple wrapper for the native GraphicBuffer class.
24 *
25 * @hide
26 */
27@SuppressWarnings("UnusedDeclaration")
28public class GraphicBuffer implements Parcelable {
29    // Note: keep usage flags in sync with GraphicBuffer.h and gralloc.h
30    public static final int USAGE_SW_READ_NEVER = 0x0;
31    public static final int USAGE_SW_READ_RARELY = 0x2;
32    public static final int USAGE_SW_READ_OFTEN = 0x3;
33    public static final int USAGE_SW_READ_MASK = 0xF;
34
35    public static final int USAGE_SW_WRITE_NEVER = 0x0;
36    public static final int USAGE_SW_WRITE_RARELY = 0x20;
37    public static final int USAGE_SW_WRITE_OFTEN = 0x30;
38    public static final int USAGE_SW_WRITE_MASK = 0xF0;
39
40    public static final int USAGE_SOFTWARE_MASK = USAGE_SW_READ_MASK | USAGE_SW_WRITE_MASK;
41
42    public static final int USAGE_PROTECTED = 0x4000;
43
44    public static final int USAGE_HW_TEXTURE = 0x100;
45    public static final int USAGE_HW_RENDER = 0x200;
46    public static final int USAGE_HW_2D = 0x400;
47    public static final int USAGE_HW_COMPOSER = 0x800;
48    public static final int USAGE_HW_VIDEO_ENCODER = 0x10000;
49    public static final int USAGE_HW_MASK = 0x71F00;
50
51    private final int mWidth;
52    private final int mHeight;
53    private final int mFormat;
54    private final int mUsage;
55    // Note: do not rename, this field is used by native code
56    private final long mNativeObject;
57
58    // These two fields are only used by lock/unlockCanvas()
59    private Canvas mCanvas;
60    private int mSaveCount;
61
62    // If set to true, this GraphicBuffer instance cannot be used anymore
63    private boolean mDestroyed;
64
65    /**
66     * Creates new <code>GraphicBuffer</code> instance. This method will return null
67     * if the buffer cannot be created.
68     *
69     * @param width The width in pixels of the buffer
70     * @param height The height in pixels of the buffer
71     * @param format The format of each pixel as specified in {@link PixelFormat}
72     * @param usage Hint indicating how the buffer will be used
73     *
74     * @return A <code>GraphicBuffer</code> instance or null
75     */
76    public static GraphicBuffer create(int width, int height, int format, int usage) {
77        long nativeObject = nCreateGraphicBuffer(width, height, format, usage);
78        if (nativeObject != 0) {
79            return new GraphicBuffer(width, height, format, usage, nativeObject);
80        }
81        return null;
82    }
83
84    /**
85     * Private use only. See {@link #create(int, int, int, int)}.
86     */
87    private GraphicBuffer(int width, int height, int format, int usage, long nativeObject) {
88        mWidth = width;
89        mHeight = height;
90        mFormat = format;
91        mUsage = usage;
92        mNativeObject = nativeObject;
93    }
94
95    /**
96     * For SurfaceControl JNI.
97     * @hide
98     */
99    public static GraphicBuffer createFromExisting(int width, int height,
100            int format, int usage, long unwrappedNativeObject) {
101        long nativeObject = nWrapGraphicBuffer(unwrappedNativeObject);
102        if (nativeObject != 0) {
103            return new GraphicBuffer(width, height, format, usage, nativeObject);
104        }
105        return null;
106    }
107
108    /**
109     * Returns the width of this buffer in pixels.
110     */
111    public int getWidth() {
112        return mWidth;
113    }
114
115    /**
116     * Returns the height of this buffer in pixels.
117     */
118    public int getHeight() {
119        return mHeight;
120    }
121
122    /**
123     * Returns the pixel format of this buffer. The pixel format must be one of
124     * the formats defined in {@link PixelFormat}.
125     */
126    public int getFormat() {
127        return mFormat;
128    }
129
130    /**
131     * Returns the usage hint set on this buffer.
132     */
133    public int getUsage() {
134        return mUsage;
135    }
136
137    /**
138     * <p>Start editing the pixels in the buffer. A null is returned if the buffer
139     * cannot be locked for editing.</p>
140     *
141     * <p>The content of the buffer is preserved between unlockCanvas()
142     * and lockCanvas().</p>
143     *
144     * <p>If this method is called after {@link #destroy()}, the return value will
145     * always be null.</p>
146     *
147     * @return A Canvas used to draw into the buffer, or null.
148     *
149     * @see #lockCanvas(android.graphics.Rect)
150     * @see #unlockCanvasAndPost(android.graphics.Canvas)
151     * @see #isDestroyed()
152     */
153    public Canvas lockCanvas() {
154        return lockCanvas(null);
155    }
156
157    /**
158     * Just like {@link #lockCanvas()} but allows specification of a dirty
159     * rectangle.
160     *
161     * <p>If this method is called after {@link #destroy()}, the return value will
162     * always be null.</p>
163     *
164     * @param dirty Area of the buffer that may be modified.
165
166     * @return A Canvas used to draw into the surface, or null.
167     *
168     * @see #lockCanvas()
169     * @see #unlockCanvasAndPost(android.graphics.Canvas)
170     * @see #isDestroyed()
171     */
172    public Canvas lockCanvas(Rect dirty) {
173        if (mDestroyed) {
174            return null;
175        }
176
177        if (mCanvas == null) {
178            mCanvas = new Canvas();
179        }
180
181        if (nLockCanvas(mNativeObject, mCanvas, dirty)) {
182            mSaveCount = mCanvas.save();
183            return mCanvas;
184        }
185
186        return null;
187    }
188
189    /**
190     * Finish editing pixels in the buffer.
191     *
192     * <p>This method doesn't do anything if {@link #destroy()} was
193     * previously called.</p>
194     *
195     * @param canvas The Canvas previously returned by lockCanvas()
196     *
197     * @see #lockCanvas()
198     * @see #lockCanvas(android.graphics.Rect)
199     * @see #isDestroyed()
200     */
201    public void unlockCanvasAndPost(Canvas canvas) {
202        if (!mDestroyed && mCanvas != null && canvas == mCanvas) {
203            canvas.restoreToCount(mSaveCount);
204            mSaveCount = 0;
205
206            nUnlockCanvasAndPost(mNativeObject, mCanvas);
207        }
208    }
209
210    /**
211     * Destroyes this buffer immediately. Calling this method frees up any
212     * underlying native resources. After calling this method, this buffer
213     * must not be used in any way ({@link #lockCanvas()} must not be called,
214     * etc.)
215     *
216     * @see #isDestroyed()
217     */
218    public void destroy() {
219        if (!mDestroyed) {
220            mDestroyed = true;
221            nDestroyGraphicBuffer(mNativeObject);
222        }
223    }
224
225    /**
226     * Indicates whether this buffer has been destroyed. A destroyed buffer
227     * cannot be used in any way: locking a Canvas will return null, the buffer
228     * cannot be written to a parcel, etc.
229     *
230     * @return True if this <code>GraphicBuffer</code> is in a destroyed state,
231     *         false otherwise.
232     *
233     * @see #destroy()
234     */
235    public boolean isDestroyed() {
236        return mDestroyed;
237    }
238
239    @Override
240    protected void finalize() throws Throwable {
241        try {
242            if (!mDestroyed) nDestroyGraphicBuffer(mNativeObject);
243        } finally {
244            super.finalize();
245        }
246    }
247
248    @Override
249    public int describeContents() {
250        return 0;
251    }
252
253    /**
254     * Flatten this object in to a Parcel.
255     *
256     * <p>Calling this method will throw an <code>IllegalStateException</code> if
257     * {@link #destroy()} has been previously called.</p>
258     *
259     * @param dest The Parcel in which the object should be written.
260     * @param flags Additional flags about how the object should be written.
261     *              May be 0 or {@link #PARCELABLE_WRITE_RETURN_VALUE}.
262     */
263    @Override
264    public void writeToParcel(Parcel dest, int flags) {
265        if (mDestroyed) {
266            throw new IllegalStateException("This GraphicBuffer has been destroyed and cannot be "
267                    + "written to a parcel.");
268        }
269
270        dest.writeInt(mWidth);
271        dest.writeInt(mHeight);
272        dest.writeInt(mFormat);
273        dest.writeInt(mUsage);
274        nWriteGraphicBufferToParcel(mNativeObject, dest);
275    }
276
277    public static final Parcelable.Creator<GraphicBuffer> CREATOR =
278            new Parcelable.Creator<GraphicBuffer>() {
279        public GraphicBuffer createFromParcel(Parcel in) {
280            int width = in.readInt();
281            int height = in.readInt();
282            int format = in.readInt();
283            int usage = in.readInt();
284            long nativeObject = nReadGraphicBufferFromParcel(in);
285            if (nativeObject != 0) {
286                return new GraphicBuffer(width, height, format, usage, nativeObject);
287            }
288            return null;
289        }
290
291        public GraphicBuffer[] newArray(int size) {
292            return new GraphicBuffer[size];
293        }
294    };
295
296    private static native long nCreateGraphicBuffer(int width, int height, int format, int usage);
297    private static native void nDestroyGraphicBuffer(long nativeObject);
298    private static native void nWriteGraphicBufferToParcel(long nativeObject, Parcel dest);
299    private static native long nReadGraphicBufferFromParcel(Parcel in);
300    private static native boolean nLockCanvas(long nativeObject, Canvas canvas, Rect dirty);
301    private static native boolean nUnlockCanvasAndPost(long nativeObject, Canvas canvas);
302    private static native long nWrapGraphicBuffer(long nativeObject);
303}
304