package domain; import java.awt.AlphaComposite; import java.awt.Color; import java.awt.Font; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.GraphicsConfiguration; import java.awt.GraphicsDevice; import java.awt.GraphicsEnvironment; import java.awt.Image; import java.awt.Polygon; import java.awt.Toolkit; import java.awt.Transparency; import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.Transferable; import java.awt.datatransfer.UnsupportedFlavorException; import java.awt.geom.AffineTransform; import java.awt.image.AffineTransformOp; import java.awt.image.BufferedImage; import java.awt.image.FilteredImageSource; import java.awt.image.ImageFilter; import java.awt.image.ImageProducer; import java.awt.image.RGBImageFilter; import java.awt.image.WritableRaster; import java.io.IOException; import java.net.URL; import javax.imageio.ImageIO; import core.VergeEngine; import persist.PCXReader; import static core.Script.*; public class VImage implements Transferable { public BufferedImage image; public Graphics2D g; public int width, height; public VImage(int x, int y) { this.width = x; this.height = y; GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); GraphicsDevice gs = ge.getDefaultScreenDevice(); GraphicsConfiguration gc = gs.getDefaultConfiguration(); image = gc.createCompatibleImage(x, y, Transparency.TRANSLUCENT); //image = new BufferedImage(x, y, BufferedImage.TYPE_INT_ARGB); g = (Graphics2D)image.getGraphics(); } public VImage(URL url, boolean transparent) { try { if(url==null) { System.err.println("Unable to find image from URL " + url); return; } if(url.getFile().toUpperCase().endsWith("PCX")) { image = PCXReader.loadImage(url.openStream()); } else { image = ImageIO.read(url); } } catch (IOException e) { System.err.println("Unable to read image from URL " + url); } this.width = image.getWidth(); this.height = image.getHeight(); // Make death magenta = transparent if(transparent) { Image img = makeColorTransparent(image, new Color(255, 0, 255)); this.image = imageToBufferedImage(img); } g = (Graphics2D)image.getGraphics(); } public VImage(URL url) { // Rafael: per default, all images are loaded as transparent this(url, true); } public BufferedImage getImage() { return this.image; } public int getWidth() { return this.width; } public int getHeight() { return this.height; } // See http://wiki.java.net/bin/view/Games/LoadingSpritesWithImageIO private static BufferedImage imageToBufferedImage(Image image) { GraphicsConfiguration gc = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice ().getDefaultConfiguration(); BufferedImage dst = gc.createCompatibleImage(image.getWidth(null), image.getHeight(null), Transparency.TRANSLUCENT); Graphics2D g2d = dst.createGraphics(); g2d.setComposite(AlphaComposite.Src); // Copy image g2d.drawImage(image,0,0,null); g2d.dispose(); return dst; } //http://stackoverflow.com/questions/665406/how-to-make-a-color-transparent-in-a-bufferedimage-and-save-as-png public static Image makeColorTransparent(BufferedImage im, final Color color) { ImageFilter filter = new RGBImageFilter() { // the color we are looking for... Alpha bits are set to opaque public int markerRGB = color.getRGB() | 0xFF000000; public final int filterRGB(int x, int y, int rgb) { if ((rgb | 0xFF000000) == markerRGB) { // Mark the alpha bits as zero - transparent return 0x00FFFFFF & rgb; } else { // nothing to do return rgb; } } }; ImageProducer ip = new FilteredImageSource(im.getSource(), filter); return Toolkit.getDefaultToolkit().createImage(ip); } // Fast copy of a BufferedImage // http://stackoverflow.com/questions/2825837/java-how-to-do-fast-copy-of-a-bufferedimages-pixels-unit-test-included public static void copySrcIntoDstAt(final BufferedImage src, final BufferedImage dst, final int dx, final int dy) { int[] srcbuf = ((java.awt.image.DataBufferInt) src.getRaster().getDataBuffer()).getData(); int[] dstbuf = ((java.awt.image.DataBufferInt) dst.getRaster().getDataBuffer()).getData(); int width = src.getWidth(); int height = src.getHeight(); int dstoffs = dx + dy * dst.getWidth(); int srcoffs = 0; for (int y = 0 ; y < height ; y++ , dstoffs+= dst.getWidth(), srcoffs += width ) { System.arraycopy(srcbuf, srcoffs , dstbuf, dstoffs, width); } } // Transfer to Clipboard // http://elliotth.blogspot.com/2005/09/copying-images-to-clipboard-with-java.html public void copyImageToClipboard() { //VImage imageSelection = new VImage(image.getWidth(null), image.getHeight(null)); //imageSelection.image = (BufferedImage) image; Toolkit toolkit = Toolkit.getDefaultToolkit(); toolkit.getSystemClipboard().setContents(this, null); } public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException { if (flavor.equals(DataFlavor.imageFlavor) == false) { throw new UnsupportedFlavorException(flavor); } return image; } public boolean isDataFlavorSupported(DataFlavor flavor) { return flavor.equals(DataFlavor.imageFlavor); } public DataFlavor[] getTransferDataFlavors() { return new DataFlavor[] { DataFlavor.imageFlavor }; } // TODO Create function drawRoundRect, similar to Graphics // Render the map and the entities to this VImage public void render() { VergeEngine.TimedProcessEntities(); VergeEngine.RenderMap(this); } //VI.f. Graphics Functions /*static void AdditiveBlit(int x, int y, int src, int dst) { image *s = ImageForHandle(src); image *d = ImageForHandle(dst); AdditiveBlit(x, y, s, d); }*/ public void alphablit(int x, int y, VImage src, VImage alpha) { // [Rafael, the Esper] TODO Implement //AlphaBlit(x, y, s, a, d); //error("Non implemented function"); this.tblit(x, y, src); } public void blitentityframe(int x, int y, int e, int f) { if (current_map==null || e<0 || e >= numentities) return; entity.get(e).chr.render(x, y, f, this); } public void blitentityframe(int x, int y, CHR chr, int f) { if (current_map==null) return; chr.render(x, y, f, this); } // Overkill (2007-08-25): src and dest were backwards. Whoops! public void blitlucent(int x, int y, int lucent, VImage src) { int oldalpha = currentLucent; setlucent(lucent); this.blit(x, y, src); setlucent(oldalpha); } public void blitTile(int x, int y, int t) { if (current_map != null) { current_map.getTileSet().UpdateAnimations(); current_map.getTileSet().Blit(x, y, t, this); } } public void blit(int x, int y, VImage src) { this.blit(x, y, src.image); } public void blit(int x, int y, Image src) { // [Rafael, the Esper] Always opaque if(currentLucent < 255) { Graphics2D g2d = (Graphics2D) getImage().getGraphics(); g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, (float)(currentLucent)/255)); g2d.drawImage(src, x, y, Color.BLACK, null); } else { this.g.drawImage(src, x, y, Color.BLACK, null); } } public void tblit(int x, int y, VImage src) { this.tblit(x, y, src.image); } public void tblit(int x, int y, Image src) { if(currentLucent < 255) { Graphics2D g2d = (Graphics2D) getImage().getGraphics(); g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, (float)(currentLucent)/255)); g2d.drawImage(src, x, y, null); } else { this.g.drawImage(src,x,y,null); } } /*static void BlitWrap(int x, int y, int src, int dst) { image *s = ImageForHandle(src); image *d = ImageForHandle(dst); BlitWrap(x, y, s, d); }*/ public VImage duplicateImage() { // TODO Test VImage img = new VImage(this.image.getWidth(), this.image.getHeight()); img.g.drawImage(this.getImage(), 0, 0, null); return img; } public enum FlipType{FLIP_HORIZONTALLY, FLIP_VERTICALLY, FLIP_BOTH}; public void flipBlit(int x, int y, FlipType type, VImage src) { /* AffineTransform tx = AffineTransform.getScaleInstance(-1, 1); tx.translate(-src.width, 0); AffineTransformOp op = new AffineTransformOp(tx, AffineTransformOp.TYPE_NEAREST_NEIGHBOR); VImage flippedImage = this.duplicateimage(); //BufferedImage flippedImage = new BufferedImage(src.width,src.height, BufferedImage.TYPE_INT_RGB); //flippedImage = op.filter(src.image, null); flippedImage.image = op.filter(flippedImage.image, null); //blit(x, y, flippedImage, dest.image); this.blit(x,y,flippedImage);*/ if(type == FlipType.FLIP_HORIZONTALLY) { this.g.drawImage(src.image, src.getWidth()+x, y, -src.getWidth(), src.getHeight(), null); //this.blit(x, y, flipimage(0,0,src.image)); } else { System.err.println("Not supported yet!"); } } public static BufferedImage flipImage(int x, int y, BufferedImage src) { BufferedImage flippedImage = new BufferedImage(src.getWidth(), src.getHeight(), BufferedImage.TYPE_INT_ARGB); for(int j=0;j<src.getHeight(); j++) for(int i=0;i<src.getWidth(); i++) flippedImage.setRGB(i, j, src.getRGB(src.getWidth()-i-1, j)); // Flip horizontally //flippedImage.setRGB(i, j, src.getRGB(src.getWidth()-i-1, src.getHeight()-j-1)); // Flip Both return flippedImage; } /*static int GetImageFromClipboard() { image *t = clipboard_getImage(); if (!t) return 0; else return HandleForImage(t); }*/ /*public static int GetPixel(int x, int y, VImage src) { WritableRaster wr = src.image.getRaster(); wr.getPixel(x, y, arg2); return ReadPixel(x, y, s); }*/ /*static void GrabRegion(int sx1, int sy1, int sx2, int sy2, int dx, int dy, int src, int dst) { image *s = ImageForHandle(src); image *d = ImageForHandle(dst); int dcx1, dcy1, dcx2, dcy2; d.GetClip(dcx1, dcy1, dcx2, dcy2); if (sx1>sx2) SWAP(sx1, sx2); if (sy1>sy2) SWAP(sy1, sy2); int grabwidth = sx2 - sx1; int grabheight = sy2 - sy1; if (dx+grabwidth<0 || dy+grabheight<0) return; d.SetClip(dx, dy, dx+grabwidth, dy+grabheight); Blit(dx-sx1, dy-sy1, s, d); d.SetClip(dcx1, dcy1, dcx2, dcy2); }*/ /** This extremely powerful function will allow you to take an image, define a rectangle * within it, and get an image handle referencing that rectangle within the original image. * xofs, yofs, width, height indicate the position and dimensions of the rectangle within * the source image. Clipping rectangles for the two images are completely independent. * Rendering into one will render into the other.* If that is what you want to do, then this * is the function you want to use. */ public VImage imageShell(int x, int y, int w, int h) { if (w+x > this.width || y+h > this.height) System.err.printf( "ImageShell() - Bad arguments. x/y+w/h greater than original image dimensions\n\nx:%d,w:%d (%d),y:%d,h:%d (%d), orig_x:%d, orig_y:%d", x,w,x+w,y,h,y+h,this.width,this.height ); VImage dst = new VImage(w, h); //dst.delete_data(); //dst.shell = true; //dst.data = ((quad *)src.data + (y*src.pitch)+x); //dst.pitch = src.pitch; // TODO Implement this mechanism! error("Non implemented function: imageshell"); return dst; } public void line(int x1, int y1, int x2, int y2, Color c) { // [Rafael, the Esper] this.g.setColor(new Color(c.getRed(), c.getGreen(), c.getBlue(), currentLucent)); this.g.drawLine(x1, y1, x2, y2); } /*static void Mosaic(int xgran, int ygran, int dst) { image *dest = ImageForHandle(dst); Mosaic(xgran, ygran, dest); }*/ public void rotscale(int x, int y, int angle, int scale, VImage src) { //TODO [Rafael, the Esper] Implement this.blit(x, y, src); //RotScale(x, y, angle*(float)3.14159/(float)180.0, scale/(float)1000.0, s, d); } public void scaleblit(int x, int y, int dw, int dh, VImage src) { //ScaleBlit(x, y, dw, dh, s, d); //this.blit(x, y, src); // TODO [Rafael, the Esper] Implement scaling this.g.drawImage(src.getImage(), x, y, x+dw, y+dh, 0, 0, src.getWidth(), src.getHeight(), null); } /* Draws a scaled image. A bit more complex than the other blitters to use. * The x,y values give the upper-left corner of where the blit will start. * iw,ih are the width and height of the *source* image. * dw, dh are the width and height that the image should appear on screen. * (ie, the end result bounding box of the image would be, x, y, x+dw, y+dh) * Image is, as with the other blit routines, a pointer to the image graphic. */ public void scalesprite(int x, int y, int iw, int ih, int dw, int dh) { screen.g.drawImage(this.getImage(), x, y, x+dw, y+dh, 0, 0, iw, ih, null); } public void setClip(int x1, int y1, int x2, int y2) { //img.SetClip(x1, y1, x2, y2); // TODO [Rafael, the Esper] Implement this mechanism in VImage //error("Non implemented function: setclip"); } public void silhouette(int x, int y, Color c, VImage src) { int x1,x2,y1,y2; //WritableRaster wr = dst.getImage().getRaster(); x1 = y1 = 0; x2 = src.width; y2 = src.height; for (int j=y1; j<y2; j++) { for(int i=x1;i<x2;i++) { if(src.getImage().getRGB(i, j)==transcolor || src.getImage().getRGB(i, j)==0) // black this.setPixel(x+i, y+j, new Color(0,0,0,0)); else this.setPixel(x+i, y+j, c); } } } /* static void SubtractiveBlit(int x, int y, int src, int dst) { image *s = ImageForHandle(src); image *d = ImageForHandle(dst); SubtractiveBlit(x, y, s, d); } static void TAdditiveBlit(int x, int y, int src ,int dst) { image *s = ImageForHandle(src); image *d = ImageForHandle(dst); TAdditiveBlit(x, y, s, d); }*/ public void grabRegion(int sx1, int sy1, int sx2, int sy2, int dx, int dy, VImage src) { this.grabRegion(sx1, sy1, sx2, sy2, dx, dy, src.image); } public void grabRegion(int sx1, int sy1, int sx2, int sy2, int dx, int dy, BufferedImage src) { // Getclip //int dcx1 = dst.cx1; //int dcy1 = dst.cy1; //int dcx2 = dst.cx2; //int dcy2 = dst.cy2; if (sx1>sx2) { // swap sx1, sx2 int temp = sx1; sx1 = sx2; sx2 = temp; } if (sy1>sy2) { // swap sy1, sy2 int temp = sy1; sy1 = sy2; sy2 = temp; } Color color = null; for(int j=0; j<sy2-sy1; j++) for(int i=0; i<sx2-sx1; i++) { if(sx1+i >= src.getWidth() || sy1+j >= src.getHeight() || dx+i >= this.getWidth() || dy+j >= this.getHeight()) break; color = new Color(src.getRGB(sx1+i, sy1+j)); if(color.getRed() + color.getGreen() + color.getBlue() == 0) // TODO [Rafael, the Esper] Probably move it to tgrabregion? color = new Color(0,0,0,0); //color.getRed(), color.getGreen(), color.getBlue(), 0); this.setPixel(i+dx, j+dy, color); } /*int grabwidth = sx2 - sx1; int grabheight = sy2 - sy1; if (dx+grabwidth<0 || dy+grabheight<0) return; dst.SetClip(dx, dy, dx+grabwidth, dy+grabheight); Blit(dx-sx1, dy-sy1, src, dst); dst.SetClip(dcx1, dcy1, dcx2, dcy2);*/ } public void tgrabRegion(int sx1, int sy1, int sx2, int sy2, int dx, int dy, Color transC, VImage src) { this.tgrabRegion(sx1, sy1, sx2, sy2, dx, dy, transC, src.image); } public void tgrabRegion(int sx1, int sy1, int sx2, int sy2, int dx, int dy, Color transC, BufferedImage src) { if (sx1>sx2) { // swap sx1, sx2 int temp = sx1; sx1 = sx2; sx2 = temp; } if (sy1>sy2) { // swap sy1, sy2 int temp = sy1; sy1 = sy2; sy2 = temp; } Color color = null; for(int j=0; j<sy2-sy1; j++) for(int i=0; i<sx2-sx1; i++) { if(sx1+i >= src.getWidth() || sy1+j >= src.getHeight() || dx+i >= this.getWidth() || dy+j >= this.getHeight()) break; color = new Color(src.getRGB(sx1+i, sy1+j)); if(color.equals(transC)) color = new Color(255,0,255,0); //color.getRed(), color.getGreen(), color.getBlue(), 0); this.setPixel(i+dx, j+dy, color); } } public void setPixel(int x, int y, Color color) { this.image.setRGB(x, y, color.getRGB()); } public int readPixel(int x, int y) { if(this.image != null) { return this.image.getRGB(x, y); } return 0; } public void changeColor(Color src, Color dest) { for(int y=0;y<height;y++) { for(int x=0;x<width;x++) { if(readPixel(x, y) == src.getRGB()) { setPixel(x, y, dest); } } } } // Overkill (2007-08-25): src and dest were backwards. Whoops! public void tblitLucent(int x, int y, int lucent, VImage src) { int oldalpha = currentLucent; setlucent(lucent); this.tblit(x, y, src); setlucent(oldalpha); } public void tblitTile(int x, int y, int t) { if (current_map!=null) current_map.getTileSet().TBlit(x, y, t, this); } /* static void TGrabRegion(int sx1, int sy1, int sx2, int sy2, int dx, int dy, int src, int dst) { image *s = ImageForHandle(src); image *d = ImageForHandle(dst); int dcx1, dcy1, dcx2, dcy2; d.GetClip(dcx1, dcy1, dcx2, dcy2); if (sx1>sx2) SWAP(sx1, sx2); if (sy1>sy2) SWAP(sy1, sy2); int grabwidth = sx2 - sx1; int grabheight = sy2 - sy1; if (dx+grabwidth<0 || dy+grabheight<0) return; d.SetClip(dx, dy, dx+grabwidth, dy+grabheight); TBlit(dx-sx1, dy-sy1, s, d); d.SetClip(dcx1, dcy1, dcx2, dcy2); }*/ public void rect(int x1, int y1, int x2, int y2, int c) { this.rect(x1, y1, x2, y2, palette.getColor(c, currentLucent)); } public void rect(int x1, int y1, int x2, int y2, Color c) { // [Rafael, the Esper] this.g.setColor(new Color(c.getRed(), c.getGreen(), c.getBlue(), currentLucent)); if(x1>x2) { int temp = x1; x1 = x2; x2 = temp; } // swap x1,x2 if(y1>y2) { int temp = y1; y1 = y2; y2 = temp; } // swap y1,y2 this.g.drawRect(x1, y1, x2-x1, y2-y1); } public void rectfill(int x1, int y1, int x2, int y2, int c) { if(c==transcolor) { Graphics2D g2d = (Graphics2D) this.getImage().getGraphics(); g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.CLEAR, 0.0f)); g2d.setColor(new Color(0, 0, 0, 0)); g2d.fillRect(x1, y1, x2, y2); g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER)); } else this.rectfill(x1, y1, x2, y2, palette.getColor(c, currentLucent)); } public void rectfill(int x1, int y1, int x2, int y2, Color c) { // [Rafael, the Esper] if(c.getAlpha()==255) c = new Color(c.getRed(), c.getGreen(), c.getBlue(), currentLucent); this.g.setColor(c); if(x1>x2) { int temp = x1; x1 = x2; x2 = temp; } // swap x1,x2 if(y1>y2) { int temp = y1; y1 = y2; y2 = temp; } // swap y1,y2 this.g.fillRect(x1, y1, x2-x1, y2-y1); } // Note: different from Java fillOval, the circle is centered in (x1, y1) public void circle(int x1, int y1, int xr, int yr, Color c, VImage dst) { // [Rafael, the Esper] dst.g.setColor(new Color(c.getRed(), c.getGreen(), c.getBlue(), currentLucent)); dst.g.drawOval(x1-xr, y1-yr, xr*2, yr*2); } public void circlefill(int x1, int y1, int xr, int yr, int c) { if(c==transcolor) { Graphics2D g2d = (Graphics2D) this.getImage().getGraphics(); g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.CLEAR, 0.0f)); g2d.setColor(new Color(0, 0, 0, 0)); g2d.fillOval(x1-xr, y1-yr, xr*2, yr*2); g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER)); } else { circlefill(x1, y1, xr, yr, palette.getColor(c, currentLucent)); } } public void circlefill(int x1, int y1, int xr, int yr, Color c) { // [Rafael, the Esper] if(c.getAlpha()==255) c = new Color(c.getRed(), c.getGreen(), c.getBlue(), currentLucent); this.g.setColor(c); this.g.fillOval(x1-xr, y1-yr, xr*2, yr*2); } // Note: it's a filled triangle. A non-filled triangle can be draw with lines. public void triangle(int x1, int y1, int x2, int y2, int x3, int y3, Color c) { // [Rafael, the Esper] Polygon p = new Polygon(); p.addPoint(x1, y1); p.addPoint(x2, y2); p.addPoint(x3, y3); this.g.setColor(c); this.g.fillPolygon(p); } public void tscaleblit(int x, int y, int dw, int dh, VImage src) { //TScaleBlit(x, y, dw, dh, s, d); this.tblit(x, y, src); // TODO [Rafael, the Esper] Implement scaling }/* static void TSubtractiveBlit(int x, int y, int src, int dst) { image *s = ImageForHandle(src); image *d = ImageForHandle(dst); TSubtractiveBlit(x, y, s, d); }*/ public void twrapBlit(int x, int y, VImage src) { // TODO [Rafael, the Esper] Implement //TWrapBlit(x, y, s, d); error("Non implemented function: twrapblit"); } public void wrapBlit(int x, int y, VImage src) { // TODO [Rafael, the Esper] Implement //WrapBlit(x, y, s, d); error("Non implemented function: wrapblit"); } public void printString(int x, int y, Font font, String text) { this.g.setFont(font); this.g.setColor(Color.WHITE); this.g.drawString(text, x, y); } // Fade functions public void fadeOut(int delay, boolean rendermap) { timer = 0; while (timer<delay) { if(rendermap) this.render(); setlucent(100 - (timer*100/delay)); this.paintBlack(); setlucent(0); showpage(); } } public void fadeIn(int delay, boolean rendermap) { timer = 0; while (timer<delay) { if(rendermap) this.render(); setlucent(timer*100/delay); this.paintBlack(); setlucent(0); showpage(); } } // Handy code by [Rafael, the Esper] public void fade(int delay, boolean black) { // fade in and out if(black) { fadeOut(delay, true); this.paintBlack(); fadeIn(delay, false); } else { fadeOut(delay, false); fadeIn(delay, true); } } public void paintBlack() { this.rectfill(0, 0, this.width, this.height, Color.BLACK); } }