123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658 |
- /* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
- * Copyright 2009 Pierre Ossman for Cendio AB
- * Copyright (C) 2011 D. R. Commander. All Rights Reserved.
- * Copyright (C) 2011 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.
- */
-
- //
- // PixelFormat
- //
-
- package com.tigervnc.rfb;
-
- import java.awt.color.*;
- import java.awt.image.*;
- import java.nio.*;
- import java.util.*;
-
- import com.tigervnc.rdr.*;
-
- public class PixelFormat {
-
- public PixelFormat(int b, int d, boolean e, boolean t,
- int rm, int gm, int bm, int rs, int gs, int bs)
- {
- bpp = b; depth = d; trueColour = t; bigEndian = e;
- redMax = rm; greenMax = gm; blueMax = bm;
- redShift = rs; greenShift = gs; blueShift = bs;
- converters = new HashMap<Integer, ColorConvertOp>();
- if (!isSane())
- throw new Exception("invalid pixel format");
-
- updateState();
- }
-
- public PixelFormat()
- {
- this(8, 8, false, true, 7, 7, 3, 0, 3, 6);
- updateState();
- }
-
- public boolean equal(PixelFormat other)
- {
- if (bpp != other.bpp || depth != other.depth)
- return false;
-
- if (redMax != other.redMax)
- return false;
- if (greenMax != other.greenMax)
- return false;
- if (blueMax != other.blueMax)
- return false;
-
- // Endianness requires more care to determine compatibility
- if (bigEndian == other.bigEndian || bpp == 8) {
- if (redShift != other.redShift)
- return false;
- if (greenShift != other.greenShift)
- return false;
- if (blueShift != other.blueShift)
- return false;
- } else {
- // Has to be the same byte for each channel
- if (redShift/8 != (3 - other.redShift/8))
- return false;
- if (greenShift/8 != (3 - other.greenShift/8))
- return false;
- if (blueShift/8 != (3 - other.blueShift/8))
- return false;
-
- // And the same bit offset within the byte
- if (redShift%8 != other.redShift%8)
- return false;
- if (greenShift%8 != other.greenShift%8)
- return false;
- if (blueShift%8 != other.blueShift%8)
- return false;
-
- // And not cross a byte boundary
- if (redShift/8 != (redShift + redBits - 1)/8)
- return false;
- if (greenShift/8 != (greenShift + greenBits - 1)/8)
- return false;
- if (blueShift/8 != (blueShift + blueBits - 1)/8)
- return false;
- }
-
- return true;
- }
-
- public void read(InStream is) {
- bpp = is.readU8();
- depth = is.readU8();
- bigEndian = is.readU8()!=0;
- trueColour = is.readU8()!=0;
- redMax = is.readU16();
- greenMax = is.readU16();
- blueMax = is.readU16();
- redShift = is.readU8();
- greenShift = is.readU8();
- blueShift = is.readU8();
- is.skip(3);
-
- // We have no real support for colour maps. If the client
- // wants one, then we force a 8-bit true colour format and
- // pretend it's a colour map.
- if (!trueColour) {
- redMax = 7;
- greenMax = 7;
- blueMax = 3;
- redShift = 0;
- greenShift = 3;
- blueShift = 6;
- }
-
- if (!isSane())
- throw new Exception("invalid pixel format: "+print());
-
- updateState();
- }
-
- public void write(OutStream os) {
- os.writeU8(bpp);
- os.writeU8(depth);
- os.writeU8(bigEndian?1:0);
- os.writeU8(trueColour?1:0);
- os.writeU16(redMax);
- os.writeU16(greenMax);
- os.writeU16(blueMax);
- os.writeU8(redShift);
- os.writeU8(greenShift);
- os.writeU8(blueShift);
- os.pad(3);
- }
-
- public final boolean isBigEndian() {
- return bigEndian;
- }
-
- public final boolean isLittleEndian() {
- return ! bigEndian;
- }
-
- public final boolean is888() {
- if(!trueColour)
- return false;
- if(bpp != 32)
- return false;
- if(depth != 24)
- return false;
- if(redMax != 255)
- return false;
- if(greenMax != 255)
- return false;
- if(blueMax != 255)
- return false;
- if ((redShift & 0x7) != 0)
- return false;
- if ((greenShift & 0x7) != 0)
- return false;
- if ((blueShift & 0x7) != 0)
- return false;
-
- return true;
- }
-
- public int pixelFromRGB(int red, int green, int blue, ColorModel cm)
- {
- if (trueColour) {
- int r = (red * redMax + 32767) / 65535;
- int g = (green * greenMax + 32767) / 65535;
- int b = (blue * blueMax + 32767) / 65535;
-
- return (r << redShift) | (g << greenShift) | (b << blueShift);
- } else if (cm != null) {
- // Try to find the closest pixel by Cartesian distance
- int colours = 1 << depth;
- int diff = 256 * 256 * 4;
- int col = 0;
- for (int i=0; i<colours; i++) {
- int r, g, b;
- r = cm.getRed(i);
- g = cm.getGreen(i);
- b = cm.getBlue(i);
- int rd = (r-red) >> 8;
- int gd = (g-green) >> 8;
- int bd = (b-blue) >> 8;
- int d = rd*rd + gd*gd + bd*bd;
- if (d < diff) {
- col = i;
- diff = d;
- }
- }
- return col;
- }
- // XXX just return 0 for colour map?
- return 0;
- }
-
- // This method should be invoked with duplicates of dst/src Buffers
- public void bufferFromRGB(ByteBuffer dst, ByteBuffer src, int pixels)
- {
- bufferFromRGB(dst, src, pixels, pixels, 1);
- }
-
- // This method should be invoked with duplicates of dst/src Buffers
- public void bufferFromRGB(ByteBuffer dst, ByteBuffer src,
- int w, int stride, int h)
- {
- if (is888()) {
- // Optimised common case
- int r, g, b, x;
-
- if (bigEndian) {
- r = dst.position() + (24 - redShift)/8;
- g = dst.position() + (24 - greenShift)/8;
- b = dst.position() + (24 - blueShift)/8;
- x = dst.position() + (24 - (48 - redShift - greenShift - blueShift))/8;
- } else {
- r = dst.position() + redShift/8;
- g = dst.position() + greenShift/8;
- b = dst.position() + blueShift/8;
- x = dst.position() + (48 - redShift - greenShift - blueShift)/8;
- }
-
- int dstPad = (stride - w) * 4;
- while (h-- > 0) {
- int w_ = w;
- while (w_-- > 0) {
- dst.put(r, src.get());
- dst.put(g, src.get());
- dst.put(b, src.get());
- dst.put(x, (byte)0);
- r += 4;
- g += 4;
- b += 4;
- x += 4;
- }
- r += dstPad;
- g += dstPad;
- b += dstPad;
- x += dstPad;
- }
- } else {
- // Generic code
- int dstPad = (stride - w) * bpp/8;
- while (h-- > 0) {
- int w_ = w;
- while (w_-- > 0) {
- int p;
- int r, g, b;
-
- r = src.get();
- g = src.get();
- b = src.get();
-
- p = pixelFromRGB(r, g, b, model);
-
- bufferFromPixel(dst, p);
- dst.position(dst.position() + bpp/8);
- }
- dst.position(dst.position() + dstPad);
- }
- }
- }
-
- // This method should be invoked with duplicates of dst/src Buffers
- public void rgbFromBuffer(ByteBuffer dst, ByteBuffer src, int pixels)
- {
- rgbFromBuffer(dst, src, pixels, pixels, 1);
- }
-
- // This method should be invoked with duplicates of dst/src Buffers
- public void rgbFromBuffer(ByteBuffer dst, ByteBuffer src,
- int w, int stride, int h)
- {
- if (is888()) {
- // Optimised common case
- int r, g, b;
-
- if (bigEndian) {
- r = src.position() + (24 - redShift)/8;
- g = src.position() + (24 - greenShift)/8;
- b = src.position() + (24 - blueShift)/8;
- } else {
- r = src.position() + redShift/8;
- g = src.position() + greenShift/8;
- b = src.position() + blueShift/8;
- }
-
- int srcPad = (stride - w) * 4;
- while (h-- > 0) {
- int w_ = w;
- while (w_-- > 0) {
- dst.put(src.get(r));
- dst.put(src.get(g));
- dst.put(src.get(b));
- r += 4;
- g += 4;
- b += 4;
- }
- r += srcPad;
- g += srcPad;
- b += srcPad;
- }
- } else {
- // Generic code
- int srcPad = (stride - w) * bpp/8;
- while (h-- > 0) {
- int w_ = w;
- while (w_-- > 0) {
- int p;
- byte r, g, b;
-
- p = pixelFromBuffer(src.duplicate());
-
- r = (byte)getColorModel().getRed(p);
- g = (byte)getColorModel().getGreen(p);
- b = (byte)getColorModel().getBlue(p);
-
- dst.put(r);
- dst.put(g);
- dst.put(b);
- src.position(src.position() + bpp/8);
- }
- src.position(src.position() + srcPad);
- }
- }
- }
-
- public void rgbFromPixels(byte[] dst, int dstPtr, int[] src, int srcPtr, int pixels, ColorModel cm)
- {
- int p;
- byte r, g, b;
-
- for (int i=0; i < pixels; i++) {
- p = src[i];
-
- dst[dstPtr++] = (byte)cm.getRed(p);
- dst[dstPtr++] = (byte)cm.getGreen(p);
- dst[dstPtr++] = (byte)cm.getBlue(p);
- }
- }
-
- // This method should be invoked with a duplicates of buffer
- public int pixelFromBuffer(ByteBuffer buffer)
- {
- int p;
-
- p = 0xff000000;
-
- if (!bigEndian) {
- switch (bpp) {
- case 32:
- p |= buffer.get() << 24;
- p |= buffer.get() << 16;
- case 16:
- p |= buffer.get() << 8;
- case 8:
- p |= buffer.get();
- }
- } else {
- p |= buffer.get(0);
- if (bpp >= 16) {
- p |= buffer.get(1) << 8;
- if (bpp == 32) {
- p |= buffer.get(2) << 16;
- p |= buffer.get(3) << 24;
- }
- }
- }
-
- return p;
- }
-
- public String print() {
- StringBuffer s = new StringBuffer();
- s.append("depth "+depth+" ("+bpp+"bpp)");
- if (bpp != 8) {
- if (bigEndian)
- s.append(" big-endian");
- else
- s.append(" little-endian");
- }
-
- if (!trueColour) {
- s.append(" colour-map");
- return s.toString();
- }
-
- if (blueShift == 0 && greenShift > blueShift && redShift > greenShift &&
- blueMax == (1 << greenShift) - 1 &&
- greenMax == (1 << (redShift-greenShift)) - 1 &&
- redMax == (1 << (depth-redShift)) - 1)
- {
- s.append(" rgb"+(depth-redShift)+(redShift-greenShift)+greenShift);
- return s.toString();
- }
-
- if (redShift == 0 && greenShift > redShift && blueShift > greenShift &&
- redMax == (1 << greenShift) - 1 &&
- greenMax == (1 << (blueShift-greenShift)) - 1 &&
- blueMax == (1 << (depth-blueShift)) - 1)
- {
- s.append(" bgr"+(depth-blueShift)+(blueShift-greenShift)+greenShift);
- return s.toString();
- }
-
- s.append(" rgb max "+redMax+","+greenMax+","+blueMax+" shift "+redShift+
- ","+greenShift+","+blueShift);
- return s.toString();
- }
-
- private static int bits(int value)
- {
- int bits;
-
- bits = 16;
-
- if ((value & 0xff00) == 0) {
- bits -= 8;
- value <<= 8;
- }
- if ((value & 0xf000) == 0) {
- bits -= 4;
- value <<= 4;
- }
- if ((value & 0xc000) == 0) {
- bits -= 2;
- value <<= 2;
- }
- if ((value & 0x8000) == 0) {
- bits -= 1;
- value <<= 1;
- }
-
- return bits;
- }
-
- private void updateState()
- {
- int endianTest = 1;
-
- redBits = bits(redMax);
- greenBits = bits(greenMax);
- blueBits = bits(blueMax);
-
- maxBits = redBits;
- if (greenBits > maxBits)
- maxBits = greenBits;
- if (blueBits > maxBits)
- maxBits = blueBits;
-
- minBits = redBits;
- if (greenBits < minBits)
- minBits = greenBits;
- if (blueBits < minBits)
- minBits = blueBits;
-
- if ((((char)endianTest) == 0) != bigEndian)
- endianMismatch = true;
- else
- endianMismatch = false;
-
- model = getColorModel(this);
- }
-
- private boolean isSane()
- {
- int totalBits;
-
- if ((bpp != 8) && (bpp != 16) && (bpp != 32))
- return false;
- if (depth > bpp)
- return false;
-
- if (!trueColour && (depth != 8))
- return false;
-
- if ((redMax & (redMax + 1)) != 0)
- return false;
- if ((greenMax & (greenMax + 1)) != 0)
- return false;
- if ((blueMax & (blueMax + 1)) != 0)
- return false;
-
- /*
- * We don't allow individual channels > 8 bits in order to keep our
- * conversions simple.
- */
- if (redMax >= (1 << 8))
- return false;
- if (greenMax >= (1 << 8))
- return false;
- if (blueMax >= (1 << 8))
- return false;
-
- totalBits = bits(redMax) + bits(greenMax) + bits(blueMax);
- if (totalBits > depth)
- return false;
-
- if ((bits(redMax) + redShift) > bpp)
- return false;
- if ((bits(greenMax) + greenShift) > bpp)
- return false;
- if ((bits(blueMax) + blueShift) > bpp)
- return false;
-
- if (((redMax << redShift) & (greenMax << greenShift)) != 0)
- return false;
- if (((redMax << redShift) & (blueMax << blueShift)) != 0)
- return false;
- if (((greenMax << greenShift) & (blueMax << blueShift)) != 0)
- return false;
-
- return true;
- }
-
- public void bufferFromPixel(ByteBuffer buffer, int p)
- {
- if (bigEndian) {
- switch (bpp) {
- case 32:
- buffer.put((byte)((p >> 24) & 0xff));
- buffer.put((byte)((p >> 16) & 0xff));
- break;
- case 16:
- buffer.put((byte)((p >> 8) & 0xff));
- break;
- case 8:
- buffer.put((byte)((p >> 0) & 0xff));
- break;
- }
- } else {
- buffer.put(0, (byte)((p >> 0) & 0xff));
- if (bpp >= 16) {
- buffer.put(1, (byte)((p >> 8) & 0xff));
- if (bpp == 32) {
- buffer.put(2, (byte)((p >> 16) & 0xff));
- buffer.put(3, (byte)((p >> 24) & 0xff));
- }
- }
- }
- }
-
- public ColorModel getColorModel()
- {
- return model;
- }
-
- public static ColorModel getColorModel(PixelFormat pf) {
- if (!(pf.bpp == 32) && !(pf.bpp == 16) && !(pf.bpp == 8))
- throw new Exception("Internal error: bpp must be 8, 16, or 32 in PixelBuffer ("+pf.bpp+")");
- ColorModel cm;
- switch (pf.depth) {
- case 3:
- // Fall-through to depth 8
- case 6:
- // Fall-through to depth 8
- case 8:
- int rmask = pf.redMax << pf.redShift;
- int gmask = pf.greenMax << pf.greenShift;
- int bmask = pf.blueMax << pf.blueShift;
- cm = new DirectColorModel(8, rmask, gmask, bmask);
- break;
- case 16:
- cm = new DirectColorModel(32, 0xF800, 0x07C0, 0x003E);
- break;
- case 24:
- cm = new DirectColorModel(32, (0xff << 16), (0xff << 8), 0xff);
- break;
- case 32:
- cm = new DirectColorModel(32, (0xff << pf.redShift),
- (0xff << pf.greenShift), (0xff << pf.blueShift));
- break;
- default:
- throw new Exception("Unsupported color depth ("+pf.depth+")");
- }
- assert(cm != null);
- return cm;
- }
-
- public ColorConvertOp getColorConvertOp(ColorSpace src)
- {
- // The overhead associated with initializing ColorConvertOps is
- // enough to justify maintaining a static lookup table.
- if (converters.containsKey(src.getType()))
- return converters.get(src.getType());
- ColorSpace dst = model.getColorSpace();
- converters.put(src.getType(), new ColorConvertOp(src, dst, null));
- return converters.get(src.getType());
- }
-
- public ByteOrder getByteOrder()
- {
- if (isBigEndian())
- return ByteOrder.BIG_ENDIAN;
- else
- return ByteOrder.LITTLE_ENDIAN;
- }
-
- public Raster rasterFromBuffer(Rect r, ByteBuffer buf)
- {
- Buffer dst;
- DataBuffer db = null;
-
- SampleModel sm =
- model.createCompatibleSampleModel(r.width(), r.height());
- switch (sm.getTransferType()) {
- case DataBuffer.TYPE_INT:
- dst = IntBuffer.allocate(r.area()).put(buf.asIntBuffer());
- db = new DataBufferInt(((IntBuffer)dst).array(), r.area());
- break;
- case DataBuffer.TYPE_BYTE:
- db = new DataBufferByte(buf.array(), r.area());
- break;
- case DataBuffer.TYPE_SHORT:
- dst = ShortBuffer.allocate(r.area()).put(buf.asShortBuffer());
- db = new DataBufferShort(((ShortBuffer)dst).array(), r.area());
- break;
- }
- assert(db != null);
- return Raster.createRaster(sm, db, new java.awt.Point(0, 0));
- }
-
- private static HashMap<Integer, ColorConvertOp> converters;
-
- public int bpp;
- public int depth;
- public boolean bigEndian;
- public boolean trueColour;
- public int redMax;
- public int greenMax;
- public int blueMax;
- public int redShift;
- public int greenShift;
- public int blueShift;
-
- protected int redBits, greenBits, blueBits;
- protected int maxBits, minBits;
- protected boolean endianMismatch;
-
- private ColorModel model;
- }
|