blob: b16404411fb6d8ca0f0d3f20b11dfd521a7485c8 [file] [log] [blame]
/*
* 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;
}
}