| // Licensed to the Apache Software Foundation (ASF) under one |
| // or more contributor license agreements. See the NOTICE file |
| // distributed with this work for additional information |
| // regarding copyright ownership. The ASF licenses this file |
| // to you 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 rdpclient.rdp; |
| |
| import streamer.BaseElement; |
| import streamer.ByteBuffer; |
| import streamer.Element; |
| import streamer.Link; |
| import streamer.Pipeline; |
| import streamer.PipelineImpl; |
| import streamer.debug.FakeSink; |
| import common.BitmapOrder; |
| import common.BitmapRectangle; |
| |
| /** |
| * @see http://msdn.microsoft.com/en-us/library/cc240624.aspx |
| */ |
| public class ServerBitmapUpdate extends BaseElement { |
| public static final int UPDATETYPE_BITMAP = 0x0001; |
| |
| /** |
| * Indicates that the bitmap data is compressed. The bitmapComprHdr field MUST |
| * be present if the NO_BITMAP_COMPRESSION_HDR (0x0400) flag is not set. |
| */ |
| public static final int BITMAP_COMPRESSION = 0x0001; |
| |
| /** |
| * Indicates that the bitmapComprHdr field is not present (removed for |
| * bandwidth efficiency to save 8 bytes). |
| */ |
| private static final int NO_BITMAP_COMPRESSION_HDR = 0x0400; |
| |
| public ServerBitmapUpdate(String id) { |
| super(id); |
| } |
| |
| @Override |
| public void handleData(ByteBuffer buf, Link link) { |
| |
| if (verbose) |
| System.out.println("[" + this + "] INFO: Data received: " + buf + "."); |
| |
| // * DEBUG */System.out.println(buf.toHexString(buf.length)); |
| |
| BitmapOrder order = new BitmapOrder(); |
| |
| // (2 bytes): A 16-bit, unsigned integer. The update type. This field MUST |
| // be set to UPDATETYPE_BITMAP (0x0001). |
| int updateType = buf.readSignedShortLE(); |
| if (updateType != UPDATETYPE_BITMAP) |
| throw new RuntimeException("Unknown update type. Expected update type: UPDATETYPE_BITMAP (0x1). Actual update type: " + updateType + ", buf: " + buf |
| + "."); |
| |
| // (2 bytes): A 16-bit, unsigned integer. The number of screen rectangles |
| // present in the rectangles field. |
| int numberRectangles = buf.readSignedShortLE(); |
| |
| // (variable): Variable-length array of TS_BITMAP_DATA structures, each of |
| // which contains a rectangular clipping taken from the server-side screen |
| // frame buffer. The number of screen clippings in the array is specified by |
| // the numberRectangles field. |
| BitmapRectangle[] rectangles = new BitmapRectangle[numberRectangles]; |
| for (int i = 0; i < numberRectangles; i++) { |
| rectangles[i] = readRectangle(buf); |
| } |
| order.rectangles = rectangles; |
| |
| buf.assertThatBufferIsFullyRead(); |
| |
| ByteBuffer data = new ByteBuffer(order); |
| pushDataToAllOuts(data); |
| |
| buf.unref(); |
| } |
| |
| public BitmapRectangle readRectangle(ByteBuffer buf) { |
| |
| BitmapRectangle rectangle = new BitmapRectangle(); |
| |
| // (2 bytes): A 16-bit, unsigned integer. Left bound of the rectangle. |
| rectangle.x = buf.readSignedShortLE(); |
| |
| // (2 bytes): A 16-bit, unsigned integer. Top bound of the rectangle. |
| rectangle.y = buf.readSignedShortLE(); |
| |
| // (2 bytes): A 16-bit, unsigned integer. Inclusive right bound of the |
| // rectangle. |
| int destRight = buf.readSignedShortLE(); |
| rectangle.width = destRight - rectangle.x + 1; |
| |
| // (2 bytes): A 16-bit, unsigned integer. Inclusive bottom bound of the |
| // rectangle. |
| int destBottom = buf.readSignedShortLE(); |
| rectangle.height = destBottom - rectangle.y + 1; |
| |
| // (2 bytes): A 16-bit, unsigned integer. The width of the rectangle. |
| rectangle.bufferWidth = buf.readSignedShortLE(); |
| |
| // (2 bytes): A 16-bit, unsigned integer. The height of the rectangle. |
| rectangle.bufferHeight = buf.readSignedShortLE(); |
| |
| // (2 bytes): A 16-bit, unsigned integer. The color depth of the rectangle |
| // data in bits-per-pixel. |
| rectangle.colorDepth = buf.readSignedShortLE(); |
| |
| // (2 bytes): A 16-bit, unsigned integer. The flags describing the format of |
| // the bitmap data in the bitmapDataStream field. |
| int flags = buf.readSignedShortLE(); |
| |
| // BITMAP_COMPRESSION 0x0001 |
| // Indicates that the bitmap data is compressed. The bitmapComprHdr field |
| // MUST be present if the NO_BITMAP_COMPRESSION_HDR (0x0400) flag is not |
| // set. |
| boolean compressed = ((flags & BITMAP_COMPRESSION) > 0); |
| |
| // (2 bytes): A 16-bit, unsigned integer. The size in bytes of the data in |
| // the bitmapComprHdr and bitmapDataStream fields. |
| int bitmapLength = buf.readSignedShortLE(); |
| |
| // NO_BITMAP_COMPRESSION_HDR 0x0400 |
| // Indicates that the bitmapComprHdr field is not present (removed for |
| // bandwidth efficiency to save 8 bytes). |
| if (compressed && (flags & NO_BITMAP_COMPRESSION_HDR) == 0) { |
| // (8 bytes): Optional Compressed Data Header structure specifying the |
| // bitmap data in the bitmapDataStream. |
| // This field MUST be present if the BITMAP_COMPRESSION (0x0001) flag is |
| // present in the Flags field, but the NO_BITMAP_COMPRESSION_HDR (0x0400) |
| // flag is not. |
| |
| // Note: Even when compression header is enabled, server sends nothing. |
| // rectangle.compressedBitmapHeader = buf.readBytes(8); |
| } |
| |
| // (variable): A variable-length array of bytes describing a bitmap image. |
| // Bitmap data is either compressed or uncompressed, depending on whether |
| // the BITMAP_COMPRESSION flag is present in the Flags field. Uncompressed |
| // bitmap data is formatted as a bottom-up, left-to-right series of pixels. |
| // Each pixel is a whole number of bytes. Each row contains a multiple of |
| // four bytes (including up to three bytes of padding, as necessary). |
| // Compressed bitmaps not in 32 bpp format are compressed using Interleaved |
| // RLE and encapsulated in an RLE Compressed Bitmap Stream structure, |
| // while compressed bitmaps at a color depth of 32 bpp are compressed |
| // using RDP 6.0 Bitmap Compression and stored inside |
| // an RDP 6.0 Bitmap Compressed Stream structure. |
| if (!compressed) { |
| rectangle.bitmapDataStream = buf.readBytes(bitmapLength); |
| } else { |
| ByteBuffer compressedImage = buf.readBytes(bitmapLength); |
| //* DEBUG */System.out.println("Compressed image: " + compressedImage + ", depth: " + rectangle.bitsPerPixel + "."); |
| rectangle.bitmapDataStream = RLEBitmapDecompression.rleDecompress(compressedImage, rectangle.bufferWidth, rectangle.bufferHeight, rectangle.colorDepth); |
| compressedImage.unref(); |
| } |
| |
| return rectangle; |
| } |
| |
| /** |
| * Example. |
| */ |
| public static void main(String args[]) { |
| ByteBuffer packet = new ByteBuffer(new byte[] {0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00, 0x10, 0x00, |
| 0x01, 0x04, 0x0a, 0x00, 0x0c, (byte)0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}); |
| |
| Element bitmap = new ServerBitmapUpdate("bitmap") { |
| { |
| verbose = true; |
| } |
| }; |
| FakeSink fakeSink = new FakeSink("sink") { |
| { |
| verbose = true; |
| } |
| }; |
| Pipeline pipeline = new PipelineImpl("test"); |
| |
| // BufferedImageCanvas canvas = new BufferedImageCanvas(1024, 768); |
| // Element adapter = new AwtRdpAdapter("test",canvas ); |
| // pipeline.addAndLink(bitmap, adapter); |
| pipeline.addAndLink(bitmap, fakeSink); |
| |
| bitmap.handleData(packet, null); |
| |
| } |
| |
| } |