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 17 package com.android.ddmlib; 18 19 import java.nio.ByteBuffer; 20 21 /** 22 * Data representing an image taken from a device frame buffer. 23 */ 24 public final class RawImage { 25 public int version; 26 public int bpp; 27 public int size; 28 public int width; 29 public int height; 30 public int red_offset; 31 public int red_length; 32 public int blue_offset; 33 public int blue_length; 34 public int green_offset; 35 public int green_length; 36 public int alpha_offset; 37 public int alpha_length; 38 39 public byte[] data; 40 41 /** 42 * Reads the header of a RawImage from a {@link ByteBuffer}. 43 * <p/>The way the data is sent over adb is defined in system/core/adb/framebuffer_service.c 44 * @param version the version of the protocol. 45 * @param buf the buffer to read from. 46 * @return true if success 47 */ readHeader(int version, ByteBuffer buf)48 public boolean readHeader(int version, ByteBuffer buf) { 49 this.version = version; 50 51 if (version == 16) { 52 // compatibility mode with original protocol 53 this.bpp = 16; 54 55 // read actual values. 56 this.size = buf.getInt(); 57 this.width = buf.getInt(); 58 this.height = buf.getInt(); 59 60 // create default values for the rest. Format is 565 61 this.red_offset = 11; 62 this.red_length = 5; 63 this.green_offset = 5; 64 this.green_length = 6; 65 this.blue_offset = 0; 66 this.blue_length = 5; 67 this.alpha_offset = 0; 68 this.alpha_length = 0; 69 } else if (version == 1) { 70 this.bpp = buf.getInt(); 71 this.size = buf.getInt(); 72 this.width = buf.getInt(); 73 this.height = buf.getInt(); 74 this.red_offset = buf.getInt(); 75 this.red_length = buf.getInt(); 76 this.blue_offset = buf.getInt(); 77 this.blue_length = buf.getInt(); 78 this.green_offset = buf.getInt(); 79 this.green_length = buf.getInt(); 80 this.alpha_offset = buf.getInt(); 81 this.alpha_length = buf.getInt(); 82 } else { 83 // unsupported protocol! 84 return false; 85 } 86 87 return true; 88 } 89 90 /** 91 * Returns the mask value for the red color. 92 * <p/>This value is compatible with org.eclipse.swt.graphics.PaletteData 93 */ getRedMask()94 public int getRedMask() { 95 return getMask(red_length, red_offset); 96 } 97 98 /** 99 * Returns the mask value for the green color. 100 * <p/>This value is compatible with org.eclipse.swt.graphics.PaletteData 101 */ getGreenMask()102 public int getGreenMask() { 103 return getMask(green_length, green_offset); 104 } 105 106 /** 107 * Returns the mask value for the blue color. 108 * <p/>This value is compatible with org.eclipse.swt.graphics.PaletteData 109 */ getBlueMask()110 public int getBlueMask() { 111 return getMask(blue_length, blue_offset); 112 } 113 114 /** 115 * Returns the size of the header for a specific version of the framebuffer adb protocol. 116 * @param version the version of the protocol 117 * @return the number of int that makes up the header. 118 */ getHeaderSize(int version)119 public static int getHeaderSize(int version) { 120 switch (version) { 121 case 16: // compatibility mode 122 return 3; // size, width, height 123 case 1: 124 return 12; // bpp, size, width, height, 4*(length, offset) 125 } 126 127 return 0; 128 } 129 130 /** 131 * Returns a rotated version of the image 132 * The image is rotated counter-clockwise. 133 */ getRotated()134 public RawImage getRotated() { 135 RawImage rotated = new RawImage(); 136 rotated.version = this.version; 137 rotated.bpp = this.bpp; 138 rotated.size = this.size; 139 rotated.red_offset = this.red_offset; 140 rotated.red_length = this.red_length; 141 rotated.blue_offset = this.blue_offset; 142 rotated.blue_length = this.blue_length; 143 rotated.green_offset = this.green_offset; 144 rotated.green_length = this.green_length; 145 rotated.alpha_offset = this.alpha_offset; 146 rotated.alpha_length = this.alpha_length; 147 148 rotated.width = this.height; 149 rotated.height = this.width; 150 151 int count = this.data.length; 152 rotated.data = new byte[count]; 153 154 int byteCount = this.bpp >> 3; // bpp is in bits, we want bytes to match our array 155 final int w = this.width; 156 final int h = this.height; 157 for (int y = 0 ; y < h ; y++) { 158 for (int x = 0 ; x < w ; x++) { 159 System.arraycopy( 160 this.data, (y * w + x) * byteCount, 161 rotated.data, ((w-x-1) * h + y) * byteCount, 162 byteCount); 163 } 164 } 165 166 return rotated; 167 } 168 169 /** 170 * Returns an ARGB integer value for the pixel at <var>index</var> in {@link #data}. 171 */ getARGB(int index)172 public int getARGB(int index) { 173 int value; 174 if (bpp == 16) { 175 value = data[index] & 0x00FF; 176 value |= (data[index+1] << 8) & 0x0FF00; 177 } else if (bpp == 32) { 178 value = data[index] & 0x00FF; 179 value |= (data[index+1] & 0x00FF) << 8; 180 value |= (data[index+2] & 0x00FF) << 16; 181 value |= (data[index+3] & 0x00FF) << 24; 182 } else { 183 throw new UnsupportedOperationException("RawImage.getARGB(int) only works in 16 and 32 bit mode."); 184 } 185 186 int r = ((value >>> red_offset) & getMask(red_length)) << (8 - red_length); 187 int g = ((value >>> green_offset) & getMask(green_length)) << (8 - green_length); 188 int b = ((value >>> blue_offset) & getMask(blue_length)) << (8 - blue_length); 189 int a; 190 if (alpha_length == 0) { 191 a = 0xFF; // force alpha to opaque if there's no alpha value in the framebuffer. 192 } else { 193 a = ((value >>> alpha_offset) & getMask(alpha_length)) << (8 - alpha_length); 194 } 195 196 return a << 24 | r << 16 | g << 8 | b; 197 } 198 199 /** 200 * creates a mask value based on a length and offset. 201 * <p/>This value is compatible with org.eclipse.swt.graphics.PaletteData 202 */ getMask(int length, int offset)203 private int getMask(int length, int offset) { 204 int res = getMask(length) << offset; 205 206 // if the bpp is 32 bits then we need to invert it because the buffer is in little endian 207 if (bpp == 32) { 208 return Integer.reverseBytes(res); 209 } 210 211 return res; 212 } 213 214 /** 215 * Creates a mask value based on a length. 216 * @param length 217 * @return 218 */ getMask(int length)219 private static int getMask(int length) { 220 return (1 << length) - 1; 221 } 222 } 223