BitmapFactory.java revision 25ba1c86945a441428194d9ebcabbf31be75a45a
1/*
2 * Copyright (C) 2007 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.content.res.AssetManager;
20import android.content.res.Resources;
21import android.os.Trace;
22import android.util.DisplayMetrics;
23import android.util.Log;
24import android.util.TypedValue;
25
26import java.io.BufferedInputStream;
27import java.io.FileDescriptor;
28import java.io.FileInputStream;
29import java.io.IOException;
30import java.io.InputStream;
31
32/**
33 * Creates Bitmap objects from various sources, including files, streams,
34 * and byte-arrays.
35 */
36public class BitmapFactory {
37    private static final int DECODE_BUFFER_SIZE = 16 * 1024;
38
39    public static class Options {
40        /**
41         * Create a default Options object, which if left unchanged will give
42         * the same result from the decoder as if null were passed.
43         */
44        public Options() {
45            inDither = false;
46            inScaled = true;
47            inPremultiplied = true;
48        }
49
50        /**
51         * If set, decode methods that take the Options object will attempt to
52         * reuse this bitmap when loading content. If the decode operation
53         * cannot use this bitmap, the decode method will return
54         * <code>null</code> and will throw an IllegalArgumentException. The
55         * current implementation necessitates that the reused bitmap be
56         * mutable, and the resulting reused bitmap will continue to remain
57         * mutable even when decoding a resource which would normally result in
58         * an immutable bitmap.</p>
59         *
60         * <p>As of {@link android.os.Build.VERSION_CODES#KEY_LIME_PIE}, any
61         * mutable bitmap can be reused to decode any other bitmaps as long as
62         * the resulting {@link Bitmap#getByteCount() byte count} of the decoded
63         * bitmap is less than or equal to the {@link
64         * Bitmap#getAllocationByteCount() allocated byte count} of the reused
65         * bitmap. This can be because the intrinsic size is smaller, or its
66         * size post scaling (for density / sample size) is smaller.</p>
67         *
68         * <p>Prior to {@link android.os.Build.VERSION_CODES#KEY_LIME_PIE}
69         * additional constraints apply: The image being decoded (whether as a
70         * resource or as a stream) must be in jpeg or png format. Only equal
71         * sized bitmaps are supported, with {@link #inSampleSize} set to 1.
72         * Additionally, the {@link android.graphics.Bitmap.Config
73         * configuration} of the reused bitmap will override the setting of
74         * {@link #inPreferredConfig}, if set.</p>
75         *
76         * <p>You should still always use the returned Bitmap of the decode
77         * method and not assume that reusing the bitmap worked, due to the
78         * constraints outlined above and failure situations that can occur.
79         * Checking whether the return value matches the value of the inBitmap
80         * set in the Options structure will indicate if the bitmap was reused,
81         * but in all cases you should use the Bitmap returned by the decoding
82         * function to ensure that you are using the bitmap that was used as the
83         * decode destination.</p>
84         *
85         * @see Bitmap#reconfigure(int,int, android.graphics.Bitmap.Config)
86         */
87        public Bitmap inBitmap;
88
89        /**
90         * If set, decode methods will always return a mutable Bitmap instead of
91         * an immutable one. This can be used for instance to programmatically apply
92         * effects to a Bitmap loaded through BitmapFactory.
93         */
94        @SuppressWarnings({"UnusedDeclaration"}) // used in native code
95        public boolean inMutable;
96
97        /**
98         * If set to true, the decoder will return null (no bitmap), but
99         * the out... fields will still be set, allowing the caller to query
100         * the bitmap without having to allocate the memory for its pixels.
101         */
102        public boolean inJustDecodeBounds;
103
104        /**
105         * If set to a value > 1, requests the decoder to subsample the original
106         * image, returning a smaller image to save memory. The sample size is
107         * the number of pixels in either dimension that correspond to a single
108         * pixel in the decoded bitmap. For example, inSampleSize == 4 returns
109         * an image that is 1/4 the width/height of the original, and 1/16 the
110         * number of pixels. Any value <= 1 is treated the same as 1. Note: the
111         * decoder uses a final value based on powers of 2, any other value will
112         * be rounded down to the nearest power of 2.
113         */
114        public int inSampleSize;
115
116        /**
117         * If this is non-null, the decoder will try to decode into this
118         * internal configuration. If it is null, or the request cannot be met,
119         * the decoder will try to pick the best matching config based on the
120         * system's screen depth, and characteristics of the original image such
121         * as if it has per-pixel alpha (requiring a config that also does).
122         *
123         * Image are loaded with the {@link Bitmap.Config#ARGB_8888} config by
124         * default.
125         */
126        public Bitmap.Config inPreferredConfig = Bitmap.Config.ARGB_8888;
127
128        /**
129         * If true (which is the default), the resulting bitmap will have its
130         * color channels pre-multipled by the alpha channel.
131         *
132         * <p>This should NOT be set to false for images to be directly drawn by
133         * the view system or through a {@link Canvas}. The view system and
134         * {@link Canvas} assume all drawn images are pre-multiplied to simplify
135         * draw-time blending, and will throw a RuntimeException when
136         * un-premultiplied are drawn.</p>
137         *
138         * <p>This is likely only useful if you want to manipulate raw encoded
139         * image data, e.g. with RenderScript or custom OpenGL.</p>
140         *
141         * <p>This does not affect bitmaps without an alpha channel.</p>
142         *
143         * @see Bitmap#hasAlpha()
144         * @see Bitmap#isPremultiplied()
145         */
146        public boolean inPremultiplied;
147
148        /**
149         * If dither is true, the decoder will attempt to dither the decoded
150         * image.
151         */
152        public boolean inDither;
153
154        /**
155         * The pixel density to use for the bitmap.  This will always result
156         * in the returned bitmap having a density set for it (see
157         * {@link Bitmap#setDensity(int) Bitmap.setDensity(int)}).  In addition,
158         * if {@link #inScaled} is set (which it is by default} and this
159         * density does not match {@link #inTargetDensity}, then the bitmap
160         * will be scaled to the target density before being returned.
161         *
162         * <p>If this is 0,
163         * {@link BitmapFactory#decodeResource(Resources, int)},
164         * {@link BitmapFactory#decodeResource(Resources, int, android.graphics.BitmapFactory.Options)},
165         * and {@link BitmapFactory#decodeResourceStream}
166         * will fill in the density associated with the resource.  The other
167         * functions will leave it as-is and no density will be applied.
168         *
169         * @see #inTargetDensity
170         * @see #inScreenDensity
171         * @see #inScaled
172         * @see Bitmap#setDensity(int)
173         * @see android.util.DisplayMetrics#densityDpi
174         */
175        public int inDensity;
176
177        /**
178         * The pixel density of the destination this bitmap will be drawn to.
179         * This is used in conjunction with {@link #inDensity} and
180         * {@link #inScaled} to determine if and how to scale the bitmap before
181         * returning it.
182         *
183         * <p>If this is 0,
184         * {@link BitmapFactory#decodeResource(Resources, int)},
185         * {@link BitmapFactory#decodeResource(Resources, int, android.graphics.BitmapFactory.Options)},
186         * and {@link BitmapFactory#decodeResourceStream}
187         * will fill in the density associated the Resources object's
188         * DisplayMetrics.  The other
189         * functions will leave it as-is and no scaling for density will be
190         * performed.
191         *
192         * @see #inDensity
193         * @see #inScreenDensity
194         * @see #inScaled
195         * @see android.util.DisplayMetrics#densityDpi
196         */
197        public int inTargetDensity;
198
199        /**
200         * The pixel density of the actual screen that is being used.  This is
201         * purely for applications running in density compatibility code, where
202         * {@link #inTargetDensity} is actually the density the application
203         * sees rather than the real screen density.
204         *
205         * <p>By setting this, you
206         * allow the loading code to avoid scaling a bitmap that is currently
207         * in the screen density up/down to the compatibility density.  Instead,
208         * if {@link #inDensity} is the same as {@link #inScreenDensity}, the
209         * bitmap will be left as-is.  Anything using the resulting bitmap
210         * must also used {@link Bitmap#getScaledWidth(int)
211         * Bitmap.getScaledWidth} and {@link Bitmap#getScaledHeight
212         * Bitmap.getScaledHeight} to account for any different between the
213         * bitmap's density and the target's density.
214         *
215         * <p>This is never set automatically for the caller by
216         * {@link BitmapFactory} itself.  It must be explicitly set, since the
217         * caller must deal with the resulting bitmap in a density-aware way.
218         *
219         * @see #inDensity
220         * @see #inTargetDensity
221         * @see #inScaled
222         * @see android.util.DisplayMetrics#densityDpi
223         */
224        public int inScreenDensity;
225
226        /**
227         * When this flag is set, if {@link #inDensity} and
228         * {@link #inTargetDensity} are not 0, the
229         * bitmap will be scaled to match {@link #inTargetDensity} when loaded,
230         * rather than relying on the graphics system scaling it each time it
231         * is drawn to a Canvas.
232         *
233         * <p>This flag is turned on by default and should be turned off if you need
234         * a non-scaled version of the bitmap.  Nine-patch bitmaps ignore this
235         * flag and are always scaled.
236         */
237        public boolean inScaled;
238
239        /**
240         * If this is set to true, then the resulting bitmap will allocate its
241         * pixels such that they can be purged if the system needs to reclaim
242         * memory. In that instance, when the pixels need to be accessed again
243         * (e.g. the bitmap is drawn, getPixels() is called), they will be
244         * automatically re-decoded.
245         *
246         * For the re-decode to happen, the bitmap must have access to the
247         * encoded data, either by sharing a reference to the input
248         * or by making a copy of it. This distinction is controlled by
249         * inInputShareable. If this is true, then the bitmap may keep a shallow
250         * reference to the input. If this is false, then the bitmap will
251         * explicitly make a copy of the input data, and keep that. Even if
252         * sharing is allowed, the implementation may still decide to make a
253         * deep copy of the input data.
254         */
255        public boolean inPurgeable;
256
257        /**
258         * This field works in conjuction with inPurgeable. If inPurgeable is
259         * false, then this field is ignored. If inPurgeable is true, then this
260         * field determines whether the bitmap can share a reference to the
261         * input data (inputstream, array, etc.) or if it must make a deep copy.
262         */
263        public boolean inInputShareable;
264
265        /**
266         * If inPreferQualityOverSpeed is set to true, the decoder will try to
267         * decode the reconstructed image to a higher quality even at the
268         * expense of the decoding speed. Currently the field only affects JPEG
269         * decode, in the case of which a more accurate, but slightly slower,
270         * IDCT method will be used instead.
271         */
272        public boolean inPreferQualityOverSpeed;
273
274        /**
275         * The resulting width of the bitmap, set independent of the state of
276         * inJustDecodeBounds. However, if there is an error trying to decode,
277         * outWidth will be set to -1.
278         */
279
280        public int outWidth;
281
282        /**
283         * The resulting height of the bitmap, set independent of the state of
284         * inJustDecodeBounds. However, if there is an error trying to decode,
285         * outHeight will be set to -1.
286         */
287        public int outHeight;
288
289        /**
290         * If known, this string is set to the mimetype of the decoded image.
291         * If not know, or there is an error, it is set to null.
292         */
293        public String outMimeType;
294
295        /**
296         * Temp storage to use for decoding.  Suggest 16K or so.
297         */
298        public byte[] inTempStorage;
299
300        private native void requestCancel();
301
302        /**
303         * Flag to indicate that cancel has been called on this object.  This
304         * is useful if there's an intermediary that wants to first decode the
305         * bounds and then decode the image.  In that case the intermediary
306         * can check, inbetween the bounds decode and the image decode, to see
307         * if the operation is canceled.
308         */
309        public boolean mCancel;
310
311        /**
312         *  This can be called from another thread while this options object is
313         *  inside a decode... call. Calling this will notify the decoder that
314         *  it should cancel its operation. This is not guaranteed to cancel
315         *  the decode, but if it does, the decoder... operation will return
316         *  null, or if inJustDecodeBounds is true, will set outWidth/outHeight
317         *  to -1
318         */
319        public void requestCancelDecode() {
320            mCancel = true;
321            requestCancel();
322        }
323    }
324
325    /**
326     * Decode a file path into a bitmap. If the specified file name is null,
327     * or cannot be decoded into a bitmap, the function returns null.
328     *
329     * @param pathName complete path name for the file to be decoded.
330     * @param opts null-ok; Options that control downsampling and whether the
331     *             image should be completely decoded, or just is size returned.
332     * @return The decoded bitmap, or null if the image data could not be
333     *         decoded, or, if opts is non-null, if opts requested only the
334     *         size be returned (in opts.outWidth and opts.outHeight)
335     */
336    public static Bitmap decodeFile(String pathName, Options opts) {
337        Bitmap bm = null;
338        InputStream stream = null;
339        try {
340            stream = new FileInputStream(pathName);
341            bm = decodeStream(stream, null, opts);
342        } catch (Exception e) {
343            /*  do nothing.
344                If the exception happened on open, bm will be null.
345            */
346            Log.e("BitmapFactory", "Unable to decode stream: " + e);
347        } finally {
348            if (stream != null) {
349                try {
350                    stream.close();
351                } catch (IOException e) {
352                    // do nothing here
353                }
354            }
355        }
356        return bm;
357    }
358
359    /**
360     * Decode a file path into a bitmap. If the specified file name is null,
361     * or cannot be decoded into a bitmap, the function returns null.
362     *
363     * @param pathName complete path name for the file to be decoded.
364     * @return the resulting decoded bitmap, or null if it could not be decoded.
365     */
366    public static Bitmap decodeFile(String pathName) {
367        return decodeFile(pathName, null);
368    }
369
370    /**
371     * Decode a new Bitmap from an InputStream. This InputStream was obtained from
372     * resources, which we pass to be able to scale the bitmap accordingly.
373     */
374    public static Bitmap decodeResourceStream(Resources res, TypedValue value,
375            InputStream is, Rect pad, Options opts) {
376
377        if (opts == null) {
378            opts = new Options();
379        }
380
381        if (opts.inDensity == 0 && value != null) {
382            final int density = value.density;
383            if (density == TypedValue.DENSITY_DEFAULT) {
384                opts.inDensity = DisplayMetrics.DENSITY_DEFAULT;
385            } else if (density != TypedValue.DENSITY_NONE) {
386                opts.inDensity = density;
387            }
388        }
389
390        if (opts.inTargetDensity == 0 && res != null) {
391            opts.inTargetDensity = res.getDisplayMetrics().densityDpi;
392        }
393
394        return decodeStream(is, pad, opts);
395    }
396
397    /**
398     * Synonym for opening the given resource and calling
399     * {@link #decodeResourceStream}.
400     *
401     * @param res   The resources object containing the image data
402     * @param id The resource id of the image data
403     * @param opts null-ok; Options that control downsampling and whether the
404     *             image should be completely decoded, or just is size returned.
405     * @return The decoded bitmap, or null if the image data could not be
406     *         decoded, or, if opts is non-null, if opts requested only the
407     *         size be returned (in opts.outWidth and opts.outHeight)
408     */
409    public static Bitmap decodeResource(Resources res, int id, Options opts) {
410        Bitmap bm = null;
411        InputStream is = null;
412
413        try {
414            final TypedValue value = new TypedValue();
415            is = res.openRawResource(id, value);
416
417            bm = decodeResourceStream(res, value, is, null, opts);
418        } catch (Exception e) {
419            /*  do nothing.
420                If the exception happened on open, bm will be null.
421                If it happened on close, bm is still valid.
422            */
423        } finally {
424            try {
425                if (is != null) is.close();
426            } catch (IOException e) {
427                // Ignore
428            }
429        }
430
431        if (bm == null && opts != null && opts.inBitmap != null) {
432            throw new IllegalArgumentException("Problem decoding into existing bitmap");
433        }
434
435        return bm;
436    }
437
438    /**
439     * Synonym for {@link #decodeResource(Resources, int, android.graphics.BitmapFactory.Options)}
440     * will null Options.
441     *
442     * @param res The resources object containing the image data
443     * @param id The resource id of the image data
444     * @return The decoded bitmap, or null if the image could not be decode.
445     */
446    public static Bitmap decodeResource(Resources res, int id) {
447        return decodeResource(res, id, null);
448    }
449
450    /**
451     * Decode an immutable bitmap from the specified byte array.
452     *
453     * @param data byte array of compressed image data
454     * @param offset offset into imageData for where the decoder should begin
455     *               parsing.
456     * @param length the number of bytes, beginning at offset, to parse
457     * @param opts null-ok; Options that control downsampling and whether the
458     *             image should be completely decoded, or just is size returned.
459     * @return The decoded bitmap, or null if the image data could not be
460     *         decoded, or, if opts is non-null, if opts requested only the
461     *         size be returned (in opts.outWidth and opts.outHeight)
462     */
463    public static Bitmap decodeByteArray(byte[] data, int offset, int length, Options opts) {
464        if ((offset | length) < 0 || data.length < offset + length) {
465            throw new ArrayIndexOutOfBoundsException();
466        }
467
468        Bitmap bm;
469
470        Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, "decodeBitmap");
471        try {
472            bm = nativeDecodeByteArray(data, offset, length, opts);
473
474            if (bm == null && opts != null && opts.inBitmap != null) {
475                throw new IllegalArgumentException("Problem decoding into existing bitmap");
476            }
477            setDensityFromOptions(bm, opts);
478        } finally {
479            Trace.traceEnd(Trace.TRACE_TAG_GRAPHICS);
480        }
481
482        return bm;
483    }
484
485    /**
486     * Decode an immutable bitmap from the specified byte array.
487     *
488     * @param data byte array of compressed image data
489     * @param offset offset into imageData for where the decoder should begin
490     *               parsing.
491     * @param length the number of bytes, beginning at offset, to parse
492     * @return The decoded bitmap, or null if the image could not be decode.
493     */
494    public static Bitmap decodeByteArray(byte[] data, int offset, int length) {
495        return decodeByteArray(data, offset, length, null);
496    }
497
498    /**
499     * Set the newly decoded bitmap's density based on the Options.
500     */
501    private static void setDensityFromOptions(Bitmap outputBitmap, Options opts) {
502        if (outputBitmap == null || opts == null) return;
503
504        final int density = opts.inDensity;
505        if (density != 0) {
506            outputBitmap.setDensity(density);
507            final int targetDensity = opts.inTargetDensity;
508            if (targetDensity == 0 || density == targetDensity || density == opts.inScreenDensity) {
509                return;
510            }
511
512            byte[] np = outputBitmap.getNinePatchChunk();
513            final boolean isNinePatch = np != null && NinePatch.isNinePatchChunk(np);
514            if (opts.inScaled || isNinePatch) {
515                outputBitmap.setDensity(targetDensity);
516            }
517        } else if (opts.inBitmap != null) {
518            // bitmap was reused, ensure density is reset
519            outputBitmap.setDensity(Bitmap.getDefaultDensity());
520        }
521    }
522
523    /**
524     * Decode an input stream into a bitmap. If the input stream is null, or
525     * cannot be used to decode a bitmap, the function returns null.
526     * The stream's position will be where ever it was after the encoded data
527     * was read.
528     *
529     * @param is The input stream that holds the raw data to be decoded into a
530     *           bitmap.
531     * @param outPadding If not null, return the padding rect for the bitmap if
532     *                   it exists, otherwise set padding to [-1,-1,-1,-1]. If
533     *                   no bitmap is returned (null) then padding is
534     *                   unchanged.
535     * @param opts null-ok; Options that control downsampling and whether the
536     *             image should be completely decoded, or just is size returned.
537     * @return The decoded bitmap, or null if the image data could not be
538     *         decoded, or, if opts is non-null, if opts requested only the
539     *         size be returned (in opts.outWidth and opts.outHeight)
540     */
541    public static Bitmap decodeStream(InputStream is, Rect outPadding, Options opts) {
542        // we don't throw in this case, thus allowing the caller to only check
543        // the cache, and not force the image to be decoded.
544        if (is == null) {
545            return null;
546        }
547
548        Bitmap bm;
549
550        Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, "decodeBitmap");
551        try {
552            // we need mark/reset to work properly
553            if (!is.markSupported()) {
554                is = new BufferedInputStream(is, DECODE_BUFFER_SIZE);
555            }
556
557            // so we can call reset() if a given codec gives up after reading up to
558            // this many bytes. FIXME: need to find out from the codecs what this
559            // value should be.
560            is.mark(1024);
561
562            if (is instanceof AssetManager.AssetInputStream) {
563                final int asset = ((AssetManager.AssetInputStream) is).getAssetInt();
564                bm = nativeDecodeAsset(asset, outPadding, opts);
565            } else {
566                // pass some temp storage down to the native code. 1024 is made up,
567                // but should be large enough to avoid too many small calls back
568                // into is.read(...) This number is not related to the value passed
569                // to mark(...) above.
570                byte [] tempStorage = null;
571                if (opts != null) tempStorage = opts.inTempStorage;
572                if (tempStorage == null) tempStorage = new byte[DECODE_BUFFER_SIZE];
573                bm = nativeDecodeStream(is, tempStorage, outPadding, opts);
574            }
575
576            if (bm == null && opts != null && opts.inBitmap != null) {
577                throw new IllegalArgumentException("Problem decoding into existing bitmap");
578            }
579
580            setDensityFromOptions(bm, opts);
581        } finally {
582            Trace.traceEnd(Trace.TRACE_TAG_GRAPHICS);
583        }
584
585        return bm;
586    }
587
588    /**
589     * Decode an input stream into a bitmap. If the input stream is null, or
590     * cannot be used to decode a bitmap, the function returns null.
591     * The stream's position will be where ever it was after the encoded data
592     * was read.
593     *
594     * @param is The input stream that holds the raw data to be decoded into a
595     *           bitmap.
596     * @return The decoded bitmap, or null if the image data could not be decoded.
597     */
598    public static Bitmap decodeStream(InputStream is) {
599        return decodeStream(is, null, null);
600    }
601
602    /**
603     * Decode a bitmap from the file descriptor. If the bitmap cannot be decoded
604     * return null. The position within the descriptor will not be changed when
605     * this returns, so the descriptor can be used again as-is.
606     *
607     * @param fd The file descriptor containing the bitmap data to decode
608     * @param outPadding If not null, return the padding rect for the bitmap if
609     *                   it exists, otherwise set padding to [-1,-1,-1,-1]. If
610     *                   no bitmap is returned (null) then padding is
611     *                   unchanged.
612     * @param opts null-ok; Options that control downsampling and whether the
613     *             image should be completely decoded, or just is size returned.
614     * @return the decoded bitmap, or null
615     */
616    public static Bitmap decodeFileDescriptor(FileDescriptor fd, Rect outPadding, Options opts) {
617        if (nativeIsSeekable(fd)) {
618            Bitmap bm = nativeDecodeFileDescriptor(fd, outPadding, opts);
619            if (bm == null && opts != null && opts.inBitmap != null) {
620                throw new IllegalArgumentException("Problem decoding into existing bitmap");
621            }
622            return bm;
623        } else {
624            FileInputStream fis = new FileInputStream(fd);
625            try {
626                return decodeStream(fis, outPadding, opts);
627            } finally {
628                try {
629                    fis.close();
630                } catch (Throwable t) {/* ignore */}
631            }
632        }
633    }
634
635    /**
636     * Decode a bitmap from the file descriptor. If the bitmap cannot be decoded
637     * return null. The position within the descriptor will not be changed when
638     * this returns, so the descriptor can be used again as is.
639     *
640     * @param fd The file descriptor containing the bitmap data to decode
641     * @return the decoded bitmap, or null
642     */
643    public static Bitmap decodeFileDescriptor(FileDescriptor fd) {
644        return decodeFileDescriptor(fd, null, null);
645    }
646
647    private static native Bitmap nativeDecodeStream(InputStream is, byte[] storage,
648            Rect padding, Options opts);
649    private static native Bitmap nativeDecodeFileDescriptor(FileDescriptor fd,
650            Rect padding, Options opts);
651    private static native Bitmap nativeDecodeAsset(int asset, Rect padding, Options opts);
652    private static native Bitmap nativeDecodeByteArray(byte[] data, int offset,
653            int length, Options opts);
654    private static native boolean nativeIsSeekable(FileDescriptor fd);
655}
656