view src/main/java/com/glavsoft/drawing/Renderer.java @ 52:472a9bcacb21 draft default tip

TightVNC 2.7.1.0
author you@cr.ie.u-ryukyu.ac.jp
date Wed, 07 Aug 2013 19:01:17 +0900
parents 4689cc86d6cb
children
line wrap: on
line source

// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC.
// All rights reserved.
//
//-------------------------------------------------------------------------
// This file is part of the TightVNC software.  Please visit our Web site:
//
//                       http://www.tightvnc.com/
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
//-------------------------------------------------------------------------
//

package com.glavsoft.drawing;

import com.glavsoft.exceptions.TransportException;
import com.glavsoft.rfb.encoding.PixelFormat;
import com.glavsoft.rfb.encoding.decoder.FramebufferUpdateRectangle;
import com.glavsoft.transport.Reader;

import java.util.Arrays;

/**
 * Render bitmap data
 *
 * @author dime @ tightvnc.com
 */
public abstract class Renderer {

    protected Reader reader;
    private final Object lock = new Object();

    public abstract void drawJpegImage(byte[] bytes, int offset,
                                       int jpegBufferLength, FramebufferUpdateRectangle rect);

    protected int width;
    protected int height;
    protected int[] pixels;
    protected SoftCursor cursor;
    public PixelFormat pixelFormat;
    protected ColorDecoder colorDecoder;

    protected void init(Reader reader, int width, int height, PixelFormat pixelFormat) {
        this.reader = reader;
        this.width = width;
        this.height = height;
        initPixelFormat(pixelFormat);
        pixels = new int[width * height];
        Arrays.fill(pixels, 0);
    }

    public void initPixelFormat(PixelFormat pixelFormat) {
        synchronized (lock) {
            this.pixelFormat = pixelFormat;
            colorDecoder = new ColorDecoder(pixelFormat);
        }
    }

    /**
     * Draw byte array bitmap data
     *
     * @param bytes  bitmap data
     * @param x      bitmap x position
     * @param y      bitmap y position
     * @param width  bitmap width
     * @param height bitmap height
     */
    public void drawBytes(byte[] bytes, int x, int y, int width, int height) {
        int i = 0;
        for (int ly = y; ly < y + height; ++ly) {
            int end = ly * this.width + x + width;
            for (int pixelsOffset = ly * this.width + x; pixelsOffset < end; ++pixelsOffset) {
                pixels[pixelsOffset] = getPixelColor(bytes, i);
                i += colorDecoder.bytesPerPixel;
            }
        }
    }

    /**
     * Draw byte array bitmap data (for ZRLE)
     */
    public int drawCompactBytes(byte[] bytes, int offset, int x, int y, int width, int height) {
        synchronized (lock) {
            int i = offset;
            for (int ly = y; ly < y + height; ++ly) {
                int end = ly * this.width + x + width;
                for (int pixelsOffset = ly * this.width + x; pixelsOffset < end; ++pixelsOffset) {
                    pixels[pixelsOffset] = getCompactPixelColor(bytes, i);
                    i += colorDecoder.bytesPerCPixel;
                }
            }
            return i - offset;
        }
    }

    /**
     * Draw int (colors) array bitmap data (for ZRLE)
     */
    public void drawColoredBitmap(int[] colors, int x, int y, int width, int height) {
        synchronized (lock) {
            int i = 0;
            for (int ly = y; ly < y + height; ++ly) {
                int end = ly * this.width + x + width;
                for (int pixelsOffset = ly * this.width + x; pixelsOffset < end; ++pixelsOffset) {
                    pixels[pixelsOffset] = colors[i++];
                }
            }
        }
    }

    /**
     * Draw byte array bitmap data (for Tight)
     */
    public int drawTightBytes(byte[] bytes, int offset, int x, int y, int width, int height) {
        synchronized (lock) {
            int i = offset;
            for (int ly = y; ly < y + height; ++ly) {
                int end = ly * this.width + x + width;
                for (int pixelsOffset = ly * this.width + x; pixelsOffset < end; ++pixelsOffset) {
                    pixels[pixelsOffset] = colorDecoder.getTightColor(bytes, i);
                    i += colorDecoder.bytesPerPixelTight;
                }
            }
            return i - offset;
        }
    }

    /**
     * Draw byte array bitmap data (from array with plain RGB color components. Assumed: rrrrrrrr gggggggg bbbbbbbb)
     */
    public void drawUncaliberedRGBLine(byte[] bytes, int x, int y, int width) {
        synchronized (lock) {
            int end = y * this.width + x + width;
            for (int i = 3, pixelsOffset = y * this.width + x; pixelsOffset < end; ++pixelsOffset) {
                pixels[pixelsOffset] =
//					(0xff & bytes[i++]) << 16 |
//					(0xff & bytes[i++]) << 8 |
//					0xff & bytes[i++];
                        (0xff & 255 * (colorDecoder.redMax & bytes[i++]) / colorDecoder.redMax) << 16 |
                                (0xff & 255 * (colorDecoder.greenMax & bytes[i++]) / colorDecoder.greenMax) << 8 |
                                0xff & 255 * (colorDecoder.blueMax & bytes[i++]) / colorDecoder.blueMax;
            }
        }
    }

