=== modified file 'src/com/goldencode/p2j/ui/client/driver/web/res/p2j.socket.js' --- src/com/goldencode/p2j/ui/client/driver/web/res/p2j.socket.js 2015-08-10 18:28:09 +0000 +++ src/com/goldencode/p2j/ui/client/driver/web/res/p2j.socket.js 2015-08-10 20:38:03 +0000 @@ -19,9 +19,9 @@ ** can be used for both ChUI and GUI web clients. ** 008 SBI 20150731 Applied "use strict" directive, fixed undeclared variables. ** 009 GES 20150709 Added support for MSG_READ_CLIPBOARD (server-driven clipboard request) and -** MSG_WRITE_CLIPBOARD. -** 010 SBI 20150807 Fixed a network order for reading int32 from binaries message -** 011 CA 20150810 Added infrastructure for font and metrics related requests. +** MSG_WRITE_CLIPBOARD, fixed a network order for reading int32 +** from binaries message. +** 010 CA 20150810 Added infrastructure for font and metrics related requests. */ "use strict"; @@ -219,7 +219,7 @@ for (var i = 0; i < 4; i++) { - num |= message[offset + i] << (8 * (3-i)); + num |= message[offset + i] << (8 * (3 - i)); } return num; === added file 'src/com/goldencode/p2j/ui/client/gui/driver/BufferedImageDrawHelper.java' --- src/com/goldencode/p2j/ui/client/gui/driver/BufferedImageDrawHelper.java 1970-01-01 00:00:00 +0000 +++ src/com/goldencode/p2j/ui/client/gui/driver/BufferedImageDrawHelper.java 2015-08-10 20:28:50 +0000 @@ -0,0 +1,306 @@ +/* +** Module : BufferedImageDrawHelper.java +** Abstract : Implements the helper function for images processing. +** +** Copyright (c) 2015, Golden Code Development Corporation. +** ALL RIGHTS RESERVED. Use is subject to license terms. +** +** Golden Code Development Corporation +** CONFIDENTIAL +** +** -#- -I- --Date-- --------------------------------Description---------------------------------- +** 001 SBI 20150810 First version which implements the helper function for image processing +** as a part of the common functionality used by swing and web clients. +*/ + +package com.goldencode.p2j.ui.client.gui.driver; + +import java.awt.*; +import java.awt.image.*; + +/** + * BufferedImageDrawHelper implements the common functionality used by swing and web clients + * to transform images. + */ +public class BufferedImageDrawHelper implements ClientImageDrawHelper +{ + + /** + * Do the common transformations on the target image. + * + * @param ps + * The holder for images parameters. + * + * @return The wrapped image with its with and its height. + */ + @Override + public ImageWrapper processImage(PaintStructure ps) + { + BufferedImage img2draw = null; + int width = 0; + int height = 0; + // scaling factors for vertical and horizontal + float kHor = 0; + float kVer = 0; + if (ps.xOffset == -1 && ps.yOffset == -1) + { + // full original image + img2draw = ((ImageWrapper)ps.img).getImage(); + width = ps.width; + height = ps.height; + kHor = ((float)ps.width) / ((float) ps.img.getWidth()); + kVer = ((float)ps.height) / ((float) ps.img.getHeight()); + } + else + { + // need to recalc the width and height for subimage to cut + // to not go out of image borders + int widthToCut = ps.img.getWidth() - ps.xOffset; + if (widthToCut > ps.width) + { + widthToCut = ps.width; + } + int heightToCut = ps.img.getHeight() - ps.yOffset; + if (heightToCut > ps.height) + { + heightToCut = ps.height; + } + // partial image, get subimage + BufferedImage fullImg = ((ImageWrapper)ps.img).getImage(); + BufferedImage bimg = fullImg.getSubimage(ps.xOffset, + ps.yOffset, + widthToCut, + heightToCut); + + if (bimg != null) + { + img2draw = bimg; + width = widthToCut; + height = heightToCut; + kHor = ((float)ps.width) / ((float)widthToCut); + kVer = ((float)ps.height) / ((float)heightToCut); + } + } + // finally draw the image + if (img2draw != null) + { + // check binary image options + // 3D color conversions + if (ps.convert3D) + { + img2draw = get3DColorsConvertedImage((BufferedImage)img2draw); + } + // transparency + if (ps.transparent) + { + int pixTrans = img2draw.getRGB(0, height - 1); + img2draw = getTransparentImage(img2draw, pixTrans); + } + // resizing + if (ps.stretchToFit) + { + if (ps.retainShape) + { + // need to calculate native aspect ratio for image + // if we have two different scaling factors for x and y we choose only one + // to keep scaling in both directions, this must be one that is smaller + if (kHor < kVer) + { + width = ps.width; + height = Math.round(((float)height) * kHor); + } + else + { + width = Math.round(((float)width) * kVer); + height = ps.height; + } + } + else + { + width = ps.width; + height = ps.height; + } + } + } + + return new ImageWrapper(img2draw, height, width); + } + + /** + * Converts the image replacing standard 3D colors with currently redefined. + * + * @param img + * Initial image to convert. + * + * @return The image with replaced 3D colors color. + */ + private BufferedImage get3DColorsConvertedImage(BufferedImage img) + { + // define custom image producer + ImageProducer ip = new FilteredImageSource(img.getSource(), + new Convert3DColorsFilter(SystemColor.controlLtHighlight.getRGB(), + SystemColor.control.getRGB(), + SystemColor.controlShadow.getRGB(), + SystemColor.controlText.getRGB())); + // convert image + return getBufferedImage(Toolkit.getDefaultToolkit().createImage(ip)); + } + + /** + * Converts the image into buffered image. + * + * @param imgToConvert + * Initial image to convert. + * + * @return The buffered image for further use. + */ + private BufferedImage getBufferedImage(java.awt.Image imgToConvert) + { + // create empty image + BufferedImage imgRes = new BufferedImage(imgToConvert.getWidth(null), + imgToConvert.getHeight(null), + BufferedImage.TYPE_INT_ARGB); + + // copy data to newly created image + Graphics2D g2img = imgRes.createGraphics(); + g2img.drawImage(imgToConvert, 0, 0, null); + g2img.dispose(); + + return imgRes; + } + + /** + * Custom RGB filter to change image transparency data on the fly. + */ + static class TransparencyFilter + extends RGBImageFilter + { + /** Alpha channel constant. */ + private static final int TRANSPARENCY_MASK = 0xFF000000; + + /** Pixel to use as transparent marker. */ + int rgbCheck; + + /** + * Creates new filter with the given transparent pixel value. + * + * @param pixTrans + * The pixel sample we will consider as transparent. + */ + TransparencyFilter(int pixTrans) + { + rgbCheck = pixTrans | TRANSPARENCY_MASK; + } + + /** + * This is on the fly converter to turn on transparency for every pixels the sames as + * one we defined as marker on the filter construction. This method is calling when image + * drawing is happening. + * + * @param x + * The X pixel coordinate. + * @param y + * The Y pixel coordinate. + * @param pixel + * The ARGB pixel value of the original image. + * + * @return The new ARGB pixel value after transformation to render. + */ + public int filterRGB(int x, int y, int pixel) + { + return (pixel | TRANSPARENCY_MASK) == rgbCheck ? pixel & ~TRANSPARENCY_MASK : pixel; + } + } + + /** + * Converts the image to one having transparent color. + * + * @param img + * Initial image to convert. + * @param transPix + * The color to be considered as transparent. + * + * @return The image with one tarnsparent color. + */ + private BufferedImage getTransparentImage(BufferedImage img, int transPix) + { + // define custom image producer + ImageProducer ip = new FilteredImageSource(img.getSource(), + new TransparencyFilter(transPix)); + // convert image + return getBufferedImage(Toolkit.getDefaultToolkit().createImage(ip)); + } + + /** + * Custom RGB filter to change image 3D colors on the fly. + */ + static class Convert3DColorsFilter + extends RGBImageFilter + { + /** Original 3D white color(255, 255, 255). */ + private static final int COLOR_3D_WHITE = 0x00FFFFFF; + + /** Original 3D light gray color(192, 192, 192). */ + private static final int COLOR_3D_LT_GRAY = 0x00C0C0C0; + + /** Original 3D dark gray color(128, 128, 128). */ + private static final int COLOR_3D_DK_GRAY = 0x00808080; + + /** Original 3D black color(0, 0, 0). */ + private static final int COLOR_3D_BLACK = 0x00000000; + + /** New value for 3D white. */ + int newWhite; + + /** New value for 3D light gray. */ + int newLtGray; + + /** New value for 3D dark gray. */ + int newDkGray; + + /** New value for 3D black. */ + int newBlack; + + /** + * Creates new filter with the given 3D colors to replace. + * + * @param white + * The new value for white color. + * @param ltGray + * The new value for light gray color. + * @param dkGray + * The new value for dark gray color. + * @param black + * The new value for dark gray color. + */ + Convert3DColorsFilter(int white, int ltGray, int dkGray, int black) + { + newWhite = white; + newLtGray = ltGray; + newDkGray = dkGray; + newBlack = black; + } + + /** + * This is on the fly converter to change the 3D colors with one we passed on the filter + * construction. This method is calling when image drawing is happening. + * + * @param x + * The X pixel coordinate. + * @param y + * The Y pixel coordinate. + * @param pixel + * The ARGB pixel value of the original image. + * + * @return The new ARGB pixel value after transformation to render. + */ + public int filterRGB(int x, int y, int pixel) + { + return (pixel | COLOR_3D_WHITE) == COLOR_3D_WHITE ? newWhite : + (pixel | COLOR_3D_LT_GRAY) == COLOR_3D_LT_GRAY ? newLtGray : + (pixel | COLOR_3D_DK_GRAY) == COLOR_3D_DK_GRAY ? newDkGray : + (pixel | COLOR_3D_BLACK) == COLOR_3D_BLACK ? newBlack : pixel; + } + } + +} === added file 'src/com/goldencode/p2j/ui/client/gui/driver/ClientImageDrawHelper.java' --- src/com/goldencode/p2j/ui/client/gui/driver/ClientImageDrawHelper.java 1970-01-01 00:00:00 +0000 +++ src/com/goldencode/p2j/ui/client/gui/driver/ClientImageDrawHelper.java 2015-08-10 20:25:46 +0000 @@ -0,0 +1,34 @@ +/* +** Module : ClientImageDrawHelper.java +** Abstract : Defines a helper function for images processing. +** +** Copyright (c) 2015, Golden Code Development Corporation. +** ALL RIGHTS RESERVED. Use is subject to license terms. +** +** Golden Code Development Corporation +** CONFIDENTIAL +** +** -#- -I- --Date-- --------------------------------Description---------------------------------- +** 001 SBI 20150810 First version which defines a helper function for image processing. +*/ + +package com.goldencode.p2j.ui.client.gui.driver; + +/** + * Defines additional processing for images. + * + * @param + * The source image type. + */ +public interface ClientImageDrawHelper +{ + /** + * Do the implemented transformations on the target image. + * + * @param ps + * The holder for images parameters. + * + * @return The wrapped image with its with and its height. + */ + ImageWrapper processImage(PaintStructure ps); +} === added file 'src/com/goldencode/p2j/ui/client/gui/driver/VirtualScreen.java' --- src/com/goldencode/p2j/ui/client/gui/driver/VirtualScreen.java 1970-01-01 00:00:00 +0000 +++ src/com/goldencode/p2j/ui/client/gui/driver/VirtualScreen.java 2015-08-10 18:21:23 +0000 @@ -0,0 +1,64 @@ +/* +** Module : VirtualScreen.java +** Abstract : Define methods to work with screen +** +** Copyright (c) 2015, Golden Code Development Corporation. +** ALL RIGHTS RESERVED. Use is subject to license terms. +** +** Golden Code Development Corporation +** CONFIDENTIAL +** +** -#- -I- --Date-- --------------------------------Description---------------------------------- +** 001 SBI 20150810 First version which describes a virtual screen based on sRGB model. +*/ + +package com.goldencode.p2j.ui.client.gui.driver; + +import java.awt.image.DirectColorModel; + +/** + * VirtualScreen describes a virtual screen based on sRGB model. It provides an access + * to a screen pixel model and perform drawing operations. + */ +public interface VirtualScreen +{ + /** + * Returns RGBA model via pixels, each pixel is encoded as an integer. + * + * @return The array of pixels. + */ + int[] getPixels(); + + /** + * Gets a screen width. + * + * @return The screen width. + */ + int getScreenWidth(); + + /** + * Gets a screen height. + * + * @return The screen height + */ + int getScreenHeight(); + + /** + * Describes a pixel format used for drawing. + * + * @return The color model. + */ + DirectColorModel getColorModel(); + + /** + * Draw an image at the target position on the screen. + * + * @param image + * The target image. + * @param x + * The x-coordinate of the target image position. + * @param y + * The y-coordinate of the target image position. + */ + void drawImage(ImageWrapper image, int x, int y); +} === modified file 'src/com/goldencode/p2j/ui/client/gui/driver/swing/SwingEmulatedWindow.java' --- src/com/goldencode/p2j/ui/client/gui/driver/swing/SwingEmulatedWindow.java 2015-08-07 20:47:38 +0000 +++ src/com/goldencode/p2j/ui/client/gui/driver/swing/SwingEmulatedWindow.java 2015-08-10 20:31:10 +0000 @@ -53,7 +53,8 @@ ** 026 HC 20150612 Support for modal windows. ** 027 HC 20150702 Implemented window modality independently on the actual UI driver. Improved ** positioning of top-level windows on systems with multiple displays. -** 028 GES 20150715 Cleanup of window resources by calling quit() on the content pane. +** 028 GES 20150715 Cleanup of window resources by calling quit() on the content pane, +** moved the common image processing functionalities to the separate unit. */ package com.goldencode.p2j.ui.client.gui.driver.swing; @@ -143,6 +144,7 @@ /** Temporary context stored during paintComponent() operations. */ private Graphics graphics = null; + private final BufferedImageDrawHelper drawHelper; /** * Constructor. * @@ -265,6 +267,7 @@ new float[] {LineStroke.DOT_STEP_SMALL, LineStroke.DOT_STEP_SMALL}, LineStroke.DOT_STEP_SMALL); + drawHelper = new BufferedImageDrawHelper(); // TODO: disable the popup of the window actions (min/max/close/etc) via key? } @@ -761,93 +764,9 @@ g2.drawRoundRect(ps.x, ps.y, ps.width, ps.height, ps.arcDiameter, ps.arcDiameter); break; case DRAW_IMAGE: - BufferedImage img2draw = null; - int width = 0; - int height = 0; - // scalng factors for vertical and horizontal - float kHor = 0; - float kVer = 0; - if (ps.xOffset == -1 && ps.yOffset == -1) - { - // full original image - img2draw = ((ImageWrapper)ps.img).getImage(); - width = ps.width; - height = ps.height; - kHor = ((float)ps.width) / ((float) ps.img.getWidth()); - kVer = ((float)ps.height) / ((float) ps.img.getHeight()); - } - else - { - // need to recalc the width and height for subimage to cut - // to not go out of image borders - int widthToCut = ps.img.getWidth() - ps.xOffset; - if (widthToCut > ps.width) - { - widthToCut = ps.width; - } - int heightToCut = ps.img.getHeight() - ps.yOffset; - if (heightToCut > ps.height) - { - heightToCut = ps.height; - } - // partial image, get subimage - BufferedImage fullImg = ((ImageWrapper)ps.img).getImage(); - BufferedImage bimg = fullImg.getSubimage(ps.xOffset, - ps.yOffset, - widthToCut, - heightToCut); - - if (bimg != null) - { - img2draw = bimg; - width = widthToCut; - height = heightToCut; - kHor = ((float)ps.width) / ((float)widthToCut); - kVer = ((float)ps.height) / ((float)heightToCut); - } - } - // finally draw the image - if (img2draw != null) - { - // check binary image options - // 3D color conversions - if (ps.convert3D) - { - img2draw = get3DColorsConvertedImage((BufferedImage)img2draw); - } - // transparency - if (ps.transparent) - { - int pixTrans = img2draw.getRGB(0, height - 1); - img2draw = getTransparentImage(img2draw, pixTrans); - } - // resizing - if (ps.stretchToFit) - { - if (ps.retainShape) - { - // need to calculate native aspect ratio for image - // if we have two different scaling factors for x and y we choose only one - // to keep scaling in both directions, this must be one that is smaller - if (kHor < kVer) - { - width = ps.width; - height = Math.round(((float)height) * kHor); - } - else - { - width = Math.round(((float)width) * kVer); - height = ps.height; - } - } - else - { - width = ps.width; - height = ps.height; - } - } - // call Java backend - g2.drawImage(img2draw, ps.x, ps.y, width, height, pane); + ImageWrapper preparedImage = drawHelper.processImage(ps); + if (preparedImage.getImage() != null) { + g2.drawImage(preparedImage.getImage(), ps.x, ps.y, preparedImage.getWidth(), preparedImage.getHeight(), pane); } break; case FILL_RECT: @@ -1135,68 +1054,6 @@ } /** - * Converts the image to one having transparent color. - * - * @param img - * Initial image to convert. - * @param transPix - * The color to be considered as transparent. - * - * @return The image with one tarnsparent color. - */ - private BufferedImage getTransparentImage(BufferedImage img, int transPix) - { - // define custom image producer - ImageProducer ip = new FilteredImageSource(img.getSource(), - new TransparencyFilter(transPix)); - // convert image - return getBufferedImage(Toolkit.getDefaultToolkit().createImage(ip)); - } - - /** - * Converts the image replacing standard 3D colors with currently redefined. - * - * @param img - * Initial image to convert. - * - * @return The image with replaced 3D colors color. - */ - private BufferedImage get3DColorsConvertedImage(BufferedImage img) - { - // define custom image producer - ImageProducer ip = new FilteredImageSource(img.getSource(), - new Convert3DColorsFilter(SystemColor.controlLtHighlight.getRGB(), - SystemColor.control.getRGB(), - SystemColor.controlShadow.getRGB(), - SystemColor.controlText.getRGB())); - // convert image - return getBufferedImage(Toolkit.getDefaultToolkit().createImage(ip)); - } - - /** - * Converts the image into buffered image. - * - * @param imgToConvert - * Initial image to convert. - * - * @return The buffered image for further use. - */ - private BufferedImage getBufferedImage(java.awt.Image imgToConvert) - { - // create empty image - BufferedImage imgRes = new BufferedImage(imgToConvert.getWidth(null), - imgToConvert.getHeight(null), - BufferedImage.TYPE_INT_ARGB); - - // copy data to newly created image - Graphics2D g2img = imgRes.createGraphics(); - g2img.drawImage(imgToConvert, 0, 0, null); - g2img.dispose(); - - return imgRes; - } - - /** * Draw a string using the current font, which will be scaled so that the final string will * have the specified legacy width and height. *

@@ -1402,119 +1259,4 @@ highlightRestoreBuffer = null; } - - /** - * Custom RGB filter to change image transparency data on the fly. - */ - static class TransparencyFilter - extends RGBImageFilter - { - /** Alpha channel constant. */ - private static final int TRANSPARENCY_MASK = 0xFF000000; - - /** Pixel to use as transparent marker. */ - int rgbCheck; - - /** - * Creates new filter with the given transparent pixel value. - * - * @param pixTrans - * The pixel sample we will consider as transparent. - */ - TransparencyFilter(int pixTrans) - { - rgbCheck = pixTrans | TRANSPARENCY_MASK; - } - - /** - * This is on the fly converter to turn on transparency for every pixels the sames as - * one we defined as marker on the filter construction. This method is calling when image - * drawing is happening. - * - * @param x - * The X pixel coordinate. - * @param y - * The Y pixel coordinate. - * @param pixel - * The ARGB pixel value of the original image. - * - * @return The new ARGB pixel value after transformation to render. - */ - public int filterRGB(int x, int y, int pixel) - { - return (pixel | TRANSPARENCY_MASK) == rgbCheck ? pixel & ~TRANSPARENCY_MASK : pixel; - } - } - - /** - * Custom RGB filter to change image 3D colors on the fly. - */ - static class Convert3DColorsFilter - extends RGBImageFilter - { - /** Original 3D white color(255, 255, 255). */ - private static final int COLOR_3D_WHITE = 0x00FFFFFF; - - /** Original 3D light gray color(192, 192, 192). */ - private static final int COLOR_3D_LT_GRAY = 0x00C0C0C0; - - /** Original 3D dark gray color(128, 128, 128). */ - private static final int COLOR_3D_DK_GRAY = 0x00808080; - - /** Original 3D black color(0, 0, 0). */ - private static final int COLOR_3D_BLACK = 0x00000000; - - /** New value for 3D white. */ - int newWhite; - - /** New value for 3D light gray. */ - int newLtGray; - - /** New value for 3D dark gray. */ - int newDkGray; - - /** New value for 3D black. */ - int newBlack; - - /** - * Creates new filter with the given 3D colors to replace. - * - * @param white - * The new value for white color. - * @param ltGray - * The new value for light gray color. - * @param dkGray - * The new value for dark gray color. - * @param black - * The new value for dark gray color. - */ - Convert3DColorsFilter(int white, int ltGray, int dkGray, int black) - { - newWhite = white; - newLtGray = ltGray; - newDkGray = dkGray; - newBlack = black; - } - - /** - * This is on the fly converter to change the 3D colors with one we passed on the filter - * construction. This method is calling when image drawing is happening. - * - * @param x - * The X pixel coordinate. - * @param y - * The Y pixel coordinate. - * @param pixel - * The ARGB pixel value of the original image. - * - * @return The new ARGB pixel value after transformation to render. - */ - public int filterRGB(int x, int y, int pixel) - { - return (pixel | COLOR_3D_WHITE) == COLOR_3D_WHITE ? newWhite : - (pixel | COLOR_3D_LT_GRAY) == COLOR_3D_LT_GRAY ? newLtGray : - (pixel | COLOR_3D_DK_GRAY) == COLOR_3D_DK_GRAY ? newDkGray : - (pixel | COLOR_3D_BLACK) == COLOR_3D_BLACK ? newBlack : pixel; - } - } } === removed file 'src/com/goldencode/p2j/ui/client/gui/driver/web/DrawImageOp.java' --- src/com/goldencode/p2j/ui/client/gui/driver/web/DrawImageOp.java 2015-08-07 20:47:38 +0000 +++ src/com/goldencode/p2j/ui/client/gui/driver/web/DrawImageOp.java 1970-01-01 00:00:00 +0000 @@ -1,114 +0,0 @@ -/* -** Module : DrawImageOp.java -** Abstract : Defines a draw image command. -** -** Copyright (c) 2015, Golden Code Development Corporation. -** ALL RIGHTS RESERVED. Use is subject to license terms. -** -** Golden Code Development Corporation -** CONFIDENTIAL -** -** -#- -I- --Date-- --------------------------------Description---------------------------------- -** 001 SBI 20150807 First version which is a draw image command. -** -** -*/ - -package com.goldencode.p2j.ui.client.gui.driver.web; - -import java.awt.Image; -import java.util.HashSet; - -import com.goldencode.p2j.ui.client.gui.driver.PaintPrimitives; -import com.goldencode.p2j.ui.client.gui.driver.PaintStructure; - -/** - * - * DrawImageOp performs a draw image on a virtual screen and prepares - * a draw message to transfer via networks, holds a hash map of loaded images. - * - */ -public class DrawImageOp -{ - - private final VirtualScreen virtualScreen; - - private final HashSet loadedImages; - /** - * - * @param virtualScreen - */ - public DrawImageOp(VirtualScreen virtualScreen) - { - this.virtualScreen = virtualScreen; - this.loadedImages = new HashSet(); - } - - /** - * Write a 32-bit integer value into a binary message at a given offset. - * - * @param message - * Message content as an byte array. - * @param offset - * Offset into the message at which the integer should be written. - * @param value - * The 32-bit value to write. - */ - private void writeMessageInt32(byte[] message, int offset, int value) - { - message[offset] = (byte)((value >> 24) & 0x00FF); - message[offset + 1] = (byte)((value >> 16) & 0x00FF); - message[offset + 2] = (byte)((value >> 8) & 0x00FF); - message[offset + 3] = (byte)( value & 0x00FF); - } - - /** - * Perform a draw image on a virtual screen, the image parameters provided - * in PaintStructure. A binary message format is defined as - * DRAW_IMAGE, 1 byte, operation id - * x, 4 bytes, an image x-offset - * y, 4 bytes, an image y-offset - * width, 4 bytes, an image width - * height, 4 bytes, an image width - * encoding, 1 byte, an image encoding schema, Raw or Hash - * imageHashCode, 4 bytes, an image hash code - * image binaries, 4*width*height bytes, an encoded image, it is omitted - * if an encoding is Hash. - * @param ps PaintStructure parameters for an image drawing - * @return byte[] a drawing image prepared to transfer via network. - */ - public byte[] execute(PaintStructure ps) { - int x = ps.xOffset;//4 - int y = ps.yOffset;//4 - Image image = (Image) ps.img.getImage(); - int width = image.getWidth(null);//4 - int height = image.getHeight(null);//4 - virtualScreen.drawImage(image, x, y); - int imageHash = image.hashCode(); - ImageEncoding encoding; - if (!loadedImages.contains(imageHash)) { - loadedImages.add(imageHash); - encoding = ImageEncoding.RAW; - } else { - encoding = ImageEncoding.HASH; - } - final byte[] encodedImage = new RawEncoder().packToBinaries(virtualScreen, x, y, width, height); - int msgLen = 22; - if (encoding == ImageEncoding.RAW) { - msgLen += encodedImage.length; - } - byte[] drawImageMsg = new byte[msgLen]; - drawImageMsg[0] = (byte) (PaintPrimitives.DRAW_IMAGE.ordinal()); - writeMessageInt32(drawImageMsg, 1, x); - writeMessageInt32(drawImageMsg, 5, y); - writeMessageInt32(drawImageMsg, 9, width); - writeMessageInt32(drawImageMsg, 13, height); - drawImageMsg[17] = (byte) encoding.getValue(); - writeMessageInt32(drawImageMsg, 18, imageHash); - if (encoding == ImageEncoding.RAW) { - System.arraycopy(encodedImage, 0, drawImageMsg, 22, encodedImage.length); - } - - return drawImageMsg; - } -} === modified file 'src/com/goldencode/p2j/ui/client/gui/driver/web/GuiWebDriver.java' --- src/com/goldencode/p2j/ui/client/gui/driver/web/GuiWebDriver.java 2015-08-10 18:28:09 +0000 +++ src/com/goldencode/p2j/ui/client/gui/driver/web/GuiWebDriver.java 2015-08-10 20:40:35 +0000 @@ -13,13 +13,17 @@ ** 002 CA 20150520 Changed signature for createFont. ** 003 HC 20150612 Compatibility changes related to support of modal windows. ** 004 HC 20150702 Implemented window modality independently on the actual UI driver. -** 005 GES 20150701 Added GUI implementation for previously stubbed-out methods. +** 005 GES 20150701 Added GUI implementation for previously stubbed-out methods, +** implemented images drawing operation. ** 006 CA 20150808 Added GUI implementation for font and metrics-related APIs. */ package com.goldencode.p2j.ui.client.gui.driver.web; -//don't import the entire logging package as it has naming conflicts with jetty +// TODO: remove this, it is there to allow compilation +import java.awt.Font; +import java.util.HashSet; +// don't import the entire logging package as it has naming conflicts with jetty import java.util.logging.Level; import java.util.logging.Logger; @@ -73,6 +77,12 @@ /** Work-Area bounds. */ private NativeDimension workarea; + /** Virtual screen for drawing in a memory */ + private VirtualScreen virtualScreen; + + /** Images loaded by a client. */ + private final HashSet loadedImages; + /** A cache of font metrics objects, by their remote font ID. */ private Map fontMetricsCache = new HashMap<>(); @@ -97,6 +107,7 @@ }); this.config = config; + this.loadedImages = new HashSet(); } /** @@ -123,7 +134,8 @@ desktop = new NativeDimension(width, height); workarea = new NativeDimension(width, taskbar ? height - TASKBAR_HEIGHT : height); - + virtualScreen = new VirtualScreenImpl(width, height); + // calculate our resource base Resource cpres = EmbeddedWebServerImpl.calcResourceBase(GuiWebDriver.class, "res"); @@ -182,7 +194,20 @@ { return new NativeRectangle(new NativePoint(0, 0), desktop); } - + + /** + * + * @return + */ + public VirtualScreen getVirtualScreen() + { + return virtualScreen; + } + + public boolean addImage(int imageHash) { + return this.loadedImages.add(imageHash); + } + /** * Returns an application-usable display area. For example, a task bar is * not counted as the usable area and it is always at the bottom of the === modified file 'src/com/goldencode/p2j/ui/client/gui/driver/web/GuiWebEmulatedWindow.java' --- src/com/goldencode/p2j/ui/client/gui/driver/web/GuiWebEmulatedWindow.java 2015-08-10 18:28:09 +0000 +++ src/com/goldencode/p2j/ui/client/gui/driver/web/GuiWebEmulatedWindow.java 2015-08-10 20:42:13 +0000 @@ -12,8 +12,8 @@ ** 001 GES 20150504 First version which is just stubbed out. ** 002 HC 20150612 Compatibility changes related to support of paragraph layout. ** 003 HC 20150702 Improved positioning of top-level windows on systems with multiple displays. -** 004 GES 20150701 Added GUI implementation for previously stubbed-out methods. -** 005 SBI 20150807 Added an images drawing operation. +** 004 GES 20150701 Added GUI implementation for previously stubbed-out methods, +** implemented an images drawing operation. ** 006 CA 20150810 Implemented APIs and operations for font and text metrics, text drawing and ** font creation/selection. */ @@ -58,6 +58,8 @@ /** Locally cached title text. */ private String title = null; + private final BufferedImageDrawHelper drawHelper; + /** * Constructor. * @@ -76,6 +78,7 @@ this.lock = (lock != null) ? lock : new Object(); this.websock = websock; this.webdriver = webdriver; + this.drawHelper = new BufferedImageDrawHelper(); } /** @@ -346,7 +349,7 @@ { return websock.getParagraphHeight(text, font.font, maxWidth); } - } + } /** * Draw using a paint operation. @@ -379,7 +382,25 @@ websock.drawRoundRect(ps.x, ps.y, ps.width, ps.height, ps.arcDiameter); break; case DRAW_IMAGE: - websock.drawImage(ps); + ImageWrapper wrappedImage = drawHelper.processImage(ps); + VirtualScreen virtualScreen = webdriver.getVirtualScreen(); + int imageHash = wrappedImage.getImage().hashCode(); + ImageEncoding encoding; + byte[] encodedImage; + if (webdriver.addImage(imageHash)) + { + encoding = ImageEncoding.RAW; + virtualScreen.drawImage(wrappedImage, ps.x, ps.y); + encodedImage = new RawEncoder().packToBinaries(virtualScreen, ps.x, ps.y, + wrappedImage.getWidth(), wrappedImage.getHeight()); + } + else + { + encoding = ImageEncoding.HASH; + encodedImage = null; + } + websock.drawImage(ps.x, ps.y, wrappedImage.getWidth(), wrappedImage.getHeight(), + encoding, imageHash, encodedImage); break; case FILL_RECT: websock.fillRect(ps.x, ps.y, ps.width, ps.height); === modified file 'src/com/goldencode/p2j/ui/client/gui/driver/web/GuiWebSocket.java' --- src/com/goldencode/p2j/ui/client/gui/driver/web/GuiWebSocket.java 2015-08-10 18:28:09 +0000 +++ src/com/goldencode/p2j/ui/client/gui/driver/web/GuiWebSocket.java 2015-08-10 20:44:32 +0000 @@ -10,16 +10,13 @@ ** ** -#- -I- --Date-- --------------------------------Description---------------------------------- ** 001 GES 20150331 First version which is a placeholder using the common parent code as ChUI. -** 002 GES 20150701 Added messages for a first pass at GUI support. -** 003 SBI 20150807 Added an images drawing operation. -** 004 CA 20150810 Added operations for font, metrics and string drawing. +** 002 GES 20150701 Added messages for a first pass at GUI support, +** implemented an images drawing operation. +** 003 CA 20150810 Added operations for font, metrics and string drawing. */ package com.goldencode.p2j.ui.client.gui.driver.web; -import java.awt.color.ColorSpace; -import java.awt.image.DataBuffer; -import java.awt.image.DirectColorModel; import java.util.*; import com.goldencode.p2j.ui.client.driver.web.*; @@ -53,21 +50,6 @@ private int pendingBytes = 0; /** - * Defines a pixels model as sRGB 32bits per a pixel - */ - private final DirectColorModel colorModel = new DirectColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB), - 32, 0xFF, 0xFF00, 0xFF0000, 0xFF000000, true, DataBuffer.TYPE_INT); - /** - * TODO to get client screen width and height. - */ - private final VirtualScreen virtualScreen = new VirtualScreenImpl(colorModel, 800, 600); - - /** - * Image drawing manager holds a hash map of loaded images - */ - private final DrawImageOp drawImageOp = new DrawImageOp(virtualScreen); - - /** * A map with the received messages from java-script side. Requests which require a * "synchronous" approach must call {@link #waitForResult(int)} for the expected operation. */ @@ -284,11 +266,45 @@ } /** - * Draw a given image on the virtual screen and prepare it to transfer. - * @param imageParameters PaintStructure + * Draw a given image on the virtual screen and prepare it to transfer. + * + * @param x + * The x-coordinate of the image position. + * @param y + * The y-coordinate of the image position. + * @param width + * The width of the image. + * @param height + * The height of the image. + * @param encoding + * The encoding id. + * @param imageHash + * The image hash code, its own id. + * @param encodedImage + * The encoded image represented by bytes. */ - public void drawImage(PaintStructure imageParameters) { - addDrawingOp(drawImageOp.execute(imageParameters)); + public void drawImage(int x, int y, int width, int height, ImageEncoding encoding, + int imageHash, byte[] encodedImage) + { + int msgLen = 22; + if (encoding == ImageEncoding.RAW) + { + msgLen += encodedImage.length; + } + byte[] drawImageMsg = new byte[msgLen]; + drawImageMsg[0] = (byte) (PaintPrimitives.DRAW_IMAGE.ordinal()); + writeMessageInt32(drawImageMsg, 1, x); + writeMessageInt32(drawImageMsg, 5, y); + writeMessageInt32(drawImageMsg, 9, width); + writeMessageInt32(drawImageMsg, 13, height); + drawImageMsg[17] = (byte) encoding.getValue(); + writeMessageInt32(drawImageMsg, 18, imageHash); + if (encoding == ImageEncoding.RAW) + { + System.arraycopy(encodedImage, 0, drawImageMsg, 22, encodedImage.length); + } + + addDrawingOp(drawImageMsg); } /** @@ -1227,10 +1243,13 @@ } /** - * Add a prepared drawing to the pending operations list - * @param message byte[] prepared drawing to transfer + * Add a prepared drawing to the pending operations list. + * + * @param message + * The prepared drawing to transfer. */ - private void addDrawingOp(byte[] message) { + private void addDrawingOp(byte[] message) + { drawingOps.add(message); pendingBytes += message.length; } === modified file 'src/com/goldencode/p2j/ui/client/gui/driver/web/ImageEncoding.java' --- src/com/goldencode/p2j/ui/client/gui/driver/web/ImageEncoding.java 2015-08-07 20:47:38 +0000 +++ src/com/goldencode/p2j/ui/client/gui/driver/web/ImageEncoding.java 2015-08-10 18:26:19 +0000 @@ -9,7 +9,7 @@ ** CONFIDENTIAL ** ** -#- -I- --Date-- --------------------------------Description---------------------------------- -** 001 SBI 20150807 First version which describes a schema for Raw encoding and MD5 hash encoding +** 001 SBI 20150810 First version which describes a schema for Raw encoding and MD5 hash encoding ** that is used to transfer images md5 hashes via networks. ** */ @@ -17,10 +17,8 @@ package com.goldencode.p2j.ui.client.gui.driver.web; /** - * - * ImageEncoding defines Raw encoding and Hash encoding - * that is used to transfer images ids instead of their binaries via networks. - * + * ImageEncoding defines Raw encoding and Hash encoding that is used to transfer images ids + * instead of their binaries via networks. */ public enum ImageEncoding { @@ -35,7 +33,8 @@ /** * Returns an encoding well-known id. - * @return int an encoding id + * + * @return The encoding id. */ public int getValue() { === modified file 'src/com/goldencode/p2j/ui/client/gui/driver/web/PixelsEncoder.java' --- src/com/goldencode/p2j/ui/client/gui/driver/web/PixelsEncoder.java 2015-08-07 20:47:38 +0000 +++ src/com/goldencode/p2j/ui/client/gui/driver/web/PixelsEncoder.java 2015-08-10 19:34:35 +0000 @@ -9,27 +9,36 @@ ** CONFIDENTIAL ** ** -#- -I- --Date-- --------------------------------Description---------------------------------- -** 001 SBI 20150807 First version which describes a schema for encoding of a rectangle +** 001 SBI 20150810 First version which describes a schema for encoding of a rectangle ** on a provided virtual screen. */ package com.goldencode.p2j.ui.client.gui.driver.web; +import com.goldencode.p2j.ui.client.gui.driver.VirtualScreen; + /** - * - * PixelsEncoder - * + * PixelsEncoder describes an encoding method to pack a screen's rectangular area + * to binaries. */ public interface PixelsEncoder { /** * Encode the rectangular area on the screen to transfer. - * @param screen virtual screen with the defined color model - * @param x int a position of the left side of the target rectangle along the screen horizon - * @param y int a position of the upper side of the target rectangle along the screen's vertical - * @param w int a width of the target rectangular area - * @param h int a height of the target rectangular area - * @return + * + * @param screen + * The virtual screen with the defined color model. + * @param x + * The position of the left side of the target rectangle along the screen horizon. + * @param y + * The position of the upper side of the target rectangle along the screen's + * vertical. + * @param w + * The width of the target rectangular area. + * @param h + * The height of the target rectangular area. + * + * @return The encoded message contents. */ byte[] packToBinaries(VirtualScreen screen, int x, int y, int w, int h); } === modified file 'src/com/goldencode/p2j/ui/client/gui/driver/web/RawEncoder.java' --- src/com/goldencode/p2j/ui/client/gui/driver/web/RawEncoder.java 2015-08-07 20:47:38 +0000 +++ src/com/goldencode/p2j/ui/client/gui/driver/web/RawEncoder.java 2015-08-10 19:32:20 +0000 @@ -18,21 +18,39 @@ import java.awt.image.DirectColorModel; +import com.goldencode.p2j.ui.client.gui.driver.VirtualScreen; + /** - * * RawEncoder implements Raw encoding based on RFB specification documents: * https://www.realvnc.com/docs/rfbproto.pdf * https://tools.ietf.org/html/rfc6143 * This implementation supports only sRGB pixels models with 32 bits per a pixel. */ -class RawEncoder implements PixelsEncoder +class RawEncoder +implements PixelsEncoder { - + /** Defines a 32 bits per a pixel integer constant. */ private final static int TRUE_COLOR_BITS_PER_PIXEL = 32; - + + /** + * Encode the rectangular area on the screen to transfer. + * + * @param screen + * The virtual screen with the defined color model. + * @param x + * The position of the left side of the target rectangle along the screen horizon. + * @param y + * The position of the upper side of the target rectangle along the screen's + * vertical. + * @param w + * The width of the target rectangular area. + * @param h + * The height of the target rectangular area. + * + * @return The encoded message contents. + */ @Override - public byte[] packToBinaries(VirtualScreen screen, int x, int y, - int w, int h) + public byte[] packToBinaries(VirtualScreen screen, int x, int y, int w, int h) { byte[] encodedRectangle = null; int b = 0; @@ -45,11 +63,12 @@ int p = y * scanline + x; int[] pixels = screen.getPixels(); DirectColorModel colorModel = screen.getColorModel(); - if (colorModel.getPixelSize() == TRUE_COLOR_BITS_PER_PIXEL) { - encodedRectangle = new byte[ numberOfPixels << 2 ]; - for( ; i < numberOfPixels; i++, s++, p++ ) + if (colorModel.getPixelSize() == TRUE_COLOR_BITS_PER_PIXEL) + { + encodedRectangle = new byte[numberOfPixels << 2]; + for (; i < numberOfPixels; i++, s++, p++) { - if( s == w ) + if (s == w) { s = 0; p += jump; @@ -60,11 +79,12 @@ encodedRectangle[b++] = (byte) colorModel.getBlue(pixel); encodedRectangle[b++] = (byte) colorModel.getAlpha(pixel); } - } else { + } + else + { throw new UnsupportedOperationException("Supported only 32 bits per a pixel"); } - + return encodedRectangle; } - } === removed file 'src/com/goldencode/p2j/ui/client/gui/driver/web/VirtualScreen.java' --- src/com/goldencode/p2j/ui/client/gui/driver/web/VirtualScreen.java 2015-08-07 20:47:38 +0000 +++ src/com/goldencode/p2j/ui/client/gui/driver/web/VirtualScreen.java 1970-01-01 00:00:00 +0000 @@ -1,53 +0,0 @@ -/* -** Module : VirtualScreen.java -** Abstract : Define methods to work with screen -** -** Copyright (c) 2015, Golden Code Development Corporation. -** ALL RIGHTS RESERVED. Use is subject to license terms. -** -** Golden Code Development Corporation -** CONFIDENTIAL -** -** -#- -I- --Date-- --------------------------------Description---------------------------------- -** 001 SBI 20150807 First version which describes a virtual screen based on sRGB model. -*/ - -package com.goldencode.p2j.ui.client.gui.driver.web; - -import java.awt.Image; -import java.awt.image.DirectColorModel; - -/** - * VirtualScreen describes a virtual screen based on sRGB model. It provides - * an access to a screen pixel model and perform drawing operations. - */ -public interface VirtualScreen -{ - /** - * - * @return pixels int[] RGBA model via pixels - */ - int[] getPixels(); - /** - * Gets a screen width. - * @return int a screen width - */ - int getScreenWidth(); - /** - * Gets a screen height. - * @return height a screen height - */ - int getScreenHeight(); - /** - * Describes a pixel format used for drawing. - * @return DirectColorModel color model - */ - DirectColorModel getColorModel(); - /** - * Draw an image at the target position on the screen - * @param image Image - * @param x int x-coordinate of the target image position - * @param y int y-coordinate of the target image position - */ - void drawImage(Image image, int x, int y); -} === modified file 'src/com/goldencode/p2j/ui/client/gui/driver/web/VirtualScreenImpl.java' --- src/com/goldencode/p2j/ui/client/gui/driver/web/VirtualScreenImpl.java 2015-08-07 20:47:38 +0000 +++ src/com/goldencode/p2j/ui/client/gui/driver/web/VirtualScreenImpl.java 2015-08-10 19:50:52 +0000 @@ -9,7 +9,7 @@ ** CONFIDENTIAL ** ** -#- -I- --Date-- --------------------------------Description---------------------------------- -** 001 SBI 20150807 First version which implements a virtual screen based on sRGB model. +** 001 SBI 20150810 First version which implements a virtual screen based on sRGB model. */ package com.goldencode.p2j.ui.client.gui.driver.web; @@ -17,6 +17,7 @@ import java.awt.Color; import java.awt.Graphics2D; import java.awt.Image; +import java.awt.color.ColorSpace; import java.awt.image.BufferedImage; import java.awt.image.DataBuffer; import java.awt.image.DataBufferInt; @@ -26,74 +27,130 @@ import java.awt.image.SinglePixelPackedSampleModel; import java.awt.image.WritableRaster; +import com.goldencode.p2j.ui.client.gui.driver.ImageWrapper; +import com.goldencode.p2j.ui.client.gui.driver.VirtualScreen; + /** - * * VirtualScreenImpl is an implementation of VirtualScreen drawings operations. - * */ -class VirtualScreenImpl implements VirtualScreen +class VirtualScreenImpl +implements VirtualScreen { - + /** The color model that defines pixels representation */ private final DirectColorModel colorModel; + + /** The screen width. */ private final int width; + + /** The screen height. */ private final int height; + + /** The pixels array.*/ private final int[] pixels; + + /** The canvas in a memory to draw images. */ private final BufferedImage screen; - /** - * Constructs a virtual screen of the given width and height - * based on the given pixels model. - * - * @param colorModel DirectColorModel pixels model based on sRGB - * @param width int a screen width - * @param height int a screen height + + /** + * Constructs a virtual screen with the default color model model as sRGB 32bits per a pixel. + * + * @param width + * The screen width. + * @param height + * The screen height. + */ + public VirtualScreenImpl(int width, int height) + { + this(new DirectColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB), 32, 0xFF, 0xFF00, + 0xFF0000, 0xFF000000, true, DataBuffer.TYPE_INT), width, height); + } + + /** + * Constructs a virtual screen of the given width and height based on the given pixels model. + * + * @param colorModel + * The pixels model based on sRGB. + * @param width + * The screen width. + * @param height + * The screen height. */ public VirtualScreenImpl(DirectColorModel colorModel, int width, int height) { this.colorModel = colorModel; this.width = width; this.height = height; - this.pixels = new int[width*height]; - final DataBuffer data = new DataBufferInt(pixels, pixels.length); - final SampleModel sampleModel = new SinglePixelPackedSampleModel( - DataBuffer.TYPE_INT, width, height, colorModel.getMasks()); - final WritableRaster raster = Raster.createWritableRaster(sampleModel, data, null); + this.pixels = new int[width * height]; + DataBuffer data = new DataBufferInt(pixels, pixels.length); + SampleModel sampleModel = new SinglePixelPackedSampleModel(DataBuffer.TYPE_INT, + width, height, colorModel.getMasks()); + WritableRaster raster = Raster.createWritableRaster(sampleModel, data, null); this.screen = new BufferedImage(colorModel, raster, true, null); } + /** + * Returns RGBA model via pixels, each pixel is encoded as an integer. + * + * @return The array of pixels. + */ @Override public int[] getPixels() { return pixels; } + /** + * Gets a screen width. + * + * @return The screen width. + */ @Override public int getScreenWidth() { return width; } + /** + * Gets a screen height. + * + * @return The screen height + */ @Override public int getScreenHeight() { return height; } + /** + * Describes a pixel format used for drawing. + * + * @return The color model. + */ @Override public DirectColorModel getColorModel() { return colorModel; } + /** + * Draw an image at the target position on the screen. + * + * @param image + * The target image. + * @param x + * The x-coordinate of the target image position. + * @param y + * The y-coordinate of the target image position. + */ @Override - public void drawImage(Image image, int x, int y) + public void drawImage(ImageWrapper image, int x, int y) { Graphics2D g2 = screen.createGraphics(); g2.setColor(new Color(0,0,0)); g2.fillRect(0, 0, getScreenWidth(), getScreenHeight()); - g2.drawImage(image, x, y, null); + g2.drawImage((Image) image.getImage(), x, y, null); g2.dispose(); } - } === modified file 'src/com/goldencode/p2j/ui/client/gui/driver/web/res/p2j.screen.js' --- src/com/goldencode/p2j/ui/client/gui/driver/web/res/p2j.screen.js 2015-08-10 12:43:49 +0000 +++ src/com/goldencode/p2j/ui/client/gui/driver/web/res/p2j.screen.js 2015-08-10 20:35:16 +0000 @@ -13,8 +13,8 @@ ** Renamed objects to match the more generic web client approach (not just ** ChUI). Moved some logic to common code locations. ** 002 SBI 20150731 Applied "use strict" directive, fixed undeclared variables. -** 003 GES 20150720 Total rewrite to implement a first pass at GUI support. -** 004 SBI 20150807 Added a drawing image operation +** 003 GES 20150720 Total rewrite to implement a first pass at GUI support, +** added a drawing image operation. */ "use strict"; @@ -448,8 +448,6 @@ //console.debug("encoding type: " + encoding); var key = p2j.socket.readInt32BinaryMessage(message, idx + 18); console.debug("hash: " + key); - var img = ctx.createImageData(width, height); - var data = img.data; var pixelsInBytes = width * height * 4; var imgData; var imgDataOffset; @@ -461,14 +459,7 @@ imgDataOffset = idx + 22; loadedImages.set(key, message.subarray(imgDataOffset, imgDataOffset + pixelsInBytes)); } - for (var i = 0, j = imgDataOffset; i < pixelsInBytes; i += 4, j += 4) { - data[i] = imgData[j]; - data[i + 1] = imgData[j + 1]; - data[i + 2] = imgData[j + 2]; - data[i + 3] = imgData[j + 3]; - } - ctx.putImageData(img, x, y); - + drawImage(x, y, width, height, imgData, imgDataOffset); break; case ops.FILL_RECT: x = p2j.socket.readInt32BinaryMessage(message, idx + 1); @@ -764,7 +755,26 @@ drawLine(ctx, x + width, y + 1, x + width, y + height, color); drawLine(ctx, x + 1, y + height, x + width - 1, y + height, color); } - + + /** + * + */ + function drawImage(x, y, width, height, imgData, imgDataOffset) + { + var img = ctx.createImageData(width, height); + var data = img.data; + var pixelsInBytes = width * height * 4; + + for (var i = 0, j = imgDataOffset; i < pixelsInBytes; i += 4, j += 4) + { + data[i] = imgData[j]; + data[i + 1] = imgData[j + 1]; + data[i + 2] = imgData[j + 2]; + data[i + 3] = imgData[j + 3]; + } + ctx.putImageData(img, x, y); + } + /** * Convert a standard fillStyle or strokeStyle color into an array of [ R, G, B ] values. *