/* Copyright 2016 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 synchronized 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());
buf.setDataElements(0, 0, raster);
commitBufferRW(r);
}
// Copy pixel data to the buffer
public synchronized 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);
dest.setDataElements(0, 0, raster);
commitBufferRW(r);
}
// Copy pixel data from one PixelBuffer location to another
public synchronized 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);
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 synchronized 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;
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 synchronized 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 synchronized 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());
converter.filter(raster, dstBuffer);
}
commitBufferRW(dest);
}
public synchronized 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());
converter.filter(raster, dstBuffer);
}
commitBufferRW(dest);
}
public synchronized void imageRect(PixelFormat pf, Rect dest, Raster pixels)
{
WritableRaster dstBuffer = getBufferRW(dest);
ColorModel cm = pf.getColorModel();
if (cm.isCompatibleRaster(dstBuffer) &&
cm.isCompatibleSampleModel(dstBuffer.getSampleModel())) {
dstBuffer.setDataElements(0, 0, pixels);
} else {
ColorConvertOp converter = format.getColorConvertOp(cm.getColorSpace());
converter.filter(pixels, dstBuffer);
}
commitBufferRW(dest);
}
}