    /**
     * Draw paletted byte array bitmap data
     *
     * @param buffer  bitmap data
     * @param rect    bitmap location and dimensions
     * @param palette colour palette
     * @param paletteSize number of colors in palette
     */
    public void drawBytesWithPalette(byte[] buffer, FramebufferUpdateRectangle rect,
                                     int[] palette, int paletteSize) {
        synchronized (lock) {
            // 2 colors
            if (2 == paletteSize) {
                int dx, dy, n;
                int i = rect.y * this.width + rect.x;
                int rowBytes = (rect.width + 7) / 8;
                byte b;

                for (dy = 0; dy < rect.height; dy++) {
                    for (dx = 0; dx < rect.width / 8; dx++) {
                        b = buffer[dy * rowBytes + dx];
                        for (n = 7; n >= 0; n--) {
                            pixels[i++] = palette[b >> n & 1];
                        }
                    }
                    for (n = 7; n >= 8 - rect.width % 8; n--) {
                        pixels[i++] = palette[buffer[dy * rowBytes + dx] >> n & 1];
                    }
                    i += this.width - rect.width;
                }
            } else {
                // 3..255 colors (assuming bytesPixel == 4).
                int i = 0;
                for (int ly = rect.y; ly < rect.y + rect.height; ++ly) {
                    for (int lx = rect.x; lx < rect.x + rect.width; ++lx) {
                        int pixelsOffset = ly * this.width + lx;
                        pixels[pixelsOffset] = palette[buffer[i++] & 0xFF];
                    }
                }
            }
        }
    }

    /**
     * Copy rectangle region from one position to another. Regions may be overlapped.
     *
     * @param srcX    source rectangle x position
     * @param srcY    source rectangle y position
     * @param dstRect destination rectangle
     */
    public void copyRect(int srcX, int srcY, FramebufferUpdateRectangle dstRect) {
        int startSrcY, endSrcY, dstY, deltaY;
        if (srcY > dstRect.y) {
            startSrcY = srcY;
            endSrcY = srcY + dstRect.height;
            dstY = dstRect.y;
            deltaY = +1;
        } else {
            startSrcY = srcY + dstRect.height - 1;
            endSrcY = srcY - 1;
            dstY = dstRect.y + dstRect.height - 1;
            deltaY = -1;
        }
        synchronized (lock) {
            for (int y = startSrcY; y != endSrcY; y += deltaY) {
                System.arraycopy(pixels, y * width + srcX,
                        pixels, dstY * width + dstRect.x, dstRect.width);
                dstY += deltaY;
            }
        }
    }

    /**
     * Fill rectangle region with specified colour
     *
     * @param color colour to fill with
     * @param rect  rectangle region positions and dimensions
     */
    public void fillRect(int color, FramebufferUpdateRectangle rect) {
        fillRect(color, rect.x, rect.y, rect.width, rect.height);
    }

    /**
     * Fill rectangle region with specified colour
     *
     * @param color  colour to fill with
     * @param x      rectangle x position
     * @param y      rectangle y position
     * @param width  rectangle width
     * @param height rectangle height
     */
    public void fillRect(int color, int x, int y, int width, int height) {
        synchronized (lock) {
            int sy = y * this.width + x;
            int ey = sy + height * this.width;
            for (int i = sy; i < ey; i += this.width) {
                Arrays.fill(pixels, i, i + width, color);
            }
        }
    }

    /**
     * Reads color bytes (PIXEL) from reader, returns int combined RGB
     * value consisting of the red component in bits 16-23, the green component
     * in bits 8-15, and the blue component in bits 0-7. May be used directly for
     * creation awt.Color object
     */
    public int readPixelColor(Reader reader) throws TransportException {
        return colorDecoder.readColor(reader);
    }

    public int readTightPixelColor(Reader reader) throws TransportException {
        return colorDecoder.readTightColor(reader);
    }

    public ColorDecoder getColorDecoder() {
        return colorDecoder;
    }

    public int getCompactPixelColor(byte[] bytes, int offset) {
        return colorDecoder.getCompactColor(bytes, offset);
    }

    public int getPixelColor(byte[] bytes, int offset) {
        return colorDecoder.getColor(bytes, offset);
    }

    public int getBytesPerPixel() {
        return colorDecoder.bytesPerPixel;
    }

    public int getBytesPerCPixel() {
        return colorDecoder.bytesPerCPixel;
    }

    public int getBytesPerPixelTight() {
        return colorDecoder.bytesPerPixelTight;
    }

    public void fillColorBitmapWithColor(int[] bitmapData, int decodedOffset, int rlength, int color) {
        while (rlength-- > 0) {
            bitmapData[decodedOffset++] = color;
        }
    }

    /**
     * Width of rendered image
     *
     * @return width
     */
    public int getWidth() {
        return width;
    }

    /**
     * Height of rendered image
     *
     * @return height
     */
    public int getHeight() {
        return height;
    }

    /**
     * Read and decode cursor image
     *
     * @param rect new cursor hot point position and cursor dimensions
     * @throws TransportException
     */
    public void createCursor(int[] cursorPixels, FramebufferUpdateRectangle rect)
            throws TransportException {
        synchronized (cursor.getLock()) {
            cursor.createCursor(cursorPixels, rect.x, rect.y, rect.width, rect.height);
        }
    }

    /**
     * Read and decode new cursor position
     *
     * @param rect cursor position
     */
    public void decodeCursorPosition(FramebufferUpdateRectangle rect) {
        synchronized (cursor.getLock()) {
            cursor.updatePosition(rect.x, rect.y);
        }
    }

    public Object getLock() {
        return lock;
    }
}