/* * $Id$ * * Copyright (C) 2003-2015 JNode.org * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library 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 Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; If not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ package org.jnode.driver.video.vesa; import gnu.classpath.SystemProperties; import java.awt.Color; import java.awt.Rectangle; import java.awt.Shape; import java.awt.geom.AffineTransform; import java.awt.image.ColorModel; import java.awt.image.DirectColorModel; import java.awt.image.Raster; import javax.naming.NameNotFoundException; import org.apache.log4j.Logger; import org.jnode.awt.util.BitmapGraphics; import org.jnode.driver.DriverException; import org.jnode.driver.bus.pci.PCIDevice; import org.jnode.driver.video.FrameBufferConfiguration; import org.jnode.driver.video.HardwareCursor; import org.jnode.driver.video.HardwareCursorAPI; import org.jnode.driver.video.Surface; import org.jnode.driver.video.cursor.SoftwareCursor; import org.jnode.driver.video.util.AbstractSurface; import org.jnode.naming.InitialNaming; import org.jnode.system.resource.MemoryResource; import org.jnode.system.resource.ResourceManager; import org.jnode.system.resource.ResourceNotFreeException; import org.jnode.util.NumberUtils; import org.vmmagic.unboxed.Address; /** * * @author Fabien DUMINY (fduminy at jnode.org) * */ public class VESACore extends AbstractSurface implements HardwareCursorAPI { /** My logger */ private static final Logger log = Logger.getLogger(VESACore.class); private final VESADriver driver; private final MemoryResource videoRam; private final int videoRamSize; private final int maxWidth; private final int maxHeight; private final int bitsPerPixel; private final int redMask; private final int greenMask; private final int blueMask; private final int alphaMask; private final int redMaskShift; private final int greenMaskShift; private final int blueMaskShift; private final int alphaMaskShift; private final int capabilities; private int bytesPerLine; private int offset; private int displayWidth; private SoftwareCursor bitmapGraphics; private ColorModel model; /** * Create a new instance * * @param driver * @param device */ public VESACore(VESADriver driver, VbeInfoBlock vbeInfoBlock, ModeInfoBlock modeInfoBlock, PCIDevice device) throws ResourceNotFreeException, DriverException { super(modeInfoBlock.getXResolution(), modeInfoBlock.getYResolution()); this.driver = driver; Address address = Address.fromIntZeroExtend(modeInfoBlock.getRamBase()); try { final ResourceManager rm = InitialNaming.lookup(ResourceManager.NAME); this.capabilities = vbeInfoBlock.getCapabilities(); this.maxWidth = modeInfoBlock.getXResolution(); this.maxHeight = modeInfoBlock.getYResolution(); this.bitsPerPixel = modeInfoBlock.getBitsPerPixel(); int multiplier = 1; if (bitsPerPixel == 16) multiplier = 2; else if (bitsPerPixel == 24) multiplier = 3; else if (bitsPerPixel == 32) multiplier = 4; this.bytesPerLine = modeInfoBlock.getXResolution() * multiplier; this.videoRamSize = bytesPerLine * modeInfoBlock.getYResolution(); this.videoRam = rm.claimMemoryResource(device, address, videoRamSize, ResourceManager.MEMMODE_NORMAL); BitmapGraphics graphics; switch (bitsPerPixel) { case 8: graphics = BitmapGraphics.create8bppInstance(videoRam, width, height, bytesPerLine, 0); // 8 bits color depth this.redMask = 0x00000003; // TODO get from modeInfoBlock this.greenMask = 0x0000000C; // TODO get from modeInfoBlock this.blueMask = 0x00000030; // TODO get from modeInfoBlock break; case 16: graphics = BitmapGraphics.create16bppInstance(videoRam, width, height, bytesPerLine, 0); // 16 bits color depth this.redMask = 0x00007C00; // TODO get from modeInfoBlock this.greenMask = 0x000003E0; // TODO get from modeInfoBlock this.blueMask = 0x0000001F; // TODO get from modeInfoBlock break; case 24: graphics = BitmapGraphics.create24bppInstance(videoRam, width, height, bytesPerLine, 0); // 24 bits color depth this.redMask = 0x00FF0000; // TODO get from modeInfoBlock this.greenMask = 0x0000FF00; // TODO get from modeInfoBlock this.blueMask = 0x000000FF; // TODO get from modeInfoBlock break; case 32: graphics = BitmapGraphics.create32bppInstance(videoRam, width, height, bytesPerLine, 0); // 32 bits color depth this.redMask = 0x00FF0000; // TODO get from modeInfoBlock this.greenMask = 0x0000FF00; // TODO get from modeInfoBlock this.blueMask = 0x000000FF; // TODO get from modeInfoBlock break; default: throw new DriverException("Unknown bits/pixel value " + bitsPerPixel); } bitmapGraphics = new SoftwareCursor(graphics); String transparency = SystemProperties.getProperty("org.jnode.awt.transparency"); if ((bitsPerPixel == 32) && (transparency != null) && "true".equals(transparency)) { this.alphaMask = 0xff000000; // - transparency enabled } else { this.alphaMask = 0x00000000; // - transparency disabled } this.redMaskShift = getMaskShift(redMask); this.greenMaskShift = getMaskShift(greenMask); this.blueMaskShift = getMaskShift(blueMask); this.alphaMaskShift = getMaskShift(alphaMask); } catch (NameNotFoundException ex) { throw new ResourceNotFreeException(ex); } } /** * Release all resources */ public final void release() { videoRam.release(); } /** * Open a given configuration * * @param config */ public void open(FrameBufferConfiguration config) { final int w = config.getScreenWidth(); final int h = config.getScreenHeight(); setMode(w, h, config.getColorModel()); fillRect(0, 0, w, h, 0, PAINT_MODE); dumpState(); // For debugging purposes } /** * Close the SVGA screen * * @see org.jnode.driver.video.Surface#close() */ public synchronized void close() { driver.close(this); super.close(); } /** * Initialize the graphics mode * * @param width * @param height */ public final void setMode(int width, int height, ColorModel model) { this.model = model; setSize(width, height); this.width = width; this.height = height; BitmapGraphics graphics; switch (bitsPerPixel) { case 8: graphics = BitmapGraphics.create8bppInstance(videoRam, width, height, bytesPerLine, offset); bitmapGraphics.setBitmapGraphics(graphics); break; case 16: graphics = BitmapGraphics.create16bppInstance(videoRam, width, height, bytesPerLine, offset); bitmapGraphics.setBitmapGraphics(graphics); break; case 24: graphics = BitmapGraphics.create24bppInstance(videoRam, width, height, bytesPerLine, offset); bitmapGraphics.setBitmapGraphics(graphics); break; case 32: graphics = BitmapGraphics.create32bppInstance(videoRam, width, height, bytesPerLine, offset, model.getTransparency()); bitmapGraphics.setBitmapGraphics(graphics); break; } dumpState(); } public FrameBufferConfiguration[] getConfigs() { try { final ColorModel cm = new DirectColorModel(bitsPerPixel, redMask, greenMask, blueMask, alphaMask); return new FrameBufferConfiguration[] {new VESAConfiguration(maxWidth, maxHeight, cm), }; } catch (Throwable t) { throw new RuntimeException(t); } } /** * Update the entire screen */ public final void updateScreen() { updateScreen(0, 0, width, height); } /** * Update the given region of the screen */ public final void updateScreen(int x, int y, int width, int height) { // TODO } /** * Draw the given shape * * @param shape * @param color * @param mode */ public final synchronized void draw(Shape shape, Shape clip, AffineTransform tx, Color color, int mode) { super.draw(shape, clip, tx, color, mode); final Rectangle r = getBounds(shape, tx); updateScreen(r.x - 1, r.y - 1, r.width + 2, r.height + 2); } /** * @see org.jnode.driver.video.Surface#copyArea(int, int, int, int, int, * int) */ public void copyArea(int x, int y, int width, int height, int dx, int dy) { bitmapGraphics.copyArea(x, y, width, height, dx, dy); updateScreen(dx, dy, width, height); } /** * Draw an image to this surface * * @param src * @param srcX * @param srcY * @param x The upper left x coordinate * @param y The upper left y coordinate * @param w * @param h * @param bgColor The background color to use for transparent pixels. If * null, no transparent pixels are unmodified on the destination */ public void drawCompatibleRaster(Raster src, int srcX, int srcY, int x, int y, int w, int h, Color bgColor) { if (bgColor != null) { bitmapGraphics.drawImage(src, srcX, srcY, x, y, w, h, convertColor(bgColor)); } else { bitmapGraphics.drawImage(src, srcX, srcY, x, y, w, h); } updateScreen(x, y, w, h); } /** * @see org.jnode.driver.video.Surface#fill(Shape, Shape, AffineTransform, * Color, int) */ public final synchronized void fill(Shape shape, Shape clip, AffineTransform tx, Color color, int mode) { super.fill(shape, clip, tx, color, mode); final Rectangle b = getBounds(shape, tx); updateScreen(b.x, b.y, b.width, b.height); } /** * Fill a given rectangle with a given color */ public final synchronized void fillRectangle(int x1, int y1, int x2, int y2, Color color, int mode) { fillRect(x1, y1, x2 - x1, y2 - y1, convertColor(color), mode); } /** * Fill a given rectangle with a given color * * @param x * @param y * @param width * @param height * @param color * @param mode */ public final void fillRect(int x, int y, int width, int height, int color, int mode) { if (x < 0) { width = Math.max(0, x + width); x = 0; } if (y < 0) { height = Math.max(0, y + height); y = 0; } if ((width > 0) && (height > 0)) { // TODO optimize it like in VMWareCore ? for (int line = y + height - 1; line >= y; line--) { bitmapGraphics.drawPixels(x, line, width, color, mode); } } } /** * Dump the state to log. */ public final void dumpState() { log.debug("Max. Resolution " + maxWidth + "*" + maxHeight); log.debug("Cur. Resolution " + width + "*" + height); log.debug("Bits/Pixel " + bitsPerPixel); log.debug("Bytes/Line " + bytesPerLine); log.debug("Offset " + offset); log.debug("Display width " + displayWidth); log.debug("Red mask 0x" + NumberUtils.hex(redMask)); log.debug("Green mask 0x" + NumberUtils.hex(greenMask)); log.debug("Blue mask 0x" + NumberUtils.hex(blueMask)); log.debug("Capabilities 0x" + NumberUtils.hex(capabilities)); } /** * Set the pixel at the given location to the given color. * * @param x * @param y * @param color */ public final void drawPixel(int x, int y, int color, int mode) { bitmapGraphics.drawPixels(x, y, 1, color, mode); } /** * Low level draw line method. This method does not call updateScreen. * * @param x1 * @param y1 * @param x2 * @param y2 * @param c * @param mode */ public final void drawLine(int x1, int y1, int x2, int y2, int c, int mode) { if (x1 == x2) { // Vertical line fillRect(x1, Math.min(y1, y2), 1, Math.abs(y2 - y1), c, mode); } else if (y1 == y2) { // Horizontal line // drawHorizontalLine(Math.min(x1, x2), y1, Math.abs(x2 - x1)+1, c, // mode); fillRect(Math.min(x1, x2), y1, Math.abs(x2 - x1) + 1, 1, c, mode); } else { super.drawLine(x1, y1, x2, y2, c, mode); } } protected final void drawHorizontalLine(int x, int y, int w, int color, int mode) { if ((x >= 0) && (x < width) && (y >= 0) && (y < height)) { w = Math.min(width - x, w); final int ofsY = bytesPerLine * y; int ofs; if (mode == Surface.XOR_MODE) { switch (bitsPerPixel) { case 8: ofs = ofsY + x; videoRam.xorByte(ofs, (byte) color, w); break; case 16: ofs = ofsY + (x << 1); videoRam.xorShort(ofs, (short) color, w); break; case 24: ofs = ofsY + (x * 3); while (w > 0) { videoRam.xorShort(ofs, (short) (color & 0xFFFF), 1); videoRam.xorByte(ofs + 2, (byte) ((color >> 16) & 0xFF), 1); w--; ofs += 3; } break; case 32: ofs = ofsY + (x << 2); videoRam.xorInt(ofs, color, w); break; default: throw new RuntimeException("Unknown bitsPerPixel"); } } else { switch (bitsPerPixel) { case 8: ofs = ofsY + x; videoRam.setByte(ofs, (byte) color, w); break; case 16: ofs = ofsY + (x << 1); videoRam.setShort(ofs, (short) color, w); break; case 24: ofs = ofsY + (x * 3); while (w > 0) { videoRam.setShort(ofs, (short) (color & 0xFFFF)); videoRam.setByte(ofs + 2, (byte) ((color >> 16) & 0xFF)); w--; ofs += 3; } break; case 32: ofs = ofsY + (x << 2); videoRam.setInt(ofs, color, w); break; default: throw new RuntimeException("Unknown bitsPerPixel"); } } } } /** * Convert the given color to a value suitable for VMWare * * @param color */ protected final int convertColor(Color color) { return convertColor(color.getRed(), color.getGreen(), color.getBlue(), color.getAlpha()); } /** * Convert the given color to a value suitable for VMWare * * @param r * @param g * @param b */ protected final int convertColor(int r, int g, int b) { return ((r << redMaskShift) & redMask) | ((g << greenMaskShift) & greenMask) | ((b << blueMaskShift) & blueMask); } protected final int convertColor(int r, int g, int b, int a) { return ((a << alphaMaskShift) & alphaMask) | ((r << redMaskShift) & redMask) | ((g << greenMaskShift) & greenMask) | ((b << blueMaskShift) & blueMask); } /** * Gets the size of the video ram in bytes. */ final int getVideoRamSize() { return this.videoRamSize; } /** * Gets the maximum screen height in pixels */ final int getMaxHeight() { return this.maxHeight; } /** * Gets the maximum screen width in pixels */ final int getMaxWidth() { return this.maxWidth; } /** * Gets the number of bits per pixel */ final int getBitsPerPixel() { return this.bitsPerPixel; } /** * Gets the number of bytes per line */ final int getBytesPerLine() { return this.bytesPerLine; } /** * Gets the number of shift needed for the given mask. * * E.g. getMaskShift(0xFF00) == 8 * * @param mask * @return */ private final int getMaskShift(int mask) { if (mask == 0) return 0; int count = 0; while ((mask & 1) == 0) { count++; mask = mask >> 1; } return count; } /** * @see org.jnode.driver.video.Surface#getColorModel() */ public ColorModel getColorModel() { return model; } /** * @see org.jnode.driver.video.HardwareCursorAPI#setCursorPosition(int, int) */ public synchronized void setCursorPosition(int x, int y) { bitmapGraphics.setCursorPosition(x, y); } /** * @see org.jnode.driver.video.HardwareCursorAPI#setCursorVisible(boolean) */ public synchronized void setCursorVisible(boolean visible) { bitmapGraphics.setCursorVisible(visible); } /** * Sets the cursor image. * * @param cursor */ public void setCursorImage(HardwareCursor cursor) { bitmapGraphics.setCursorImage(cursor); } /** * @see org.jnode.driver.video.Surface#drawAlphaRaster(java.awt.image.Raster, * java.awt.geom.AffineTransform, int, int, int, int, int, int, * java.awt.Color) */ public void drawAlphaRaster(Raster raster, AffineTransform tx, int srcX, int srcY, int dstX, int dstY, int width, int height, Color color) { bitmapGraphics.drawAlphaRaster(raster, tx, srcX, srcY, dstX, dstY, width, height, convertColor(color)); } @Override public int getRGBPixel(int x, int y) { return bitmapGraphics.doGetPixel(x, y); } @Override public int[] getRGBPixels(Rectangle region) { return bitmapGraphics.doGetPixels(region); } }