• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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