ETC1Util.java revision 8af9649d44745adba8be4db4e96af053ba32f2c5
15f7643150411b16e71bc012c6ceb2d865c0a34d4Ted Kremenek/*
2cb7464ab402d057849dda9749d62a62d86c35ab8Zhongxing Xu *  Copyright 2009 Google Inc.
3cb7464ab402d057849dda9749d62a62d86c35ab8Zhongxing Xu *
4cb7464ab402d057849dda9749d62a62d86c35ab8Zhongxing Xu * Licensed under the Apache License, Version 2.0 (the "License");
5cb7464ab402d057849dda9749d62a62d86c35ab8Zhongxing Xu * you may not use this file except in compliance with the License.
6cb7464ab402d057849dda9749d62a62d86c35ab8Zhongxing Xu * You may obtain a copy of the License at
7cb7464ab402d057849dda9749d62a62d86c35ab8Zhongxing Xu *
8cb7464ab402d057849dda9749d62a62d86c35ab8Zhongxing Xu *     http://www.apache.org/licenses/LICENSE-2.0
9cb7464ab402d057849dda9749d62a62d86c35ab8Zhongxing Xu *
10cb7464ab402d057849dda9749d62a62d86c35ab8Zhongxing Xu * Unless required by applicable law or agreed to in writing, software
11cb7464ab402d057849dda9749d62a62d86c35ab8Zhongxing Xu * distributed under the License is distributed on an "AS IS" BASIS,
12cb7464ab402d057849dda9749d62a62d86c35ab8Zhongxing Xu * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13cb7464ab402d057849dda9749d62a62d86c35ab8Zhongxing Xu * See the License for the specific language governing permissions and
149b663716449b618ba0390b1dbebc54fa8e971124Ted Kremenek * limitations under the License.
15cb7464ab402d057849dda9749d62a62d86c35ab8Zhongxing Xu */
16337e4dbc6859589b8878146a88bebf754e916702Ted Kremenek
17563ea2335d7d0df44bbfe8941f64523e8af1fc14Jordan Rosepackage android.opengl;
1855fc873017f10f6f566b182b70f6fc22aefa3464Chandler Carruth
1955fc873017f10f6f566b182b70f6fc22aefa3464Chandler Carruthimport java.io.IOException;
2055fc873017f10f6f566b182b70f6fc22aefa3464Chandler Carruthimport java.io.InputStream;
21cb7464ab402d057849dda9749d62a62d86c35ab8Zhongxing Xuimport java.io.OutputStream;
22cb7464ab402d057849dda9749d62a62d86c35ab8Zhongxing Xuimport java.nio.Buffer;
239ef6537a894c33003359b1f9b9676e9178e028b7Ted Kremenekimport java.nio.ByteBuffer;
24cb7464ab402d057849dda9749d62a62d86c35ab8Zhongxing Xuimport java.nio.ByteOrder;
25eea72a925f294225391ecec876a342771c09b635Ted Kremenek
26eea72a925f294225391ecec876a342771c09b635Ted Kremenek/**
27eea72a925f294225391ecec876a342771c09b635Ted Kremenek * Utility methods for using ETC1 compressed textures.
2866c486f275531df6362b3511fc3af6563561801bTed Kremenek *
296a835dddf45922e71a87637fdfac0863de65123cTed Kremenek */
308bef8238181a30e52dea380789a7e2d760eac532Ted Kremenekpublic class ETC1Util {
315eca482fe895ea57bc82410222e6426c09e63284Ted Kremenek    /**
32cb7464ab402d057849dda9749d62a62d86c35ab8Zhongxing Xu     * Convenience method to load an ETC1 texture whether or not the active OpenGL context
33c210cb7a358d14cdd93b58562f33ff5ed2d895c1Jordan Rose     * supports the ETC1 texture compression format.
34cb7464ab402d057849dda9749d62a62d86c35ab8Zhongxing Xu     * @param target the texture target.
356ebea89be233eaba5e29de8cf3524ad150c860bbJordan Rose     * @param level the texture level
366ebea89be233eaba5e29de8cf3524ad150c860bbJordan Rose     * @param border the border size. Typically 0.
37eafb5c694cc5d165149fcb9453bc9355fb0d44a5Jordan Rose     * @param fallbackFormat the format to use if ETC1 texture compression is not supported.
38eafb5c694cc5d165149fcb9453bc9355fb0d44a5Jordan Rose     * Must be GL_RGB.
396ebea89be233eaba5e29de8cf3524ad150c860bbJordan Rose     * @param fallbackType the type to use if ETC1 texture compression is not supported.
40eafb5c694cc5d165149fcb9453bc9355fb0d44a5Jordan Rose     * Can be either GL_UNSIGNED_SHORT_5_6_5, which implies 16-bits-per-pixel,
41eafb5c694cc5d165149fcb9453bc9355fb0d44a5Jordan Rose     * or GL_UNSIGNED_BYTE, which implies 24-bits-per-pixel.
42eafb5c694cc5d165149fcb9453bc9355fb0d44a5Jordan Rose     * @param input the input stream containing an ETC1 texture in PKM format.
43eafb5c694cc5d165149fcb9453bc9355fb0d44a5Jordan Rose     * @throws IOException
44eafb5c694cc5d165149fcb9453bc9355fb0d44a5Jordan Rose     */
45cb7464ab402d057849dda9749d62a62d86c35ab8Zhongxing Xu    public static void loadTexture(int target, int level, int border,
46eafb5c694cc5d165149fcb9453bc9355fb0d44a5Jordan Rose            int fallbackFormat, int fallbackType, InputStream input)
47eafb5c694cc5d165149fcb9453bc9355fb0d44a5Jordan Rose        throws IOException {
485e5440ba9c135f523f72e7e7c5da59d390d697c5Jordan Rose        loadTexture(target, level, border, fallbackFormat, fallbackType, createTexture(input));
49cb7464ab402d057849dda9749d62a62d86c35ab8Zhongxing Xu    }
50cb7464ab402d057849dda9749d62a62d86c35ab8Zhongxing Xu
51a8d937e4bdd39cdf503f77454e9dc4c9c730a9f7Jordan Rose    /**
52a8d937e4bdd39cdf503f77454e9dc4c9c730a9f7Jordan Rose     * Convenience method to load an ETC1 texture whether or not the active OpenGL context
53bc403861bc4e6f7ad1371e9e129f0f25b38b3a9aJordan Rose     * supports the ETC1 texture compression format.
54a8d937e4bdd39cdf503f77454e9dc4c9c730a9f7Jordan Rose     * @param target the texture target.
55a8d937e4bdd39cdf503f77454e9dc4c9c730a9f7Jordan Rose     * @param level the texture level
56a8d937e4bdd39cdf503f77454e9dc4c9c730a9f7Jordan Rose     * @param border the border size. Typically 0.
57a8d937e4bdd39cdf503f77454e9dc4c9c730a9f7Jordan Rose     * @param fallbackFormat the format to use if ETC1 texture compression is not supported.
58a8d937e4bdd39cdf503f77454e9dc4c9c730a9f7Jordan Rose     * Must be GL_RGB.
59a8d937e4bdd39cdf503f77454e9dc4c9c730a9f7Jordan Rose     * @param fallbackType the type to use if ETC1 texture compression is not supported.
60a8d937e4bdd39cdf503f77454e9dc4c9c730a9f7Jordan Rose     * Can be either GL_UNSIGNED_SHORT_5_6_5, which implies 16-bits-per-pixel,
61a8d937e4bdd39cdf503f77454e9dc4c9c730a9f7Jordan Rose     * or GL_UNSIGNED_BYTE, which implies 24-bits-per-pixel.
62a8d937e4bdd39cdf503f77454e9dc4c9c730a9f7Jordan Rose     * @param texture the ETC1 to load.
63a8d937e4bdd39cdf503f77454e9dc4c9c730a9f7Jordan Rose     */
64a8d937e4bdd39cdf503f77454e9dc4c9c730a9f7Jordan Rose    public static void loadTexture(int target, int level, int border,
65a8d937e4bdd39cdf503f77454e9dc4c9c730a9f7Jordan Rose            int fallbackFormat, int fallbackType, ETC1Texture texture) {
66a8d937e4bdd39cdf503f77454e9dc4c9c730a9f7Jordan Rose        if (fallbackFormat != GLES10.GL_RGB) {
67a8d937e4bdd39cdf503f77454e9dc4c9c730a9f7Jordan Rose            throw new IllegalArgumentException("fallbackFormat must be GL_RGB");
68a8d937e4bdd39cdf503f77454e9dc4c9c730a9f7Jordan Rose        }
69bc403861bc4e6f7ad1371e9e129f0f25b38b3a9aJordan Rose        if (! (fallbackType == GLES10.GL_UNSIGNED_SHORT_5_6_5
70bc403861bc4e6f7ad1371e9e129f0f25b38b3a9aJordan Rose                || fallbackType == GLES10.GL_UNSIGNED_BYTE)) {
71bc403861bc4e6f7ad1371e9e129f0f25b38b3a9aJordan Rose            throw new IllegalArgumentException("Unsupported fallbackType");
72bc403861bc4e6f7ad1371e9e129f0f25b38b3a9aJordan Rose        }
73bc403861bc4e6f7ad1371e9e129f0f25b38b3a9aJordan Rose
74bc403861bc4e6f7ad1371e9e129f0f25b38b3a9aJordan Rose        int width = texture.getWidth();
75bc403861bc4e6f7ad1371e9e129f0f25b38b3a9aJordan Rose        int height = texture.getHeight();
76bc403861bc4e6f7ad1371e9e129f0f25b38b3a9aJordan Rose        Buffer data = texture.getData();
77a8d937e4bdd39cdf503f77454e9dc4c9c730a9f7Jordan Rose        if (isETC1Supported()) {
78a8d937e4bdd39cdf503f77454e9dc4c9c730a9f7Jordan Rose            int imageSize = data.remaining();
79dc84cd5efdd3430efb22546b4ac656aa0540b210David Blaikie            GLES10.glCompressedTexImage2D(target, level, ETC1.ETC1_RGB8_OES, width, height,
80bc403861bc4e6f7ad1371e9e129f0f25b38b3a9aJordan Rose                    border, imageSize, data);
81a8d937e4bdd39cdf503f77454e9dc4c9c730a9f7Jordan Rose        } else {
82a8d937e4bdd39cdf503f77454e9dc4c9c730a9f7Jordan Rose            boolean useShorts = fallbackType != GLES10.GL_UNSIGNED_BYTE;
83bc403861bc4e6f7ad1371e9e129f0f25b38b3a9aJordan Rose            int pixelSize = useShorts ? 2 : 3;
84a8d937e4bdd39cdf503f77454e9dc4c9c730a9f7Jordan Rose            int stride = pixelSize * width;
85a8d937e4bdd39cdf503f77454e9dc4c9c730a9f7Jordan Rose            ByteBuffer decodedData = ByteBuffer.allocateDirect(stride*height)
86bc403861bc4e6f7ad1371e9e129f0f25b38b3a9aJordan Rose                .order(ByteOrder.nativeOrder());
87a8d937e4bdd39cdf503f77454e9dc4c9c730a9f7Jordan Rose            ETC1.decodeImage(data, decodedData, width, height, pixelSize, stride);
88bc403861bc4e6f7ad1371e9e129f0f25b38b3a9aJordan Rose            GLES10.glTexImage2D(target, level, fallbackFormat, width, height, border,
89bc403861bc4e6f7ad1371e9e129f0f25b38b3a9aJordan Rose                    fallbackFormat, fallbackType, decodedData);
90bc403861bc4e6f7ad1371e9e129f0f25b38b3a9aJordan Rose        }
91a8d937e4bdd39cdf503f77454e9dc4c9c730a9f7Jordan Rose    }
92a8d937e4bdd39cdf503f77454e9dc4c9c730a9f7Jordan Rose
93a8d937e4bdd39cdf503f77454e9dc4c9c730a9f7Jordan Rose    /**
94a8d937e4bdd39cdf503f77454e9dc4c9c730a9f7Jordan Rose     * Check if ETC1 texture compression is supported by the active OpenGL ES context.
95bc403861bc4e6f7ad1371e9e129f0f25b38b3a9aJordan Rose     * @return true if the active OpenGL ES context supports ETC1 texture compression.
96bc403861bc4e6f7ad1371e9e129f0f25b38b3a9aJordan Rose     */
97bc403861bc4e6f7ad1371e9e129f0f25b38b3a9aJordan Rose    public static boolean isETC1Supported() {
98bc403861bc4e6f7ad1371e9e129f0f25b38b3a9aJordan Rose        int[] results = new int[20];
99362a31cacc19764f3630928a9e4779af2576e074Jordan Rose        GLES10.glGetIntegerv(GLES10.GL_NUM_COMPRESSED_TEXTURE_FORMATS, results, 0);
1005fe98728dca1f3a7a378ce1a21984a0f8a0c0b8bTed Kremenek        int numFormats = results[0];
1015fe98728dca1f3a7a378ce1a21984a0f8a0c0b8bTed Kremenek        if (numFormats > results.length) {
102888c90ac0ef6baf7d47e86cf5cc4715707d223b1Jordan Rose            results = new int[numFormats];
103075f6fbcb4d858c09e9b138f8dc10d8d3d43d935Jordan Rose        }
104075f6fbcb4d858c09e9b138f8dc10d8d3d43d935Jordan Rose        GLES10.glGetIntegerv(GLES10.GL_COMPRESSED_TEXTURE_FORMATS, results, 0);
105888c90ac0ef6baf7d47e86cf5cc4715707d223b1Jordan Rose        for (int i = 0; i < numFormats; i++) {
106bc403861bc4e6f7ad1371e9e129f0f25b38b3a9aJordan Rose            if (results[i] == ETC1.ETC1_RGB8_OES) {
107888c90ac0ef6baf7d47e86cf5cc4715707d223b1Jordan Rose                return true;
108888c90ac0ef6baf7d47e86cf5cc4715707d223b1Jordan Rose            }
109888c90ac0ef6baf7d47e86cf5cc4715707d223b1Jordan Rose        }
110075f6fbcb4d858c09e9b138f8dc10d8d3d43d935Jordan Rose        return false;
111075f6fbcb4d858c09e9b138f8dc10d8d3d43d935Jordan Rose    }
11266c486f275531df6362b3511fc3af6563561801bTed Kremenek
11366c486f275531df6362b3511fc3af6563561801bTed Kremenek    /**
11466c486f275531df6362b3511fc3af6563561801bTed Kremenek     * A utility class encapsulating a compressed ETC1 texture.
115075f6fbcb4d858c09e9b138f8dc10d8d3d43d935Jordan Rose     */
116075f6fbcb4d858c09e9b138f8dc10d8d3d43d935Jordan Rose    public static class ETC1Texture {
117b07805485c603be3d8011f72611465324c9e664bDavid Blaikie        public ETC1Texture(int width, int height, ByteBuffer data) {
118b07805485c603be3d8011f72611465324c9e664bDavid Blaikie            mWidth = width;
119e460c46c5d602f65354cab0879c458890273591cJordan Rose            mHeight = height;
120e460c46c5d602f65354cab0879c458890273591cJordan Rose            mData = data;
121e460c46c5d602f65354cab0879c458890273591cJordan Rose        }
122e460c46c5d602f65354cab0879c458890273591cJordan Rose
123e460c46c5d602f65354cab0879c458890273591cJordan Rose        /**
124e460c46c5d602f65354cab0879c458890273591cJordan Rose         * Get the width of the texture in pixels.
125e460c46c5d602f65354cab0879c458890273591cJordan Rose         * @return the width of the texture in pixels.
126e460c46c5d602f65354cab0879c458890273591cJordan Rose         */
127e460c46c5d602f65354cab0879c458890273591cJordan Rose        public int getWidth() { return mWidth; }
128e460c46c5d602f65354cab0879c458890273591cJordan Rose
129e460c46c5d602f65354cab0879c458890273591cJordan Rose        /**
130bc403861bc4e6f7ad1371e9e129f0f25b38b3a9aJordan Rose         * Get the height of the texture in pixels.
131e460c46c5d602f65354cab0879c458890273591cJordan Rose         * @return the width of the texture in pixels.
132e460c46c5d602f65354cab0879c458890273591cJordan Rose         */
133e460c46c5d602f65354cab0879c458890273591cJordan Rose        public int getHeight() { return mHeight; }
134e460c46c5d602f65354cab0879c458890273591cJordan Rose
135e460c46c5d602f65354cab0879c458890273591cJordan Rose        /**
136e460c46c5d602f65354cab0879c458890273591cJordan Rose         * Get the compressed data of the texture.
137e460c46c5d602f65354cab0879c458890273591cJordan Rose         * @return the texture data.
138e460c46c5d602f65354cab0879c458890273591cJordan Rose         */
1393a0a9e3e8bbaa45f3ca22b1e20b3beaac0f5861eJordan Rose        public ByteBuffer getData() { return mData; }
140b07805485c603be3d8011f72611465324c9e664bDavid Blaikie
141b07805485c603be3d8011f72611465324c9e664bDavid Blaikie        private int mWidth;
1423a0a9e3e8bbaa45f3ca22b1e20b3beaac0f5861eJordan Rose        private int mHeight;
1433a0a9e3e8bbaa45f3ca22b1e20b3beaac0f5861eJordan Rose        private ByteBuffer mData;
1443a0a9e3e8bbaa45f3ca22b1e20b3beaac0f5861eJordan Rose    }
1453a0a9e3e8bbaa45f3ca22b1e20b3beaac0f5861eJordan Rose
1463a0a9e3e8bbaa45f3ca22b1e20b3beaac0f5861eJordan Rose    /**
1473a0a9e3e8bbaa45f3ca22b1e20b3beaac0f5861eJordan Rose     * Create a new ETC1Texture from an input stream containing a PKM formatted compressed texture.
1483a0a9e3e8bbaa45f3ca22b1e20b3beaac0f5861eJordan Rose     * @param input an input stream containing a PKM formatted compressed texture.
1493a0a9e3e8bbaa45f3ca22b1e20b3beaac0f5861eJordan Rose     * @return an ETC1Texture read from the input stream.
1503a0a9e3e8bbaa45f3ca22b1e20b3beaac0f5861eJordan Rose     * @throws IOException
151e460c46c5d602f65354cab0879c458890273591cJordan Rose     */
1523a0a9e3e8bbaa45f3ca22b1e20b3beaac0f5861eJordan Rose    public static ETC1Texture createTexture(InputStream input) throws IOException {
1533a0a9e3e8bbaa45f3ca22b1e20b3beaac0f5861eJordan Rose        int width = 0;
154e460c46c5d602f65354cab0879c458890273591cJordan Rose        int height = 0;
1553a0a9e3e8bbaa45f3ca22b1e20b3beaac0f5861eJordan Rose        byte[] ioBuffer = new byte[4096];
1563a0a9e3e8bbaa45f3ca22b1e20b3beaac0f5861eJordan Rose        {
1573a0a9e3e8bbaa45f3ca22b1e20b3beaac0f5861eJordan Rose            if (input.read(ioBuffer, 0, ETC1.ETC_PKM_HEADER_SIZE) != ETC1.ETC_PKM_HEADER_SIZE) {
158075f6fbcb4d858c09e9b138f8dc10d8d3d43d935Jordan Rose                throw new IOException("Unable to read PKM file header.");
159075f6fbcb4d858c09e9b138f8dc10d8d3d43d935Jordan Rose            }
160075f6fbcb4d858c09e9b138f8dc10d8d3d43d935Jordan Rose            ByteBuffer headerBuffer = ByteBuffer.allocateDirect(ETC1.ETC_PKM_HEADER_SIZE)
161c210cb7a358d14cdd93b58562f33ff5ed2d895c1Jordan Rose                .order(ByteOrder.nativeOrder());
162c210cb7a358d14cdd93b58562f33ff5ed2d895c1Jordan Rose            headerBuffer.put(ioBuffer, 0, ETC1.ETC_PKM_HEADER_SIZE).position(0);
163c210cb7a358d14cdd93b58562f33ff5ed2d895c1Jordan Rose            if (!ETC1.isValid(headerBuffer)) {
164c210cb7a358d14cdd93b58562f33ff5ed2d895c1Jordan Rose                throw new IOException("Not a PKM file.");
165c210cb7a358d14cdd93b58562f33ff5ed2d895c1Jordan Rose            }
166c210cb7a358d14cdd93b58562f33ff5ed2d895c1Jordan Rose            width = ETC1.getWidth(headerBuffer);
167075f6fbcb4d858c09e9b138f8dc10d8d3d43d935Jordan Rose            height = ETC1.getHeight(headerBuffer);
168888c90ac0ef6baf7d47e86cf5cc4715707d223b1Jordan Rose        }
169888c90ac0ef6baf7d47e86cf5cc4715707d223b1Jordan Rose        int encodedSize = ETC1.getEncodedDataSize(width, height);
170888c90ac0ef6baf7d47e86cf5cc4715707d223b1Jordan Rose        ByteBuffer dataBuffer = ByteBuffer.allocateDirect(encodedSize).order(ByteOrder.nativeOrder());
171888c90ac0ef6baf7d47e86cf5cc4715707d223b1Jordan Rose        for (int i = 0; i < encodedSize; ) {
172888c90ac0ef6baf7d47e86cf5cc4715707d223b1Jordan Rose            int chunkSize = Math.min(ioBuffer.length, encodedSize - i);
173888c90ac0ef6baf7d47e86cf5cc4715707d223b1Jordan Rose            if (input.read(ioBuffer, 0, chunkSize) != chunkSize) {
174888c90ac0ef6baf7d47e86cf5cc4715707d223b1Jordan Rose                throw new IOException("Unable to read PKM file data.");
175888c90ac0ef6baf7d47e86cf5cc4715707d223b1Jordan Rose            }
176075f6fbcb4d858c09e9b138f8dc10d8d3d43d935Jordan Rose            dataBuffer.put(ioBuffer, 0, chunkSize);
177888c90ac0ef6baf7d47e86cf5cc4715707d223b1Jordan Rose            i += chunkSize;
178888c90ac0ef6baf7d47e86cf5cc4715707d223b1Jordan Rose        }
179888c90ac0ef6baf7d47e86cf5cc4715707d223b1Jordan Rose        dataBuffer.position(0);
180888c90ac0ef6baf7d47e86cf5cc4715707d223b1Jordan Rose        return new ETC1Texture(width, height, dataBuffer);
181888c90ac0ef6baf7d47e86cf5cc4715707d223b1Jordan Rose    }
1824411b423e91da0a2c879b70c0222aeba35f72044Jordan Rose
1834411b423e91da0a2c879b70c0222aeba35f72044Jordan Rose    /**
1844411b423e91da0a2c879b70c0222aeba35f72044Jordan Rose     * Helper function that compresses an image into an ETC1Texture.
1854411b423e91da0a2c879b70c0222aeba35f72044Jordan Rose     * @param input a native order direct buffer containing the image data
186e460c46c5d602f65354cab0879c458890273591cJordan Rose     * @param width the width of the image in pixels
187888c90ac0ef6baf7d47e86cf5cc4715707d223b1Jordan Rose     * @param height the height of the image in pixels
188888c90ac0ef6baf7d47e86cf5cc4715707d223b1Jordan Rose     * @param pixelSize the size of a pixel in bytes (2 or 3)
189888c90ac0ef6baf7d47e86cf5cc4715707d223b1Jordan Rose     * @param stride the width of a line of the image in bytes
190888c90ac0ef6baf7d47e86cf5cc4715707d223b1Jordan Rose     * @return the ETC1 texture.
191888c90ac0ef6baf7d47e86cf5cc4715707d223b1Jordan Rose     */
192d563d3fb73879df7147b8a5302c3bf0e1402ba18Jordan Rose    public static ETC1Texture compressTexture(Buffer input, int width, int height, int pixelSize, int stride){
193d563d3fb73879df7147b8a5302c3bf0e1402ba18Jordan Rose        int encodedImageSize = ETC1.getEncodedDataSize(width, height);
194d563d3fb73879df7147b8a5302c3bf0e1402ba18Jordan Rose        ByteBuffer compressedImage = ByteBuffer.allocateDirect(encodedImageSize).
195cb7464ab402d057849dda9749d62a62d86c35ab8Zhongxing Xu            order(ByteOrder.nativeOrder());
196362a31cacc19764f3630928a9e4779af2576e074Jordan Rose        ETC1.encodeImage(input, width, height, 3, stride, compressedImage);
197362a31cacc19764f3630928a9e4779af2576e074Jordan Rose        return new ETC1Texture(width, height, compressedImage);
19896479da6ad9d921d875e7be29fe1bfa127be8069Jordan Rose    }
19996479da6ad9d921d875e7be29fe1bfa127be8069Jordan Rose
200d563d3fb73879df7147b8a5302c3bf0e1402ba18Jordan Rose    /**
2015fe98728dca1f3a7a378ce1a21984a0f8a0c0b8bTed Kremenek     * Helper function that writes an ETC1Texture to an output stream formatted as a PKM file.
202bc403861bc4e6f7ad1371e9e129f0f25b38b3a9aJordan Rose     * @param texture the input texture.
203bc403861bc4e6f7ad1371e9e129f0f25b38b3a9aJordan Rose     * @param output the stream to write the formatted texture data to.
204bc403861bc4e6f7ad1371e9e129f0f25b38b3a9aJordan Rose     * @throws IOException
205bc403861bc4e6f7ad1371e9e129f0f25b38b3a9aJordan Rose     */
206bc403861bc4e6f7ad1371e9e129f0f25b38b3a9aJordan Rose    public static void writeTexture(ETC1Texture texture, OutputStream output) throws IOException {
207bc403861bc4e6f7ad1371e9e129f0f25b38b3a9aJordan Rose        ByteBuffer dataBuffer = texture.getData();
208bc403861bc4e6f7ad1371e9e129f0f25b38b3a9aJordan Rose        int originalPosition = dataBuffer.position();
209bc403861bc4e6f7ad1371e9e129f0f25b38b3a9aJordan Rose        try {
210bc403861bc4e6f7ad1371e9e129f0f25b38b3a9aJordan Rose            int width = texture.getWidth();
211bc403861bc4e6f7ad1371e9e129f0f25b38b3a9aJordan Rose            int height = texture.getHeight();
212bc403861bc4e6f7ad1371e9e129f0f25b38b3a9aJordan Rose            ByteBuffer header = ByteBuffer.allocateDirect(ETC1.ETC_PKM_HEADER_SIZE).order(ByteOrder.nativeOrder());
213bc403861bc4e6f7ad1371e9e129f0f25b38b3a9aJordan Rose            ETC1.formatHeader(header, width, height);
214bc403861bc4e6f7ad1371e9e129f0f25b38b3a9aJordan Rose            byte[] ioBuffer = new byte[4096];
215bc403861bc4e6f7ad1371e9e129f0f25b38b3a9aJordan Rose            header.get(ioBuffer, 0, ETC1.ETC_PKM_HEADER_SIZE);
216bc403861bc4e6f7ad1371e9e129f0f25b38b3a9aJordan Rose            output.write(ioBuffer, 0, ETC1.ETC_PKM_HEADER_SIZE);
217bc403861bc4e6f7ad1371e9e129f0f25b38b3a9aJordan Rose            int encodedSize = ETC1.getEncodedDataSize(width, height);
2185fe98728dca1f3a7a378ce1a21984a0f8a0c0b8bTed Kremenek            for (int i = 0; i < encodedSize; ) {
21996479da6ad9d921d875e7be29fe1bfa127be8069Jordan Rose                int chunkSize = Math.min(ioBuffer.length, encodedSize - i);
220bc403861bc4e6f7ad1371e9e129f0f25b38b3a9aJordan Rose                dataBuffer.get(ioBuffer, 0, chunkSize);
221d563d3fb73879df7147b8a5302c3bf0e1402ba18Jordan Rose                output.write(ioBuffer, 0, chunkSize);
22296479da6ad9d921d875e7be29fe1bfa127be8069Jordan Rose                i += chunkSize;
223cb7464ab402d057849dda9749d62a62d86c35ab8Zhongxing Xu            }
224cb7464ab402d057849dda9749d62a62d86c35ab8Zhongxing Xu        } finally {
225888c90ac0ef6baf7d47e86cf5cc4715707d223b1Jordan Rose            dataBuffer.position(originalPosition);
226888c90ac0ef6baf7d47e86cf5cc4715707d223b1Jordan Rose        }
227888c90ac0ef6baf7d47e86cf5cc4715707d223b1Jordan Rose    }
228200fa2e70d52ae6d620e81cd45536071fdde70c0Jordan Rose}
229888c90ac0ef6baf7d47e86cf5cc4715707d223b1Jordan Rose