| /* |
| * Copyright (C) 2007 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.ddmlib; |
| |
| import java.nio.ByteBuffer; |
| |
| /** |
| * Data representing an image taken from a device frame buffer. |
| */ |
| public final class RawImage { |
| public int version; |
| public int bpp; |
| public int size; |
| public int width; |
| public int height; |
| public int red_offset; |
| public int red_length; |
| public int blue_offset; |
| public int blue_length; |
| public int green_offset; |
| public int green_length; |
| public int alpha_offset; |
| public int alpha_length; |
| |
| public byte[] data; |
| |
| /** |
| * Reads the header of a RawImage from a {@link ByteBuffer}. |
| * <p/>The way the data is sent over adb is defined in system/core/adb/framebuffer_service.c |
| * @param version the version of the protocol. |
| * @param buf the buffer to read from. |
| * @return true if success |
| */ |
| public boolean readHeader(int version, ByteBuffer buf) { |
| this.version = version; |
| |
| if (version == 16) { |
| // compatibility mode with original protocol |
| this.bpp = 16; |
| |
| // read actual values. |
| this.size = buf.getInt(); |
| this.width = buf.getInt(); |
| this.height = buf.getInt(); |
| |
| // create default values for the rest. Format is 565 |
| this.red_offset = 11; |
| this.red_length = 5; |
| this.green_offset = 5; |
| this.green_length = 6; |
| this.blue_offset = 0; |
| this.blue_length = 5; |
| this.alpha_offset = 0; |
| this.alpha_length = 0; |
| } else if (version == 1) { |
| this.bpp = buf.getInt(); |
| this.size = buf.getInt(); |
| this.width = buf.getInt(); |
| this.height = buf.getInt(); |
| this.red_offset = buf.getInt(); |
| this.red_length = buf.getInt(); |
| this.blue_offset = buf.getInt(); |
| this.blue_length = buf.getInt(); |
| this.green_offset = buf.getInt(); |
| this.green_length = buf.getInt(); |
| this.alpha_offset = buf.getInt(); |
| this.alpha_length = buf.getInt(); |
| } else { |
| // unsupported protocol! |
| return false; |
| } |
| |
| return true; |
| } |
| |
| /** |
| * Returns the mask value for the red color. |
| * <p/>This value is compatible with org.eclipse.swt.graphics.PaletteData |
| */ |
| public int getRedMask() { |
| return getMask(red_length, red_offset); |
| } |
| |
| /** |
| * Returns the mask value for the green color. |
| * <p/>This value is compatible with org.eclipse.swt.graphics.PaletteData |
| */ |
| public int getGreenMask() { |
| return getMask(green_length, green_offset); |
| } |
| |
| /** |
| * Returns the mask value for the blue color. |
| * <p/>This value is compatible with org.eclipse.swt.graphics.PaletteData |
| */ |
| public int getBlueMask() { |
| return getMask(blue_length, blue_offset); |
| } |
| |
| /** |
| * Returns the size of the header for a specific version of the framebuffer adb protocol. |
| * @param version the version of the protocol |
| * @return the number of int that makes up the header. |
| */ |
| public static int getHeaderSize(int version) { |
| switch (version) { |
| case 16: // compatibility mode |
| return 3; // size, width, height |
| case 1: |
| return 12; // bpp, size, width, height, 4*(length, offset) |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * Returns a rotated version of the image |
| * The image is rotated counter-clockwise. |
| */ |
| public RawImage getRotated() { |
| RawImage rotated = new RawImage(); |
| rotated.version = this.version; |
| rotated.bpp = this.bpp; |
| rotated.size = this.size; |
| rotated.red_offset = this.red_offset; |
| rotated.red_length = this.red_length; |
| rotated.blue_offset = this.blue_offset; |
| rotated.blue_length = this.blue_length; |
| rotated.green_offset = this.green_offset; |
| rotated.green_length = this.green_length; |
| rotated.alpha_offset = this.alpha_offset; |
| rotated.alpha_length = this.alpha_length; |
| |
| rotated.width = this.height; |
| rotated.height = this.width; |
| |
| int count = this.data.length; |
| rotated.data = new byte[count]; |
| |
| int byteCount = this.bpp >> 3; // bpp is in bits, we want bytes to match our array |
| final int w = this.width; |
| final int h = this.height; |
| for (int y = 0 ; y < h ; y++) { |
| for (int x = 0 ; x < w ; x++) { |
| System.arraycopy( |
| this.data, (y * w + x) * byteCount, |
| rotated.data, ((w-x-1) * h + y) * byteCount, |
| byteCount); |
| } |
| } |
| |
| return rotated; |
| } |
| |
| /** |
| * Returns an ARGB integer value for the pixel at <var>index</var> in {@link #data}. |
| */ |
| public int getARGB(int index) { |
| int value; |
| int r, g, b, a; |
| if (bpp == 16) { |
| value = data[index] & 0x00FF; |
| value |= (data[index+1] << 8) & 0x0FF00; |
| // RGB565 to RGB888 |
| // Multiply by 255/31 to convert from 5 bits (31 max) to 8 bits (255) |
| r = ((value >>> 11) & 0x1f) * 255/31; |
| g = ((value >>> 5) & 0x3f) * 255/63; |
| b = ((value) & 0x1f) * 255/31; |
| a = 0xFF; // force alpha to opaque if there's no alpha value in the framebuffer. |
| } else if (bpp == 32) { |
| value = data[index] & 0x00FF; |
| value |= (data[index+1] & 0x00FF) << 8; |
| value |= (data[index+2] & 0x00FF) << 16; |
| value |= (data[index+3] & 0x00FF) << 24; |
| r = ((value >>> red_offset) & getMask(red_length)) << (8 - red_length); |
| g = ((value >>> green_offset) & getMask(green_length)) << (8 - green_length); |
| b = ((value >>> blue_offset) & getMask(blue_length)) << (8 - blue_length); |
| a = ((value >>> alpha_offset) & getMask(alpha_length)) << (8 - alpha_length); |
| } else { |
| throw new UnsupportedOperationException("RawImage.getARGB(int) only works in 16 and 32 bit mode."); |
| } |
| |
| return a << 24 | r << 16 | g << 8 | b; |
| } |
| |
| /** |
| * creates a mask value based on a length and offset. |
| * <p/>This value is compatible with org.eclipse.swt.graphics.PaletteData |
| */ |
| private int getMask(int length, int offset) { |
| int res = getMask(length) << offset; |
| |
| // if the bpp is 32 bits then we need to invert it because the buffer is in little endian |
| if (bpp == 32) { |
| return Integer.reverseBytes(res); |
| } |
| |
| return res; |
| } |
| |
| /** |
| * Creates a mask value based on a length. |
| * @param length |
| * @return |
| */ |
| private static int getMask(int length) { |
| return (1 << length) - 1; |
| } |
| } |