import java.awt.*; import java.awt.image.*; import java.awt.image.PixelGrabber; import java.awt.image.MemoryImageSource; import java.applet.*; import Globals.*; import Approximations.*; /* TODO Questions marked as do1 and do2 Make GUI to enter parameters in one place */ public class Editor extends Applet implements Runnable{ //=============================================== //Special sprite: rings: //----------------------------------------------- int speRadius = -1; //Flag. -1 disables spe. int speRingWidth = 4; int speFrequency = 5; int speDensity = 4096; int speColorMaxR = 234; int speColorMaxG = 244; int speColorMaxB = 250; int speColorMinR = 157; int speColorMinG = 185; int speColorMinB = 199; //Derivative: int speAR = speColorMaxR - speColorMinR; int speAG = speColorMaxG - speColorMinG; int speAB = speColorMaxB - speColorMinB; //----------------------------------------------- //Special sprite: rings: //=============================================== //Control parameters: static final int TRANSPARENT_COLOR = 0; //"Yes" means ... dimensions of both images //are equal, and they are coisided: boolean spriteFitsBase; int animationFramesNumber; int rotationCenterOnBaseX; int rotationCenterOnBaseY; int leftTopOfSpriteOnBaseX; int leftTopOfSpriteOnBaseY; Image baseIm; Image spriteIm; //topIm should have have transparent parts //or transparent color or both: Image topIm; Image noAnimationIm; //Auxiliary: MediaTracker mt; Graphics baseG; Graphics spriteG; PixelGrabber spritePG; PixelGrabber basePG; PixelGrabber topPG; int[] spriteArr; int[] baseArr; int[] baseArrBuff; int[] topArr; Image baseWithSprite; Image resultIm; Graphics resultG; Image[][] resultArr; Image spriteImNew; Graphics spriteGNew; String baseFName; String spriteFName; String topFName; String noAnimationFName; int columnNumber; int rowNumber; int resultX; int resultY; int baseW; int baseH; int spriteW; int spriteH; int topW; //Redundant int topH; //Redundant //Auxiliary members: private static boolean imageIsReady; private static int frameNumber = 0; private static Thread calculateThread; private static long timeToPrintNewFrame; private static long timeLeftAfterCalculations; Dimension scrDimension; private static long frameDelay = 200; boolean checkPassed = false; public void init() { //Control parameters: checkPassed = true; spriteFitsBase = true; columnNumber = 4; rowNumber = 4; String ws = getParameter("columnNumber"); try { if (ws != null ) columnNumber = Integer.parseInt(ws); } catch (Exception e) { checkPassed = false; GS.c(e.toString()); } ws = getParameter("rowNumber"); try { if (ws != null ) rowNumber = Integer.parseInt(ws); } catch (Exception e) { checkPassed = false; GS.c(e.toString()); } ws = getParameter("frameDelay"); try { if (ws != null ) frameDelay = (int) (Integer.parseInt(ws)); } catch (Exception e) { checkPassed = false; GS.c(e.toString()); } baseFName = getParameter("baseFName"); spriteFName = getParameter("spriteFName"); topFName = getParameter("topFName"); noAnimationFName = getParameter("noAnimationFName"); animationFramesNumber = columnNumber*rowNumber - 2; resultX = -1; //Flag. scrDimension = getSize(); imageIsReady = false; frameNumber = 0; //Principals: mt = new MediaTracker(this); baseIm = getImage( getCodeBase(), "Resources/" + baseFName ); spriteIm = getImage( getCodeBase(), "Resources/" + spriteFName ); topIm = getImage( getCodeBase(), "Resources/" + topFName ); noAnimationIm = getImage( getCodeBase(), "Resources/" + noAnimationFName ); mt.addImage( baseIm, 0 ); mt.addImage( spriteIm, 1 ); mt.addImage( topIm, 2 ); mt.addImage( noAnimationIm, 3 ); //do2 Why mt does not load without these?: mt.statusID(0, true); mt.statusID(1, true); mt.statusID(2, true); mt.statusID(3, true); //do1 infinite loops: try { while( !mt.checkAll() ) { GS.con("Still downloading images ... " + "base=" + trackerStatus(mt,0) + " sprite=" + trackerStatus(mt,1) ); Thread.sleep(1000); } while( baseIm.getWidth(this) <=0 || baseIm.getHeight(this) <=0 || spriteIm.getWidth(this) <=0 || spriteIm.getHeight(this) <=0 || topIm.getWidth(this) <=0 || topIm.getHeight(this) <=0 ) { GS.con("Still negative dimensions ... "); Thread.sleep(1000); } baseW =baseIm.getWidth(this); baseH =baseIm.getHeight(this); spriteW=baseIm.getWidth(this); spriteH=baseIm.getHeight(this); topW =topIm.getWidth(this); //Redundant topH =topIm.getHeight(this); //Redundant //Do control: rotationCenterOnBaseX = 20; //baseW/2; rotationCenterOnBaseY = 19; //baseH/2; leftTopOfSpriteOnBaseX = 0; leftTopOfSpriteOnBaseY = 0; if( spriteFitsBase ) { leftTopOfSpriteOnBaseX = 0; leftTopOfSpriteOnBaseY = 0; } } catch (Exception e) { e.printStackTrace(); } if( mt.isErrorAny() ) { GS.con("Problems with some images ... "); //int st = mt.statusID(0, false); } else { GS.con( "All images downloaded successfully." ); //do2: Strangerly: these calls "shakes" dimensions and //enables them later: //GS.con( "Width=" + baseIm.getWidth(this) + // " Height=" + baseIm.getHeight(this) ); resultX = -2; //Success. } //=============================================== //Special sprite: rings: //----------------------------------------------- ws = getParameter("speRadius"); try { if (ws != null ) speRadius = (int) (Integer.parseInt(ws)); } catch (Exception e) { checkPassed = false; GS.c(e.toString()); } GS.c("speRadius" + speRadius); ws = getParameter("speRingWidth"); try { if (ws != null ) speRingWidth = (int) (Integer.parseInt(ws)); } catch (Exception e) { checkPassed = false; GS.c(e.toString()); } ws = getParameter("speFrequency"); try { if (ws != null ) speFrequency = (int) (Integer.parseInt(ws)); } catch (Exception e) { checkPassed = false; GS.c(e.toString()); } ws = getParameter("speDensity"); try { if (ws != null ) speDensity = (int) (Integer.parseInt(ws)); } catch (Exception e) { checkPassed = false; GS.c(e.toString()); } ws = getParameter("speColorMaxR"); try { if (ws != null ) speColorMaxR = (int) (Integer.parseInt(ws)); } catch (Exception e) { checkPassed = false; GS.c(e.toString()); } ws = getParameter("speColorMaxG"); try { if (ws != null ) speColorMaxG = (int) (Integer.parseInt(ws)); } catch (Exception e) { checkPassed = false; GS.c(e.toString()); } ws = getParameter("speColorMaxB"); try { if (ws != null ) speColorMaxB = (int) (Integer.parseInt(ws)); } catch (Exception e) { checkPassed = false; GS.c(e.toString()); } ws = getParameter("speColorMinR"); try { if (ws != null ) speColorMinR = (int) (Integer.parseInt(ws)); } catch (Exception e) { checkPassed = false; GS.c(e.toString()); } ws = getParameter("speColorMinG"); try { if (ws != null ) speColorMinG = (int) (Integer.parseInt(ws)); } catch (Exception e) { checkPassed = false; GS.c(e.toString()); } ws = getParameter("speColorMinB"); try { if (ws != null ) speColorMinB = (int) (Integer.parseInt(ws)); } catch (Exception e) { checkPassed = false; GS.c(e.toString()); } //----------------------------------------------- //Special sprite: rings: //=============================================== ICos.generate(); if( !checkPassed ) GS.c("Check not passed"); GS.con("Init finished"); } //init() private String trackerStatus( MediaTracker mt, int index ) { String s = ""; int st = mt.statusID(index, false); if( 0 == st ) { s = "NOT STARTED"; return s; } if( (st & MediaTracker.ABORTED) !=0 ) s = s + "ABORTED "; if( (st & MediaTracker.COMPLETE) !=0 ) s = s + "COMPLETE "; if( (st & MediaTracker.ERRORED) !=0 ) s = s + "ERRORED "; if( (st & MediaTracker.LOADING) !=0 ) s = s + "LOADING "; return s; } public void start() { timeToPrintNewFrame = System.currentTimeMillis(); if( calculateThread == null ) { calculateThread = new Thread(this); calculateThread.setName("Calc"); calculateThread.start(); GS.con( calculateThread.getName() + " initiated." ); } } public void run() { try { while( Thread.currentThread() == calculateThread && checkPassed ) { timeLeftAfterCalculations = timeToPrintNewFrame - System.currentTimeMillis(); repaint(); Thread.sleep(Math.max(0, timeLeftAfterCalculations)); frameNumber++; //GS.con( "Finished sleeping..." ); timeToPrintNewFrame = System.currentTimeMillis() + frameDelay; if( resultX == -2 ) { resultX = columnNumber * baseW; resultY = rowNumber * baseH; GS.con ("w=" + resultX + " h=" + resultY); resultIm = createImage(resultX,resultY); resultG = resultIm.getGraphics(); GS.con("Result image graphics created."); resultArr = new Image[columnNumber][rowNumber]; spriteArr = new int[spriteW*spriteH]; baseArr = new int[baseW*baseH]; baseArrBuff = new int[baseW*baseH]; topArr = new int[topW*topH]; //if( -1 != speRadius ) makeSpriteCircle(); spritePG = new PixelGrabber(spriteIm, 0, 0, spriteW, spriteH, spriteArr, 0, spriteW); basePG = new PixelGrabber(baseIm, 0, 0, baseW, baseH, baseArr, 0, baseW ); //For case if transparent color does not work for top: topPG = new PixelGrabber(topIm, 0, 0, topW, topH, topArr, 0, topW ); /* PixelGrabber(Image img, int x, int y, int w, int h, int[] pix, int off, int scansize) */ try { spritePG.grabPixels(); basePG.grabPixels(); topPG.grabPixels(); } catch (InterruptedException e) { GS.con("Faile grab pixels: " + e); this.stop(); } GS.con("Pixels grabbed successfully ... "); this.stop(); calculateImage(); //Debug: //this.stop(); } } } catch (InterruptedException e) { GS.con( "In method run" + e ); } } //run() public void update( Graphics g ) { if( imageIsReady && resultIm != null ) { //Move result image to master image: g.drawImage( resultIm, 0,0, null ); g.drawImage( spriteIm, resultX, 0, null ); g.drawImage( baseWithSprite, resultX, baseH, null ); int wi = frameNumber % animationFramesNumber; int animationI = wi % columnNumber; int animationJ = (wi - animationI )/columnNumber; //Put animated image last: g.drawImage(resultArr[animationI][animationJ], resultX, baseH*2, null ); /* This works: (perhaps choppy for too many clips): g.drawImage( resultIm, //=Source //Target: resultX, baseH*2, resultX+baseW-1, baseH*3 -1, //Source: animationI*baseW, animationJ*baseH, animationI*baseW+baseW-1, animationJ*baseH+baseH-1, null //=ImageObserver ); */ //Debug: //g.drawString( "Frame=" + frameNumber + // "wi=" + wi + " i=" + animationI + // " j=" + animationJ, 0, resultY/2 ); //Debug: //calculateThread.stop(); } else { g.setColor( new Color(0) ); g.drawString( "Image is not ready yet", 0,0 ); } paint(g); } void calculateImage() { //Debug resultG.setColor(new Color( (255<<16) | (155<<8) | 255 ) ); resultG.fillRect(0,0,resultX-1,resultY-1); resultG.setColor( new Color( 0, 0, 0 ) ); resultG.drawString("Image Created", 3, 3 ); for( int i=0; i<columnNumber; i++ ) { for( int j=0; j<rowNumber; j++ ) { //Was bug?: //resultG.drawImage( baseIm, i*baseW, j*baseH, null ); //if( 1 == i && 1 == j ) { switch( speRadius ) { case -1: drawRotatedSprite( rotationCenterOnBaseX, rotationCenterOnBaseY, leftTopOfSpriteOnBaseX, leftTopOfSpriteOnBaseY, ICos.argMax4/3, animationFramesNumber, j*columnNumber + i ); break; default: makeFullClipWithCircle( i, j ); } resultG.drawImage( baseWithSprite, i*baseW, j*baseH, null ); //topIm should have have transparent parts: //resultG.drawImage( topIm, i*baseW, j*baseH, null ); //} } } //Debug for cos, sin: //ICos.drawTest( resultG, resultY ); //Add static images at the end: resultG.drawImage( noAnimationIm, (columnNumber-2)*baseW, (rowNumber-1)*baseH, null ); GS.con( "Mutiframe image created." ); //Fill array to demonstrate animation in on screen: //Of course, one can assign baseWithSprite to //array's element, but to verify resultIm, we prefer //to cut the piece from resultIm for( int i=0; i<columnNumber; i++ ) { for( int j=0; j<rowNumber; j++ ) { //Loogs too clumsy. Is there any way to crop //piece immediately from the picture-of-clips?: CropImageFilter cif = new CropImageFilter( i*baseW, j*baseH, baseW, baseH ); FilteredImageSource fis = new FilteredImageSource( resultIm.getSource(), cif ); resultArr[i][j] = createImage(fis); } } GS.c("Array of result clips created."); imageIsReady = true; } public void drawRotatedSprite( int circleCenterX, int circleCenterY, int spritePosX, int spritePosY, int startAngle, int framesNumber, int currentFrame ) { int argI = (ICos.argMax4*currentFrame)/framesNumber; if(argI>ICos.argMax4) argI = ICos.argMax4; int cosI = ICos.cosI[argI]; int sinI = ICos.sinI[argI]; int sinMax = ICos.sinMax; int spRelPosX = spritePosX - circleCenterX; int spRelPosY = -(spritePosY- circleCenterY); //Normal, not screen coord. //GS.b("Copying to buffer.."); for(int i=0; i<baseW; i++ ) { //do1 use copyMem for(int j=0; j<baseH; j++ ) { int ix = j*baseW + i; baseArrBuff[ix] = baseArr[ix]; } } //GS.m("rotating.."); for(int i=0; i<spriteW; i++) { int fullX = i + spRelPosX; for(int j=0; j<spriteH; j++ ) { int color = spriteArr[j*baseW + i]; if( (color & 0xFFFFFF ) != TRANSPARENT_COLOR ) { int fullY = spRelPosY-j; int xS = (fullX*cosI + fullY*sinI)/sinMax + circleCenterX; int yS = circleCenterY -((-fullX*sinI) + fullY*cosI)/sinMax; if( xS < baseW && xS > -1 && yS < baseH && yS > -1 ) { int ix = yS*baseW + xS; baseArrBuff[ix] = color; } } } } /* //Strangerly fails: //GS.m("draw circle.."); int speRadius = 19; for( int ang=0; ang<ICos.argMax4; ang+=ICos.argMax4/100 ) { int color = 255 * ang / ICos.argMax4; if(argI>ICos.argMax4) argI = ICos.argMax4; int ang2 = ( ang + argI ) % ICos.argMax4; int xS = speRadius * ICos.cosI[ang2] / ICos.sinMax + circleCenterX; int yS = - (speRadius * ICos.sinI[ang2] / ICos.sinMax) + circleCenterY; if( xS < baseW && xS > -1 && yS < baseH && yS > -1 ) { int ix = yS*baseW + xS; baseArrBuff[ix] = color; } } */ //GS.m("rotated."); GS.b( "topping.." ); if( topIm != null && topArr != null ) { GS.m("Put top clip with count of transparent color .."); for(int j=0; j<topH; j++ ) { int wj = j*topW; for(int i=0; i<topW; i++) { int wji = wj + i; int color = topArr[wji]; if( (color & 0xFFFFFF ) != TRANSPARENT_COLOR ) { baseArrBuff[wji] = color; } } } } GS.e("topped."); //this.stop(); baseWithSprite= createImage( new MemoryImageSource( baseW, baseH, baseArrBuff, 0, baseW)); //GS.con("Created from arr to graph. ... "); } //drawRotatedSprite() void makeSpriteCircle() { spriteImNew = createImage( spriteW, spriteH ); spriteGNew = spriteImNew.getGraphics(); drawSpriteCircle ( spriteGNew, 0, 0 ); } void drawSpriteCircle( Graphics g, int i, int j ) { int currentFrame = i+j*columnNumber; int argI = (ICos.argMax4*currentFrame)/animationFramesNumber; if(argI>ICos.argMax4) argI = ICos.argMax4; g.setColor(new Color( 0 ) ); g.fillRect(0,0,spriteW,spriteH); int angStep = ICos.argMax4 / speDensity; if( angStep < 1 ) angStep = 1; int xSlast=0; int ySlast=0; for( int rWidth=0; rWidth<speRingWidth; rWidth++ ) { for( int ang=0; ang<ICos.argMax4; ang+=angStep ) { int angEffective = ICos.sinI[( (ang+argI)*speFrequency ) % ICos.argMax4] + ICos.sinMax; int cR = ( speAR * angEffective / ICos.sinMax2 + speColorMinR ) & 0xFF; int cG = ( speAG * angEffective / ICos.sinMax2 + speColorMinG ) & 0xFF; int cB = ( speAB * angEffective / ICos.sinMax2 + speColorMinB ) & 0xFF; int color = ( cR<<16) | (cG<<8) | cB; int r = speRadius - rWidth; int xS = r * ICos.cosI[ang] / ICos.sinMax + rotationCenterOnBaseX; int yS = - (r * ICos.sinI[ang] / ICos.sinMax) + rotationCenterOnBaseY; if( 0 == ang ) { xSlast = xS; ySlast = yS; } if( xS < spriteW && xS > -1 && yS < spriteH && yS > -1 ) { g.setColor( new Color(color) ); g.drawLine( xS,yS, xS,yS ); } xSlast = xS; ySlast = yS; } //for( int ang=0; } // rWidth; spriteIm = spriteImNew; } //makeSpriteCircle() void makeFullClipWithCircle( int ii, int jj ) { //No need. Just put paint. //GS.b("Copying to buffer.."); Image bf = createImage( baseW, baseH ); Graphics g = bf.getGraphics(); //do2 must move image memory, not grab it: for(int i=0; i<baseW; i++ ) { //do1 use copyMem for(int j=0; j<baseH; j++ ) { int ix = j*baseW + i; baseArrBuff[ix] = baseArr[ix]; } } Image bf2 = createImage( new MemoryImageSource( baseW, baseH, baseArrBuff, 0, baseW)); g.drawImage( bf2, 0, 0, null ); //Base is transferred. drawSpriteCircle(g, ii, jj); /* int[] bfTopping = new int[baseW*baseH]; GS.b( "topping.." ); if( topIm != null && topArr != null ) { //GS.m("Put top clip with count of transparent color .."); for(int j=0; j<topH; j++ ) { int wj = j*topW; for(int i=0; i<topW; i++) { int wji = wj + i; int color = topArr[wji]; if( (color & 0xFFFFFF ) != TRANSPARENT_COLOR ) { bfTopping[wji] = color; } else { //The rest is transparent: bfTopping[wji] = color | 0x255<<24 ; } } } } //if( topIm != null && Image bf3 = createImage( new MemoryImageSource( baseW, baseH, bfTopping, 0, baseW)); //Assert: transparent c. works: g.drawImage( bf3, 0, 0, null ); */ g.drawImage( topIm, 0, 0, null ); GS.e("topped."); } //makeFullClipWithCircle() } // class