=== modified file 'src/com/goldencode/p2j/ui/client/gui/driver/BufferedImageDrawHelper.java' --- src/com/goldencode/p2j/ui/client/gui/driver/BufferedImageDrawHelper.java 2015-08-13 14:51:06 +0000 +++ src/com/goldencode/p2j/ui/client/gui/driver/BufferedImageDrawHelper.java 2015-10-11 20:59:55 +0000 @@ -31,7 +31,7 @@ * @param ps * The holder for images parameters. * - * @return The wrapped image with its with and its height. + * @return The wrapped image with its width and its height. */ @Override public ImageWrapper processImage(PaintStructure ps) @@ -127,6 +127,78 @@ } /** + * Calculate the transformed image bounds. + * + * @param ps + * The holder for images parameters. + * + * @return The 2-elements array filled with new transformed image width and its new height. + */ + @Override + public Integer[] calculateImageBounds(PaintStructure ps) + { + 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) + { + 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; + } + + width = widthToCut; + height = heightToCut; + kHor = ((float)ps.width) / ((float)widthToCut); + kVer = ((float)ps.height) / ((float)heightToCut); + } + // 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 Integer[] {width, height}; + } + + /** * Converts the image replacing standard 3D colors with currently redefined. * * @param img === modified file 'src/com/goldencode/p2j/ui/client/gui/driver/ClientImageDrawHelper.java' --- src/com/goldencode/p2j/ui/client/gui/driver/ClientImageDrawHelper.java 2015-08-13 14:51:06 +0000 +++ src/com/goldencode/p2j/ui/client/gui/driver/ClientImageDrawHelper.java 2015-10-11 20:34:44 +0000 @@ -14,6 +14,8 @@ package com.goldencode.p2j.ui.client.gui.driver; +import java.awt.image.BufferedImage; + /** * Defines additional processing for images. * @@ -28,7 +30,17 @@ * @param ps * The holder for images parameters. * - * @return The wrapped image with its with and its height. + * @return The wrapped image with its width and its height. */ public ImageWrapper processImage(PaintStructure ps); + + /** + * Calculate the transformed image bounds. + * + * @param ps + * The holder for images parameters. + * + * @return The 2-elements array filled with new transformed image width and its new height. + */ + public Integer[] calculateImageBounds(PaintStructure ps); } === added file 'src/com/goldencode/p2j/ui/client/gui/driver/EmbeddedImageStructure.java' --- src/com/goldencode/p2j/ui/client/gui/driver/EmbeddedImageStructure.java 1970-01-01 00:00:00 +0000 +++ src/com/goldencode/p2j/ui/client/gui/driver/EmbeddedImageStructure.java 2015-10-11 21:00:55 +0000 @@ -0,0 +1,171 @@ +/* +** Module : EmbeddedImageStructure.java +** Abstract : A structure used to store images drawing parameters. +** +** Copyright (c) 2014-2015, Golden Code Development Corporation. +** ALL RIGHTS RESERVED. Use is subject to license terms. +** +** Golden Code Development Corporation +** CONFIDENTIAL +** +** -#- -I- --Date-- ---------------------------------Description--------------------------------- +** 001 SBI 20151011 First version. +**/ + +package com.goldencode.p2j.ui.client.gui.driver; + +import java.util.Arrays; +import java.util.List; + + +/** + * Encapsulates the image drawing parameters. + */ +public class EmbeddedImageStructure +{ + /** convert 3d image bit mask */ + static final int CONVERT_3D_MASK = 1; + + /** transparent image bit mask */ + static final int TRANSPARENT_MASK = 2; + + /** stretch to fit image bit mask */ + static final int STRETCH_TO_FIT_MASK = 4; + + /** retain shape image bit mask */ + static final int RETAIN_SHAPE_MASK = 8; + + + /** Width parameter */ + private final int width; + + /** Height parameter */ + private final int height; + + /** X offset to start drawing image. */ + private final int xOffset; + + /** Y offset to start drawing image. */ + private final int yOffset; + + /** Image to draw */ + private final ImageWrapper img; + + /** The bits mask that encodes image conversions. */ + private final int imageConversionMask; + + /** + * @param paintStructure + */ + EmbeddedImageStructure(PaintStructure paintStructure) + { + this.width = paintStructure.width; + this.height = paintStructure.height; + this.xOffset = paintStructure.xOffset; + this.yOffset = paintStructure.yOffset; + this.img = paintStructure.img; + this.imageConversionMask = encodeImageConversionMask(paintStructure); + } + + /** + * Encodes image conversions: 3d, transparent, stretch, retain. + * + * @return The bits mask that encodes image conversions. + */ + private static byte encodeImageConversionMask(PaintStructure paintStructure) + { + byte mask = 0; + if (paintStructure.convert3D) + { + mask |= CONVERT_3D_MASK; + } + if (paintStructure.transparent) + { + mask |= TRANSPARENT_MASK; + } + if (paintStructure.stretchToFit) + { + mask |= STRETCH_TO_FIT_MASK; + } + if (paintStructure.retainShape) + { + mask |= RETAIN_SHAPE_MASK; + } + + return mask; + } + + /** + * Returns the image canvas width. + * + * @return The width. + */ + public int getWidth() + { + return this.width; + } + + /** + * Returns the image canvas height. + * + * @return The canvas height. + */ + public int getHeight() + { + return this.height; + } + + /** + * The image drawing offset along x-axis. + * + * @return The xOffset + */ + public int getxOffset() + { + return this.xOffset; + } + + /** + * The image drawing offset along y-axis. + * + * @return The yOffset. + */ + public int getyOffset() + { + return this.yOffset; + } + + /** + * Returns the wrapped image to draw. + * + * @return The wrapped image. + */ + public ImageWrapper getImage() + { + return this.img; + } + + /** + * Encodes image conversions: 3d, transparent, stretch, retain. + * + * @return The bits mask that encodes image conversions. + */ + public int getImageConversionMask() + { + return this.imageConversionMask; + } + + /** + * Returns the embedded image identity 6-tuple. + * + * @return The array of 6 integer to represent the embedded image identity. + */ + public List getObjectSeal() + { + return Arrays.asList( + new Integer [] { + this.xOffset, this.yOffset, this.width, this.height, + this.imageConversionMask, this.img.getUniqueId() + }); + } +} === modified file 'src/com/goldencode/p2j/ui/client/gui/driver/ImageWrapper.java' --- src/com/goldencode/p2j/ui/client/gui/driver/ImageWrapper.java 2015-05-18 20:48:28 +0000 +++ src/com/goldencode/p2j/ui/client/gui/driver/ImageWrapper.java 2015-10-11 18:43:32 +0000 @@ -14,11 +14,30 @@ package com.goldencode.p2j.ui.client.gui.driver; +import java.util.concurrent.atomic.AtomicInteger; + /** * Contains the state for a native image which can be accessed in a general purpose manner. */ public class ImageWrapper { + /** Represents a images sequence that holds the next free sequence id. */ + private final static AtomicInteger IMAGES_SEQUENCE = new AtomicInteger(1); + + + /** + * Returns the next unique image id within this class loader. + * + * @return The next unigue image id. + */ + private static final int getImageUniqueId() + { + return IMAGES_SEQUENCE.getAndIncrement(); + } + + /** The image unique id.*/ + private final int uniqueId; + /** Contained native image. */ private I image = null; @@ -40,11 +59,22 @@ */ public ImageWrapper(I image, int height, int width) { + this.uniqueId = getImageUniqueId(); this.image = image; this.height = height; this.width = width; } - + + /** + * Returns the image unique id. + * + * @return The image unique id. + */ + public int getUniqueId() + { + return uniqueId; + } + /** * Obtain the contained native image instance. * === modified file 'src/com/goldencode/p2j/ui/client/gui/driver/PaintStructure.java' --- src/com/goldencode/p2j/ui/client/gui/driver/PaintStructure.java 2015-09-16 16:53:54 +0000 +++ src/com/goldencode/p2j/ui/client/gui/driver/PaintStructure.java 2015-10-11 20:46:32 +0000 @@ -26,7 +26,6 @@ import com.goldencode.p2j.ui.*; import com.goldencode.p2j.ui.client.*; -import com.goldencode.util.*; /** * A structure used to store paint parameters. Depending on paint operation different fields are @@ -119,4 +118,14 @@ { this.id = id; } + + /** + * Return the embedded image structure filled with the required parameters to draw the target. + * + * @return The embedded image structure. + */ + public EmbeddedImageStructure getEmbeddedImageStructure() + { + return new EmbeddedImageStructure(this); + } } === 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-10-09 19:15:11 +0000 +++ src/com/goldencode/p2j/ui/client/gui/driver/web/GuiWebDriver.java 2015-10-11 21:05:48 +0000 @@ -101,12 +101,18 @@ /** Virtual screen for drawing in a memory */ private VirtualScreen virtualScreen; - - /** Image usages is a mapping of an image loaded by a client to its owner windows. */ - private final Map > imageUsages; - - /** Owned images is a mapping of a window to its owned images. */ - private final Map > ownedImages; + + /** Maps the image seal to the unique image identity. */ + private final Map, Integer > imageSeals; + + /** + * Image usages is a mapping of an image loaded by a client to its owner windows. + * The loaded image is represented by its image seal 6-tuple integer array. + **/ + private final Map, Set > imageUsages; + + /** Owned images is a mapping of a window to its owned images seals. */ + private final Map> > ownedImages; /** A cache of font metrics objects, by their remote font ID. */ private Map fontMetricsCache = new HashMap<>(); @@ -150,8 +156,9 @@ }); this.config = config; - this.imageUsages = new HashMap>(); - this.ownedImages = new HashMap>(); + this.imageUsages = new HashMap, Set>(); + this.ownedImages = new HashMap>>(); + this.imageSeals = new HashMap, Integer>(); new RepaintThread().start(); } @@ -271,7 +278,7 @@ * * @return True iff it is the first usage of the given image. */ - public boolean addImageUsage(int imageHash, int windowId) + public boolean addImageUsage(List imageHash, int windowId) { boolean newLoadedImage = false; @@ -284,10 +291,10 @@ } usages.add(windowId); - Set images = ownedImages.get(windowId); + Set> images = ownedImages.get(windowId); if (images == null) { - images = new HashSet(); + images = new HashSet>(); ownedImages.put(windowId, images); } images.add(imageHash); @@ -306,18 +313,19 @@ public Set removeUnusedImages(int windowId) { Set imagesToRemove = new HashSet(); - Set images = ownedImages.remove(windowId); + Set> images = ownedImages.remove(windowId); if (images != null && !images.isEmpty()) { - for (Integer imageHash : images) + for (List imageSeal : images) { - Set usages = imageUsages.get(imageHash); + Set usages = imageUsages.get(imageSeal); if (usages.remove(windowId)) { if (usages.isEmpty()) { - imagesToRemove.add(imageHash); - imageUsages.remove(imageHash); + Integer imageUniqueId = imageSeals.remove(imageSeal); + imagesToRemove.add(imageUniqueId); + imageUsages.remove(imageSeal); } } } @@ -327,6 +335,32 @@ } /** + * Maps the image identity array to the unique image id. + * + * @param imageSeal + * The image identity array. + * @param uniqueId + * The image unique id. + */ + public void mapSealToUniqueId(List imageSeal, Integer uniqueId) + { + imageSeals.put(imageSeal, uniqueId); + } + + /** + * Gets the unique image id by its identity array. + * + * @param imageSeal + * The image identity array. + * + * @return The image unique id. + */ + public Integer getUniqueIdForSeal(List imageSeal) + { + return imageSeals.get(imageSeal); + } + + /** * 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 * virtual screen (when present). === 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-09-29 14:43:06 +0000 +++ src/com/goldencode/p2j/ui/client/gui/driver/web/GuiWebEmulatedWindow.java 2015-10-11 20:45:05 +0000 @@ -320,13 +320,14 @@ break; case DRAW_IMAGE: { - ImageWrapper wrappedImage = drawHelper.processImage(ps); - Object[] imageEncodedPacket = encodeImage(ps.x, ps.y, wrappedImage); - int imageHash = (Integer) imageEncodedPacket[0]; + Object[] imageEncodedPacket = encodeImage(ps); + int imageId = (Integer) imageEncodedPacket[0]; ImageEncoding encoding = (ImageEncoding) imageEncodedPacket[1]; byte[] encodedImage = (byte[]) imageEncodedPacket[2]; - websock.drawImage(ps.x, ps.y, wrappedImage.getWidth(), wrappedImage.getHeight(), - encoding, imageHash, encodedImage); + Integer imageWidth = (Integer) imageEncodedPacket[3]; + Integer imageHeight = (Integer) imageEncodedPacket[4]; + websock.drawImage(ps.x, ps.y, imageWidth, imageHeight, + encoding, imageId, encodedImage); } break; case FILL_RECT: @@ -417,13 +418,14 @@ break; case SET_ICON: { - ImageWrapper wrappedImage = drawHelper.processImage(ps); - Object[] imageEncodedPacket = encodeImage(ps.x, ps.y, wrappedImage); - int imageHash = (Integer) imageEncodedPacket[0]; + Object[] imageEncodedPacket = encodeImage(ps); + int imageId = (Integer) imageEncodedPacket[0]; ImageEncoding encoding = (ImageEncoding) imageEncodedPacket[1]; byte[] encodedImage = (byte[]) imageEncodedPacket[2]; - websock.setIconImage(wrappedImage.getWidth(), wrappedImage.getHeight(), encoding, - imageHash, encodedImage); + Integer imageWidth = (Integer) imageEncodedPacket[3]; + Integer imageHeight = (Integer) imageEncodedPacket[4]; + websock.setIconImage(imageWidth, imageHeight, encoding, + imageId, encodedImage); } break; default: @@ -436,37 +438,49 @@ * the image hash in its first element, the image encoding in its second element * and the encoded image or null in its third element depending on its loaded state. * - * @param x - * The x-coordinate of the target image position. - * @param y - * The y-coordinate of the target image position. - * @param wrappedImage - * The wrapped image. + * @param ps + * The paint structure filled with the target image parameters. * - * @return The 3-elements array that holds the image hash in its first element, - * the image encoding in its second element and the bytes array of the encoded image - * or null in its third element. + * @return The 5-elements array that holds the image unique id in its first element, the image + * encoding in its second element, the bytes array of the encoded image or null + * in its third element, the width and the height of the embedded image in its forth + * element and its fifth element respectively. */ - private final Object[] encodeImage(int x, int y, ImageWrapper wrappedImage) + private final Object[] encodeImage(PaintStructure ps) { + EmbeddedImageStructure embeddedImageStructure = ps.getEmbeddedImageStructure(); + List imageSeal = embeddedImageStructure.getObjectSeal(); VirtualScreen virtualScreen = webdriver.getVirtualScreen(); - int imageHash = wrappedImage.getImage().hashCode(); ImageEncoding encoding; byte[] encodedImage; - if (webdriver.addImageUsage(imageHash, windowId)) + Integer imageWidth; + Integer imageHeight; + Integer imageId; + if (webdriver.addImageUsage(imageSeal, windowId)) { + int x = ps.x; + int y = ps.y; + ImageWrapper embeddedImage = drawHelper.processImage(ps); + imageId = embeddedImage.getUniqueId(); + webdriver.mapSealToUniqueId(imageSeal, imageId); + imageWidth = embeddedImage.getWidth(); + imageHeight = embeddedImage.getHeight(); encoding = ImageEncoding.RAW; - virtualScreen.drawImage(wrappedImage, x, y); + virtualScreen.drawImage(embeddedImage, x, y); encodedImage = new RawEncoder().packToBinaries(virtualScreen, x, y, - wrappedImage.getWidth(), wrappedImage.getHeight()); + imageWidth, imageHeight); } else { + imageId = webdriver.getUniqueIdForSeal(imageSeal); encoding = ImageEncoding.HASH; encodedImage = null; + Integer[] imageBounds = drawHelper.calculateImageBounds(ps); + imageWidth = imageBounds[0]; + imageHeight = imageBounds[1]; } - return new Object[] {imageHash, encoding, encodedImage}; + return new Object[] {imageId, encoding, encodedImage, imageWidth, imageHeight}; } /**