285 lines
8.5 KiB
Java
285 lines
8.5 KiB
Java
/* Copyright 2016-2019 Brian P. Hinz
|
|
*
|
|
* This 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 software 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 software; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
|
|
* USA.
|
|
*/
|
|
|
|
// -=- Modifiable generic pixel buffer class
|
|
|
|
package com.tigervnc.rfb;
|
|
|
|
import java.awt.image.*;
|
|
import java.awt.Color;
|
|
import java.awt.color.ColorSpace;
|
|
import java.lang.*;
|
|
import java.nio.*;
|
|
import java.util.*;
|
|
|
|
import static java.awt.image.DataBuffer.*;
|
|
|
|
public abstract class ModifiablePixelBuffer extends PixelBuffer
|
|
{
|
|
|
|
public ModifiablePixelBuffer(PixelFormat pf, int w, int h)
|
|
{
|
|
super(pf, w, h);
|
|
}
|
|
|
|
protected ModifiablePixelBuffer()
|
|
{
|
|
}
|
|
|
|
///////////////////////////////////////////////
|
|
// Access to pixel data
|
|
//
|
|
|
|
// Get a writeable pointer into the buffer
|
|
// Like getBuffer(), the pointer is to the top-left pixel of the
|
|
// specified Rect.
|
|
public abstract WritableRaster getBufferRW(Rect r);
|
|
// Commit the modified contents
|
|
// Ensures that the changes to the specified Rect is properly
|
|
// stored away and any temporary buffers are freed. The Rect given
|
|
// here needs to match the Rect given to the earlier call to
|
|
// getBufferRW().
|
|
public abstract void commitBufferRW(Rect r);
|
|
|
|
static LogWriter vlog = new LogWriter("ModifiablePixelBuffer");
|
|
///////////////////////////////////////////////
|
|
// Basic rendering operations
|
|
// These operations DO NOT clip to the pixelbuffer area, or trap overruns.
|
|
|
|
// Fill a rectangle
|
|
public void fillRect(Rect r, byte[] pix)
|
|
{
|
|
WritableRaster buf;
|
|
int w, h;
|
|
|
|
w = r.width();
|
|
h = r.height();
|
|
|
|
if (h == 0 || w ==0)
|
|
return;
|
|
|
|
buf = getBufferRW(r);
|
|
|
|
ByteBuffer src =
|
|
ByteBuffer.allocate(r.area()*format.bpp/8).order(format.getByteOrder());
|
|
for (int i=0; i < r.area(); i++)
|
|
src.put(pix);
|
|
Raster raster = format.rasterFromBuffer(r, (ByteBuffer)src.rewind());
|
|
synchronized(image) {
|
|
buf.setDataElements(0, 0, raster);
|
|
}
|
|
|
|
commitBufferRW(r);
|
|
}
|
|
|
|
// Copy pixel data to the buffer
|
|
public void imageRect(Rect r, byte[] pixels)
|
|
{
|
|
WritableRaster dest = getBufferRW(r);
|
|
|
|
int length = r.area()*format.bpp/8;
|
|
ByteBuffer src =
|
|
ByteBuffer.wrap(pixels, 0, length).order(format.getByteOrder());
|
|
Raster raster = format.rasterFromBuffer(r, src);
|
|
synchronized(image) {
|
|
dest.setDataElements(0, 0, raster);
|
|
}
|
|
|
|
commitBufferRW(r);
|
|
}
|
|
|
|
// Copy pixel data from one PixelBuffer location to another
|
|
public void copyRect(Rect rect, Point move_by_delta)
|
|
{
|
|
Raster srcData;
|
|
WritableRaster dstData;
|
|
|
|
Rect drect, srect;
|
|
|
|
drect = new Rect(rect.tl, rect.br);
|
|
if (!drect.enclosed_by(getRect())) {
|
|
String msg = "Destination rect %dx%d at %d,%d exceeds framebuffer %dx%d";
|
|
vlog.error(String.format(msg, drect.width(), drect.height(),
|
|
drect.tl.x, drect.tl.y, width(), height()));
|
|
drect = drect.intersect(getRect());
|
|
}
|
|
|
|
if (drect.is_empty())
|
|
return;
|
|
|
|
srect = drect.translate(move_by_delta.negate());
|
|
if (!srect.enclosed_by(getRect())) {
|
|
String msg = "Source rect %dx%d at %d,%d exceeds framebuffer %dx%d";
|
|
vlog.error(String.format(msg, srect.width(), srect.height(),
|
|
srect.tl.x, srect.tl.y, width(), height()));
|
|
srect = srect.intersect(getRect());
|
|
// Need to readjust the destination now that the area has changed
|
|
drect = srect.translate(move_by_delta);
|
|
}
|
|
|
|
if (srect.is_empty())
|
|
return;
|
|
|
|
srcData = getBuffer(srect);
|
|
dstData = getBufferRW(drect);
|
|
|
|
synchronized(image) {
|
|
dstData.setDataElements(0, 0, srcData);
|
|
}
|
|
|
|
commitBufferRW(rect);
|
|
}
|
|
|
|
// Copy pixel data to the buffer through a mask
|
|
// pixels is a pointer to the pixel to be copied to r.tl.
|
|
// maskPos specifies the pixel offset in the mask to start from.
|
|
// mask_ is a pointer to the mask bits at (0,0).
|
|
// pStride and mStride are the strides of the pixel and mask buffers.
|
|
public void maskRect(Rect r, Object pixels, byte[] mask_)
|
|
{
|
|
Rect cr = getRect().intersect(r);
|
|
if (cr.is_empty()) return;
|
|
WritableRaster data = getBufferRW(cr);
|
|
|
|
// FIXME
|
|
ColorModel cm = format.getColorModel();
|
|
SampleModel sm =
|
|
cm.createCompatibleSampleModel(r.width(), r.height());
|
|
DataBuffer db = null;
|
|
ByteBuffer src =
|
|
ByteBuffer.wrap((byte[])pixels).order(format.getByteOrder());
|
|
Buffer dst;
|
|
switch (sm.getTransferType()) {
|
|
case TYPE_INT:
|
|
dst = IntBuffer.allocate(src.remaining()).put(src.asIntBuffer());
|
|
db = new DataBufferInt(((IntBuffer)dst).array(), r.area());
|
|
break;
|
|
case TYPE_BYTE:
|
|
db = new DataBufferByte(src.array(), r.area());
|
|
break;
|
|
case TYPE_SHORT:
|
|
dst = ShortBuffer.allocate(src.remaining()).put(src.asShortBuffer());
|
|
db = new DataBufferShort(((ShortBuffer)dst).array(), r.area());
|
|
break;
|
|
}
|
|
assert(db != null);
|
|
Raster raster =
|
|
Raster.createRaster(sm, db, new java.awt.Point(0, 0));
|
|
ColorConvertOp converter = format.getColorConvertOp(cm.getColorSpace());
|
|
WritableRaster t = data.createCompatibleWritableRaster();
|
|
converter.filter(raster, t);
|
|
|
|
int w = cr.width();
|
|
int h = cr.height();
|
|
|
|
Point offset = new Point(cr.tl.x-r.tl.x, cr.tl.y-r.tl.y);
|
|
|
|
int maskBytesPerRow = (w + 7) / 8;
|
|
|
|
synchronized(image) {
|
|
for (int y = 0; y < h; y++) {
|
|
int cy = offset.y + y;
|
|
for (int x = 0; x < w; x++) {
|
|
int cx = offset.x + x;
|
|
int byte_ = cy * maskBytesPerRow + y / 8;
|
|
int bit = 7 - cx % 8;
|
|
|
|
if ((mask_[byte_] & (1 << bit)) != 0)
|
|
data.setDataElements(x+cx, y+cy, t.getDataElements(x+cx, y+cy, null));
|
|
}
|
|
}
|
|
}
|
|
|
|
commitBufferRW(r);
|
|
}
|
|
|
|
// pixel is the Pixel value to be used where mask_ is set
|
|
public void maskRect(Rect r, int pixel, byte[] mask)
|
|
{
|
|
// FIXME
|
|
}
|
|
|
|
// Render in a specific format
|
|
// Does the exact same thing as the above methods, but the given
|
|
// pixel values are defined by the given PixelFormat.
|
|
public void fillRect(PixelFormat pf, Rect dest, byte[] pix)
|
|
{
|
|
WritableRaster dstBuffer = getBufferRW(dest);
|
|
|
|
ColorModel cm = pf.getColorModel();
|
|
if (cm.isCompatibleRaster(dstBuffer) &&
|
|
cm.isCompatibleSampleModel(dstBuffer.getSampleModel())) {
|
|
fillRect(dest, pix);
|
|
} else {
|
|
ByteBuffer src =
|
|
ByteBuffer.allocate(dest.area()*pf.bpp/8).order(pf.getByteOrder());
|
|
for (int i=0; i < dest.area(); i++)
|
|
src.put(pix);
|
|
Raster raster = pf.rasterFromBuffer(dest, (ByteBuffer)src.rewind());
|
|
ColorConvertOp converter = format.getColorConvertOp(cm.getColorSpace());
|
|
synchronized(image) {
|
|
converter.filter(raster, dstBuffer);
|
|
}
|
|
}
|
|
|
|
commitBufferRW(dest);
|
|
}
|
|
|
|
public void imageRect(PixelFormat pf, Rect dest, byte[] pixels)
|
|
{
|
|
WritableRaster dstBuffer = getBufferRW(dest);
|
|
|
|
ColorModel cm = pf.getColorModel();
|
|
if (cm.isCompatibleRaster(dstBuffer) &&
|
|
cm.isCompatibleSampleModel(dstBuffer.getSampleModel())) {
|
|
imageRect(dest, pixels);
|
|
} else {
|
|
int length = dest.area()*pf.bpp/8;
|
|
ByteBuffer src = ByteBuffer.wrap(pixels, 0, length).order(pf.getByteOrder());
|
|
Raster raster = pf.rasterFromBuffer(dest, src);
|
|
ColorConvertOp converter = format.getColorConvertOp(cm.getColorSpace());
|
|
synchronized(image) {
|
|
converter.filter(raster, dstBuffer);
|
|
}
|
|
}
|
|
|
|
commitBufferRW(dest);
|
|
}
|
|
|
|
public void imageRect(PixelFormat pf, Rect dest, Raster pixels)
|
|
{
|
|
WritableRaster dstBuffer = getBufferRW(dest);
|
|
|
|
ColorModel cm = pf.getColorModel();
|
|
if (cm.isCompatibleRaster(dstBuffer) &&
|
|
cm.isCompatibleSampleModel(dstBuffer.getSampleModel())) {
|
|
synchronized(image) {
|
|
dstBuffer.setDataElements(0, 0, pixels);
|
|
}
|
|
} else {
|
|
ColorConvertOp converter = format.getColorConvertOp(cm.getColorSpace());
|
|
synchronized(image) {
|
|
converter.filter(pixels, dstBuffer);
|
|
}
|
|
}
|
|
|
|
commitBufferRW(dest);
|
|
}
|
|
|
|
}
|