1 /* 2 ** Copyright 2011, 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 17 package com.android.ide.eclipse.gldebugger; 18 19 import com.google.protobuf.ByteString; 20 21 import org.eclipse.jface.dialogs.MessageDialog; 22 import org.eclipse.swt.graphics.ImageData; 23 import org.eclipse.swt.graphics.PaletteData; 24 25 import java.nio.ByteBuffer; 26 import java.util.Arrays; 27 28 public class MessageProcessor { 29 static void showError(final String message) { 30 // need to call SWT from UI thread 31 MessageDialog.openError(null, "MessageProcessor", message); 32 } 33 34 /** 35 * data layout: uint32 total decompressed length, (chunks: uint32 chunk 36 * decompressed size, uint32 chunk compressed size, chunk data)+. 0 chunk 37 * compressed size means chunk is not compressed 38 */ 39 public static byte[] lzfDecompressChunks(final ByteString data) { 40 ByteBuffer in = data.asReadOnlyByteBuffer(); 41 in.order(GLFramesView.TARGET_BYTE_ORDER); 42 ByteBuffer out = ByteBuffer.allocate(in.getInt()); 43 byte[] inChunk = new byte[0]; 44 byte[] outChunk = new byte[0]; 45 while (in.remaining() > 0) { 46 int decompressed = in.getInt(); 47 int compressed = in.getInt(); 48 if (decompressed > outChunk.length) 49 outChunk = new byte[decompressed]; 50 if (compressed == 0) { 51 in.get(outChunk, 0, decompressed); 52 out.put(outChunk, 0, decompressed); 53 } else { 54 if (compressed > inChunk.length) 55 inChunk = new byte[compressed]; 56 in.get(inChunk, 0, compressed); 57 int size = org.liblzf.CLZF 58 .lzf_decompress(inChunk, compressed, outChunk, outChunk.length); 59 assert size == decompressed; 60 out.put(outChunk, 0, size); 61 } 62 } 63 assert !out.hasRemaining(); 64 return out.array(); 65 } 66 67 /** same data layout as LZFDecompressChunks */ 68 public static byte[] lzfCompressChunks(final byte[] in, final int inSize) { 69 byte[] chunk = new byte[256 * 1024]; // chunk size is arbitrary 70 final ByteBuffer out = ByteBuffer.allocate(4 + (inSize + chunk.length - 1) 71 / chunk.length * (chunk.length + 4 * 2)); 72 out.order(GLFramesView.TARGET_BYTE_ORDER); 73 out.putInt(inSize); 74 for (int i = 0; i < inSize; i += chunk.length) { 75 int chunkIn = chunk.length; 76 if (i + chunkIn > inSize) 77 chunkIn = inSize - i; 78 final byte[] inChunk = java.util.Arrays.copyOfRange(in, i, i + chunkIn); 79 final int chunkOut = org.liblzf.CLZF 80 .lzf_compress(inChunk, chunkIn, chunk, chunk.length); 81 out.putInt(chunkIn); 82 out.putInt(chunkOut); 83 if (chunkOut == 0) // compressed bigger than chunk (uncompressed) 84 out.put(inChunk); 85 else 86 out.put(chunk, 0, chunkOut); 87 } 88 return Arrays.copyOf(out.array(), out.position()); 89 } 90 91 /** 92 * returns new ref, which is also the decoded image; ref could be bigger 93 * than pixels, in which case the first pixels.length bytes form the image 94 */ 95 public static byte[] decodeReferencedImage(byte[] ref, byte[] pixels) { 96 if (ref.length < pixels.length) 97 ref = new byte[pixels.length]; 98 for (int i = 0; i < pixels.length; i++) 99 ref[i] ^= pixels[i]; 100 for (int i = pixels.length; i < ref.length; i++) 101 ref[i] = 0; // clear unused ref to maintain consistency 102 return ref; 103 } 104 105 public static ImageData receiveImage(int width, int height, int format, 106 int type, final ByteString data) { 107 assert width > 0 && height > 0; 108 int bpp = 0; 109 int redMask = 0, blueMask = 0, greenMask = 0; 110 switch (GLEnum.valueOf(type)) { 111 case GL_UNSIGNED_SHORT_5_6_5: 112 case GL_UNSIGNED_SHORT_4_4_4_4: 113 case GL_UNSIGNED_SHORT_5_5_5_1: 114 format = type; 115 break; 116 case GL_UNSIGNED_BYTE: 117 break; 118 default: 119 showError("unsupported texture type " + type); 120 return null; 121 } 122 123 switch (GLEnum.valueOf(format)) { 124 case GL_ALPHA: 125 case GL_LUMINANCE: 126 redMask = blueMask = greenMask = 0xff; 127 bpp = 8; 128 break; 129 case GL_LUMINANCE_ALPHA: 130 blueMask = 0xff; 131 redMask = 0xff00; 132 bpp = 16; 133 break; 134 case GL_RGB: 135 blueMask = 0xff; 136 greenMask = 0xff00; 137 redMask = 0xff0000; 138 bpp = 24; 139 break; 140 case GL_RGBA: 141 blueMask = 0xff00; 142 greenMask = 0xff0000; 143 redMask = 0xff000000; 144 bpp = 32; 145 break; 146 case GL_UNSIGNED_SHORT_5_6_5: 147 blueMask = ((1 << 5) - 1) << 0; 148 greenMask = ((1 << 6) - 1) << 5; 149 redMask = ((1 << 5) - 1) << 11; 150 bpp = 16; 151 break; 152 case GL_UNSIGNED_SHORT_4_4_4_4: 153 blueMask = ((1 << 4) - 1) << 4; 154 greenMask = ((1 << 4) - 1) << 8; 155 redMask = ((1 << 4) - 1) << 12; 156 bpp = 16; 157 break; 158 case GL_UNSIGNED_SHORT_5_5_5_1: 159 blueMask = ((1 << 5) - 1) << 1; 160 greenMask = ((1 << 5) - 1) << 6; 161 redMask = ((1 << 5) - 1) << 11; 162 bpp = 16; 163 break; 164 default: 165 showError("unsupported texture format: " + format); 166 return null; 167 } 168 byte[] pixels = lzfDecompressChunks(data); 169 assert pixels.length == width * height * (bpp / 8); 170 PaletteData palette = new PaletteData(redMask, greenMask, blueMask); 171 return new ImageData(width, height, bpp, palette, 1, pixels); 172 } 173 } 174