diff --git a/src/itdelatrisu/opsu/GameData.java b/src/itdelatrisu/opsu/GameData.java index 6c80ead5..97495730 100644 --- a/src/itdelatrisu/opsu/GameData.java +++ b/src/itdelatrisu/opsu/GameData.java @@ -97,7 +97,6 @@ public class GameData { return menuImage; Image img = getSmallImage(); - img = img.getScaledCopy((GameImage.MENU_BUTTON_BG.getImage().getHeight() * 0.45f) / img.getHeight()); if (!small.hasSkinImage()) // save default image only this.menuImage = img; return img; @@ -457,6 +456,36 @@ public class GameData { } } } + /** + * Draws a string of scoreSymbols. + * @param str the string to draw + * @param x the starting x coordinate + * @param y the y coordinate + * @param scale the scale to apply + * @param fixedsize the width to use for all symbols + * @param rightAlign align right (true) or left (false) + */ + private void drawFixedSizeSymbolString(String str, int x, int y, float scale, float fixedsize, boolean rightAlign) { + char[] c = str.toCharArray(); + int cx = x; + if (rightAlign) { + for (int i = c.length - 1; i >= 0; i--) { + Image digit = getScoreSymbolImage(c[i]); + if (scale != 1.0f) + digit = digit.getScaledCopy(scale); + cx -= fixedsize; + digit.draw(cx + (fixedsize-digit.getWidth())/2, y); + } + } else { + for (int i = 0; i < c.length; i++) { + Image digit = getScoreSymbolImage(c[i]); + if (scale != 1.0f) + digit = digit.getScaledCopy(scale); + digit.draw(cx + (fixedsize-digit.getWidth())/2, y); + cx += fixedsize; + } + } + } /** * Draws game elements: @@ -470,28 +499,28 @@ public class GameData { int marginX = (int) (width * 0.008f); // score - drawSymbolString((scoreDisplay < 100000000) ? String.format("%08d", scoreDisplay) : Long.toString(scoreDisplay), - width - marginX, 0, 1.0f, true); + drawFixedSizeSymbolString((scoreDisplay < 100000000) ? String.format("%08d", scoreDisplay) : Long.toString(scoreDisplay), + width - marginX, 0, 1.0f, getScoreSymbolImage('0').getWidth()-2, true); // score percentage int symbolHeight = getScoreSymbolImage('0').getHeight(); float scorePercent = getScorePercent(); drawSymbolString( String.format((scorePercent < 10f) ? "0%.2f%%" : "%.2f%%", scorePercent), - width - marginX, symbolHeight, 0.75f, true + width - marginX, symbolHeight, 0.60f, true ); // map progress circle g.setAntiAlias(true); g.setLineWidth(2f); g.setColor(Color.white); - int circleX = width - marginX - ( // max width: "100.00%" + float circleDiameter = symbolHeight * 0.60f; + int circleX = (int) (width - marginX - ( // max width: "100.00%" getScoreSymbolImage('1').getWidth() + getScoreSymbolImage('0').getWidth() * 4 + getScoreSymbolImage('.').getWidth() + getScoreSymbolImage('%').getWidth() - ); - float circleDiameter = symbolHeight * 0.75f; + ) * 0.60f - circleDiameter); g.drawOval(circleX, symbolHeight, circleDiameter, circleDiameter); OsuFile osu = MusicController.getOsuFile(); @@ -528,21 +557,26 @@ public class GameData { if (!breakPeriod) { // scorebar - // TODO: these might need to be scaled by cropping the empty (transparent) space around the images... float healthRatio = healthDisplay / 100f; if (firstObject) { // gradually move ki before map begins if (firstObjectTime >= 1500 && trackPosition < firstObjectTime - 500) healthRatio = (float) trackPosition / (firstObjectTime - 500); } Image scorebar = GameImage.SCOREBAR_BG.getImage(); - Image colour = (scorebarColour != null) ? - scorebarColour.getCurrentFrame() : - GameImage.SCOREBAR_COLOUR.getImage(); - float colourX = scorebar.getWidth() * 0.017f, colourY = scorebar.getHeight() * 0.3f; + Image colour; + if (scorebarColour != null){ + scorebarColour.updateNoDraw(); //TODO deprecated method + colour = scorebarColour.getCurrentFrame(); + } else { + colour = GameImage.SCOREBAR_COLOUR.getImage(); + } + float colourX = 4 * GameImage.uiscale, colourY = 15 * GameImage.uiscale; + Image colourCropped = colour.getSubImage(0, 0, (int) (645 * GameImage.uiscale * healthRatio), colour.getHeight()); + + scorebar.setAlpha(1f); scorebar.draw(0, 0); - Image colourCropped = colour.getSubImage(0, 0, (int) (colour.getWidth() * healthRatio), colour.getHeight()); colourCropped.draw(colourX, colourY); - + Image ki = null; if (health >= 50f) ki = GameImage.SCOREBAR_KI.getImage(); @@ -550,7 +584,7 @@ public class GameData { ki = GameImage.SCOREBAR_KI_DANGER.getImage(); else ki = GameImage.SCOREBAR_KI_DANGER2.getImage(); - ki.drawCentered(colourX + colourCropped.getWidth(), ki.getHeight() / 2f); + ki.drawCentered(colourX + colourCropped.getWidth(), colourY); // combo burst if (comboBurstIndex != -1 && comboBurstAlpha > 0f) { @@ -621,46 +655,38 @@ public class GameData { * @param osu the OsuFile */ public void drawRankingElements(Graphics g, OsuFile osu) { - // grade - Grade grade = getGrade(); - if (grade != Grade.NULL) - grade.getLargeImage().draw(width * 0.985f - grade.getLargeImage().getWidth(), height * 0.09f); - + + float marginX = width * 0.01f, marginY = height * 0.025f; // header & "Ranking" text Image rankingTitle = GameImage.RANKING_TITLE.getImage(); - float rankingHeight = (rankingTitle.getHeight() * 0.75f) + 3; - g.setColor(Utils.COLOR_BLACK_ALPHA); - g.fillRect(0, 0, width, rankingHeight); - rankingTitle.draw((width * 0.97f) - rankingTitle.getWidth(), 0); - float marginX = width * 0.01f, marginY = height * 0.01f; - Utils.FONT_LARGE.drawString(marginX, marginY, - String.format("%s - %s [%s]", osu.getArtist(), osu.getTitle(), osu.version), Color.white); - Utils.FONT_MEDIUM.drawString(marginX, marginY + Utils.FONT_LARGE.getLineHeight() - 6, - String.format("Beatmap by %s", osu.creator), Color.white); - Utils.FONT_MEDIUM.drawString( - marginX, marginY + Utils.FONT_LARGE.getLineHeight() + Utils.FONT_MEDIUM.getLineHeight() - 10, - String.format("Played on %s.", scoreData.getTimeString()), Color.white); - // ranking panel Image rankingPanel = GameImage.RANKING_PANEL.getImage(); - int rankingPanelWidth = rankingPanel.getWidth(); - int rankingPanelHeight = rankingPanel.getHeight(); - rankingPanel.draw(0, rankingHeight - (rankingHeight / 10f)); + + + //TODO Version 2 skins + float rankingHeight = 75; - float symbolTextScale = (height / 15f) / getScoreSymbolImage('0').getHeight(); - float rankResultScale = (height * 0.03f) / hitResults[HIT_300].getHeight(); + rankingPanel.draw(0, (int) (rankingHeight * GameImage.uiscale)); + + float scoreTextScale = 1.0f; + float symbolTextScale = 1.15f; + float rankResultScale = 0.5f; // score - drawSymbolString((score < 100000000) ? String.format("%08d", score) : Long.toString(score), - (int) (width * 0.18f), height / 6, symbolTextScale, false); + drawFixedSizeSymbolString( + (score < 100000000) ? String.format("%08d", score) : Long.toString(score), + (int) (210 * GameImage.uiscale), + (int) ((rankingHeight + 50) * GameImage.uiscale), + scoreTextScale, + getScoreSymbolImage('0').getWidth() * scoreTextScale - 2, false); // result counts - float resultInitialX = rankingPanelWidth * 0.20f; - float resultInitialY = rankingHeight + (rankingPanelHeight * 0.27f) + (rankingHeight / 10f); - float resultHitInitialX = rankingPanelWidth * 0.05f; - float resultHitInitialY = resultInitialY + (getScoreSymbolImage('0').getHeight() * symbolTextScale); - float resultOffsetX = rankingPanelWidth / 2f; - float resultOffsetY = rankingPanelHeight * 0.2f; + float resultInitialX = 130; + float resultInitialY = rankingHeight + 140; + float resultHitInitialX = 65; + float resultHitInitialY = rankingHeight + 182 ; + float resultOffsetX = 320; + float resultOffsetY = 96; int[] rankDrawOrder = { HIT_300, HIT_300G, HIT_100, HIT_100K, HIT_50, HIT_MISS }; int[] rankResultOrder = { @@ -670,28 +696,43 @@ public class GameData { }; for (int i = 0; i < rankDrawOrder.length; i += 2) { - hitResults[rankDrawOrder[i]].getScaledCopy(rankResultScale).draw( - resultHitInitialX, resultHitInitialY - (hitResults[rankDrawOrder[i]].getHeight() * rankResultScale) + (resultOffsetY * (i / 2))); - hitResults[rankDrawOrder[i+1]].getScaledCopy(rankResultScale).draw( - resultHitInitialX + resultOffsetX, resultHitInitialY - (hitResults[rankDrawOrder[i]].getHeight() * rankResultScale) + (resultOffsetY * (i / 2))); + hitResults[rankDrawOrder[i]].getScaledCopy(rankResultScale).drawCentered( + (resultHitInitialX * GameImage.uiscale), + ((resultHitInitialY + (resultOffsetY * (i / 2))) * GameImage.uiscale) + ); + hitResults[rankDrawOrder[i+1]].getScaledCopy(rankResultScale).drawCentered( + ((resultHitInitialX + resultOffsetX) * GameImage.uiscale), + ((resultHitInitialY + (resultOffsetY * (i / 2))) * GameImage.uiscale) + ); drawSymbolString(String.format("%dx", rankResultOrder[i]), - (int) resultInitialX, (int) (resultInitialY + (resultOffsetY * (i / 2))), symbolTextScale, false); + (int) (resultInitialX * GameImage.uiscale), + (int) ((resultInitialY + (resultOffsetY * (i / 2))) * GameImage.uiscale), + symbolTextScale, false); drawSymbolString(String.format("%dx", rankResultOrder[i+1]), - (int) (resultInitialX + resultOffsetX), (int) (resultInitialY + (resultOffsetY * (i / 2))), symbolTextScale, false); + (int) ((resultInitialX + resultOffsetX) * GameImage.uiscale), + (int) ((resultInitialY + (resultOffsetY * (i / 2))) * GameImage.uiscale), + symbolTextScale, false); } // combo and accuracy Image rankingMaxCombo = GameImage.RANKING_MAXCOMBO.getImage(); Image rankingAccuracy = GameImage.RANKING_ACCURACY.getImage(); - float textY = rankingHeight + (rankingPanelHeight * 0.87f) - (rankingHeight / 10f); - float numbersX = rankingMaxCombo.getWidth() * .07f; - float numbersY = textY + rankingMaxCombo.getHeight() * 0.7f; - rankingMaxCombo.draw(width * 0.01f, textY); - rankingAccuracy.draw(rankingPanelWidth / 2f, textY); + float accuracyX = 295; + float textY = rankingHeight + 425; + float numbersY = textY + 30; drawSymbolString(String.format("%dx", comboMax), - (int) (width * 0.01f + numbersX), (int) numbersY, symbolTextScale, false); + (int) (25 * GameImage.uiscale), + (int) (numbersY * GameImage.uiscale), symbolTextScale, false); drawSymbolString(String.format("%02.2f%%", getScorePercent()), - (int) (rankingPanelWidth / 2f + numbersX), (int) numbersY, symbolTextScale, false); + (int) ((accuracyX + 20) * GameImage.uiscale), + (int) (numbersY * GameImage.uiscale), symbolTextScale, false); + rankingMaxCombo.draw( + (int) (10 * GameImage.uiscale), + (int) (textY * GameImage.uiscale)); + rankingAccuracy.draw( + (int) (accuracyX * GameImage.uiscale), + (int) (textY * GameImage.uiscale)); + // full combo if (comboMax == fullObjectCount) { @@ -700,7 +741,24 @@ public class GameData { (height * 0.99f) - GameImage.RANKING_PERFECT.getImage().getHeight() ); } + // grade + Grade grade = getGrade(); + if (grade != Grade.NULL) + grade.getLargeImage().draw(width-grade.getLargeImage().getWidth(), rankingHeight); + //Header + g.setColor(Utils.COLOR_BLACK_ALPHA); + g.fillRect(0, 0, width, 100 * GameImage.uiscale); + rankingTitle.draw((width * 0.97f) - rankingTitle.getWidth(), 0); + Utils.FONT_LARGE.drawString(marginX, marginY, + String.format("%s - %s [%s]", osu.getArtist(), osu.getTitle(), osu.version), Color.white); + Utils.FONT_MEDIUM.drawString(marginX, marginY + Utils.FONT_LARGE.getLineHeight() - 6, + String.format("Beatmap by %s", osu.creator), Color.white); + Utils.FONT_MEDIUM.drawString( + marginX, marginY + Utils.FONT_LARGE.getLineHeight() + Utils.FONT_MEDIUM.getLineHeight() - 10, + String.format("Played on %s.", scoreData.getTimeString()), Color.white); + + // mod icons int modWidth = GameMod.AUTO.getImage().getWidth(); float modX = (width * 0.98f) - modWidth; @@ -1100,4 +1158,4 @@ public class GameData { public void addHitError(int time, int x, int y, int timeDiff) { hitErrorList.add(new HitErrorInfo(time, x, y, timeDiff)); } -} \ No newline at end of file +} diff --git a/src/itdelatrisu/opsu/GameImage.java b/src/itdelatrisu/opsu/GameImage.java index d3cf4d2b..23034fe0 100644 --- a/src/itdelatrisu/opsu/GameImage.java +++ b/src/itdelatrisu/opsu/GameImage.java @@ -18,12 +18,11 @@ package itdelatrisu.opsu; -import itdelatrisu.opsu.states.SongMenu; - import java.io.File; import java.util.ArrayList; import java.util.List; +import org.newdawn.slick.Animation; import org.newdawn.slick.Image; import org.newdawn.slick.SlickException; @@ -32,47 +31,17 @@ import org.newdawn.slick.SlickException; */ public enum GameImage { // Cursor - CURSOR ("cursor", "png") { - @Override - protected Image process_sub(Image img, int w, int h) { - return img.getScaledCopy(1 + ((h - 600) / 1000f)); - } - }, - CURSOR_MIDDLE ("cursormiddle", "png") { - @Override - protected Image process_sub(Image img, int w, int h) { - return CURSOR.process_sub(img, w, h); - } - }, - CURSOR_TRAIL ("cursortrail", "png") { - @Override - protected Image process_sub(Image img, int w, int h) { - return CURSOR.process_sub(img, w, h); - } - }, - CURSOR_OLD ("cursor2", "png", false, false) { - @Override - protected Image process_sub(Image img, int w, int h) { - return CURSOR.process_sub(img, w, h); - } - }, - CURSOR_TRAIL_OLD ("cursortrail2", "png", false, false) { - @Override - protected Image process_sub(Image img, int w, int h) { - return CURSOR.process_sub(img, w, h); - } - }, + CURSOR ("cursor", "png"), + CURSOR_MIDDLE ("cursormiddle", "png"), + CURSOR_TRAIL ("cursortrail", "png"), + CURSOR_OLD ("cursor2", "png", false, false), + CURSOR_TRAIL_OLD ("cursortrail2", "png", false, false), // Game SECTION_PASS ("section-pass", "png"), SECTION_FAIL ("section-fail", "png"), WARNINGARROW ("play-warningarrow", "png"), - SKIP ("play-skip", "png") { - @Override - protected Image process_sub(Image img, int w, int h) { - return img.getScaledCopy((h * 0.1f) / img.getHeight()); - } - }, + SKIP ("play-skip", "play-skip-%d","png"), COUNTDOWN_READY ("ready", "png") { @Override protected Image process_sub(Image img, int w, int h) { @@ -82,25 +51,25 @@ public enum GameImage { COUNTDOWN_3 ("count3", "png") { @Override protected Image process_sub(Image img, int w, int h) { - return COUNTDOWN_READY.process_sub(img, w, h); + return img.getScaledCopy((h / 3f) / img.getHeight()); } }, COUNTDOWN_2 ("count2", "png") { @Override protected Image process_sub(Image img, int w, int h) { - return COUNTDOWN_READY.process_sub(img, w, h); + return img.getScaledCopy((h / 3f) / img.getHeight()); } }, COUNTDOWN_1 ("count1", "png") { @Override protected Image process_sub(Image img, int w, int h) { - return COUNTDOWN_READY.process_sub(img, w, h); + return img.getScaledCopy((h / 3f) / img.getHeight()); } }, COUNTDOWN_GO ("go", "png") { @Override protected Image process_sub(Image img, int w, int h) { - return COUNTDOWN_READY.process_sub(img, w, h); + return img.getScaledCopy((h / 3f) / img.getHeight()); } }, HITCIRCLE_SELECT ("hitcircleselect", "png"), @@ -168,18 +137,9 @@ public enum GameImage { // Score Data COMBO_BURST ("comboburst", "comboburst-%d", "png"), - SCOREBAR_BG ("scorebar-bg", "png") { - @Override - protected Image process_sub(Image img, int w, int h) { - return img.getScaledCopy((w * 0.565f) / img.getWidth()); - } - }, - SCOREBAR_COLOUR ("scorebar-colour", "scorebar-colour-%d", "png") { - @Override - protected Image process_sub(Image img, int w, int h) { - return img.getScaledCopy((w * 0.521f) / img.getWidth()); - } - }, + SCOREBAR_BG ("scorebar-bg", "png"), + SCOREBAR_COLOUR ("scorebar-colour", "scorebar-colour-%d", "png"), + //TODO scorebar-marker? SCOREBAR_KI ("scorebar-ki", "png"), SCOREBAR_KI_DANGER ("scorebar-kidanger", "png"), SCOREBAR_KI_DANGER2 ("scorebar-kidanger2", "png"), @@ -192,92 +152,27 @@ public enum GameImage { HIT_300G ("hit300g", "png"), HIT_SLIDER10 ("sliderpoint10", "png"), HIT_SLIDER30 ("sliderpoint30", "png"), - RANKING_SS ("ranking-X", "png") { - @Override - protected Image process_sub(Image img, int w, int h) { - return img.getScaledCopy((h / 2f) / img.getHeight()); - } - }, + RANKING_SS ("ranking-X", "png"), RANKING_SS_SMALL ("ranking-X-small", "png"), - RANKING_SSH ("ranking-XH", "png") { - @Override - protected Image process_sub(Image img, int w, int h) { - return RANKING_SS.process_sub(img, w, h); - } - }, + RANKING_SSH ("ranking-XH", "png"), RANKING_SSH_SMALL ("ranking-XH-small", "png"), - RANKING_S ("ranking-S", "png") { - @Override - protected Image process_sub(Image img, int w, int h) { - return RANKING_SS.process_sub(img, w, h); - } - }, + RANKING_S ("ranking-S", "png"), RANKING_S_SMALL ("ranking-S-small", "png"), - RANKING_SH ("ranking-SH", "png") { - @Override - protected Image process_sub(Image img, int w, int h) { - return RANKING_SS.process_sub(img, w, h); - } - }, + RANKING_SH ("ranking-SH", "png"), RANKING_SH_SMALL ("ranking-SH-small", "png"), - RANKING_A ("ranking-A", "png") { - @Override - protected Image process_sub(Image img, int w, int h) { - return RANKING_SS.process_sub(img, w, h); - } - }, + RANKING_A ("ranking-A", "png"), RANKING_A_SMALL ("ranking-A-small", "png"), - RANKING_B ("ranking-B", "png") { - @Override - protected Image process_sub(Image img, int w, int h) { - return RANKING_SS.process_sub(img, w, h); - } - }, + RANKING_B ("ranking-B", "png"), RANKING_B_SMALL ("ranking-B-small", "png"), - RANKING_C ("ranking-C", "png") { - @Override - protected Image process_sub(Image img, int w, int h) { - return RANKING_SS.process_sub(img, w, h); - } - }, + RANKING_C ("ranking-C", "png"), RANKING_C_SMALL ("ranking-C-small", "png"), - RANKING_D ("ranking-D", "png") { - @Override - protected Image process_sub(Image img, int w, int h) { - return RANKING_SS.process_sub(img, w, h); - } - }, + RANKING_D ("ranking-D", "png"), RANKING_D_SMALL ("ranking-D-small", "png"), - RANKING_PANEL ("ranking-panel", "png") { - @Override - protected Image process_sub(Image img, int w, int h) { - return img.getScaledCopy((h * 0.63f) / img.getHeight()); - } - }, - RANKING_PERFECT ("ranking-perfect", "png") { - @Override - protected Image process_sub(Image img, int w, int h) { - return img.getScaledCopy((h * 0.16f) / img.getHeight()); - } - }, - RANKING_TITLE ("ranking-title", "png") { - @Override - protected Image process_sub(Image img, int w, int h) { - return img.getScaledCopy((h * 0.15f) / img.getHeight()); - } - }, - RANKING_MAXCOMBO ("ranking-maxcombo", "png") { - @Override - protected Image process_sub(Image img, int w, int h) { - return img.getScaledCopy((h * 0.05f) / img.getHeight()); - } - }, - RANKING_ACCURACY ("ranking-accuracy", "png") { - @Override - protected Image process_sub(Image img, int w, int h) { - return img.getScaledCopy((h * 0.05f) / img.getHeight()); - } - }, + RANKING_PANEL ("ranking-panel", "png"), + RANKING_PERFECT ("ranking-perfect", "png"), + RANKING_TITLE ("ranking-title", "png"), + RANKING_MAXCOMBO ("ranking-maxcombo", "png"), + RANKING_ACCURACY ("ranking-accuracy", "png"), DEFAULT_0 ("default-0", "png"), DEFAULT_1 ("default-1", "png"), DEFAULT_2 ("default-2", "png"), @@ -436,19 +331,8 @@ public enum GameImage { return img.getScaledCopy((h * 0.3f) / img.getHeight()); } }, - MENU_BACK ("menu-back", "png", false, false) { - @Override - protected Image process_sub(Image img, int w, int h) { - return img.getScaledCopy((h * 0.1f) / img.getHeight()); - } - }, - MENU_BUTTON_BG ("menu-button-background", "png", false, false) { - @Override - protected Image process_sub(Image img, int w, int h) { - // TODO: scale these properly (messy due to non-cropped images) - return img.getScaledCopy(w / 2, (int) (h * 0.95f) / SongMenu.MAX_SONG_BUTTONS); - } - }, + MENU_BACK ("menu-back", "menu-back-%d","png"), + MENU_BUTTON_BG ("menu-button-background", "png", false, false), MENU_TAB ("selection-tab", "png", false, false) { @Override protected Image process_sub(Image img, int w, int h) { @@ -523,19 +407,8 @@ public enum GameImage { return MUSIC_PLAY.process_sub(img, w, h); } }, - - RANKING_RETRY ("ranking-retry", "png", false, false) { - @Override - protected Image process_sub(Image img, int w, int h) { - return img.getScaledCopy((h * 0.15f) / img.getHeight()); - } - }, - RANKING_EXIT ("ranking-back", "png", false, false) { - @Override - protected Image process_sub(Image img, int w, int h) { - return img.getScaledCopy((h * 0.15f) / img.getHeight()); - } - }, + RANKING_RETRY ("ranking-retry", "png", false, false), + RANKING_EXIT ("ranking-back", "png", false, false), DOWNLOADS ("downloads", "png", false, false) { @Override protected Image process_sub(Image img, int w, int h) { @@ -606,6 +479,9 @@ public enum GameImage { /** Container dimensions. */ private static int containerWidth, containerHeight; + + /** value ui should be scaled by */ + public static float uiscale; /** * Initializes the GameImage class with container dimensions. @@ -615,6 +491,7 @@ public enum GameImage { public static void init(int width, int height) { containerWidth = width; containerHeight = height; + uiscale = containerHeight / 768f; } /** @@ -712,6 +589,17 @@ public enum GameImage { setDefaultImage(); return (skinImage != null) ? skinImage : defaultImage; } + + /** + * Returns an Animation based on the image array. + * If no image array exist, returns the single image as an animation. + */ + public Animation getAnimation(int duration){ + Image[] images = getImages(); + if (images == null) + images = new Image[]{ getImage() }; + return new Animation(images, duration); + } /** * Returns the image array associated with this resource. @@ -925,13 +813,14 @@ public enum GameImage { * Performs individual post-loading actions on the image. */ private void process() { + int fakeWid = 768*containerWidth/containerHeight; if (skinImages != null) { for (int i = 0; i < skinImages.length; i++) - setImage(process_sub(getImages()[i], containerWidth, containerHeight), i); + setImage(process_sub(getImages()[i], fakeWid, 768).getScaledCopy(uiscale), i); } else if (defaultImages != null && skinImage == null) { for (int i = 0; i < defaultImages.length; i++) - setImage(process_sub(getImages()[i], containerWidth, containerHeight), i); + setImage(process_sub(getImages()[i], fakeWid, 768).getScaledCopy(uiscale), i); } else - setImage(process_sub(getImage(), containerWidth, containerHeight)); + setImage(process_sub(getImage(), fakeWid, 768).getScaledCopy(uiscale)); } } \ No newline at end of file diff --git a/src/itdelatrisu/opsu/MenuButton.java b/src/itdelatrisu/opsu/MenuButton.java index cf4d35d9..f8340f07 100644 --- a/src/itdelatrisu/opsu/MenuButton.java +++ b/src/itdelatrisu/opsu/MenuButton.java @@ -366,20 +366,23 @@ public class MenuButton { */ private void setHoverRadius() { int xOffset = 0, yOffset = 0; - if (dir != Expand.CENTER) { - // offset by difference between normal/scaled image dimensions - xOffset = (int) ((scale - 1f) * img.getWidth()); - yOffset = (int) ((scale - 1f) * img.getHeight()); - if (dir == Expand.UP || dir == Expand.DOWN) - xOffset = 0; // no horizontal offset - if (dir == Expand.RIGHT || dir == Expand.LEFT) - yOffset = 0; // no vertical offset - if (dir == Expand.RIGHT || dir == Expand.DOWN_RIGHT || dir == Expand.UP_RIGHT) - xOffset *= -1; // flip x for right - if (dir == Expand.DOWN || dir == Expand.DOWN_LEFT || dir == Expand.DOWN_RIGHT) - yOffset *= -1; // flip y for down + if(img != null){ + if (dir != Expand.CENTER) { + // offset by difference between normal/scaled image dimensions + xOffset = (int) ((scale - 1f) * img.getWidth()); + yOffset = (int) ((scale - 1f) * img.getHeight()); + if (dir == Expand.UP || dir == Expand.DOWN) + xOffset = 0; // no horizontal offset + if (dir == Expand.RIGHT || dir == Expand.LEFT) + yOffset = 0; // no vertical offset + if (dir == Expand.RIGHT || dir == Expand.DOWN_RIGHT || dir == Expand.UP_RIGHT) + xOffset *= -1; // flip x for right + if (dir == Expand.DOWN || dir == Expand.DOWN_LEFT || dir == Expand.DOWN_RIGHT) + yOffset *= -1; // flip y for down + } + this.xRadius = ((img.getWidth() * scale) + xOffset) / 2f; + this.yRadius = ((img.getHeight() * scale) + yOffset) / 2f; + } - this.xRadius = ((img.getWidth() * scale) + xOffset) / 2f; - this.yRadius = ((img.getHeight() * scale) + yOffset) / 2f; } -} \ No newline at end of file +} diff --git a/src/itdelatrisu/opsu/OsuGroupNode.java b/src/itdelatrisu/opsu/OsuGroupNode.java index 3cefe22e..29918ec6 100644 --- a/src/itdelatrisu/opsu/OsuGroupNode.java +++ b/src/itdelatrisu/opsu/OsuGroupNode.java @@ -61,12 +61,13 @@ public class OsuGroupNode { */ public void draw(float x, float y, float headerY, float footerY, Grade grade, boolean focus) { boolean expanded = (osuFileIndex > -1); + float xOffset = 0f; OsuFile osu; Image bg = GameImage.MENU_BUTTON_BG.getImage(); + bg.setAlpha(0.9f); Color bgColor; Color textColor = Color.lightGray; - - // draw song button background + if (expanded) { // expanded x -= bg.getWidth() / 10f; if (focus) { diff --git a/src/itdelatrisu/opsu/OsuHitObject.java b/src/itdelatrisu/opsu/OsuHitObject.java index fec96502..59eb9678 100644 --- a/src/itdelatrisu/opsu/OsuHitObject.java +++ b/src/itdelatrisu/opsu/OsuHitObject.java @@ -296,4 +296,12 @@ public class OsuHitObject { * @return true if new combo */ public boolean isNewCombo() { return (type & TYPE_NEWCOMBO) > 0; } + + /** + * Returns the number of extra skips on the combo colours + */ + public int getComboSkip() { + return (type >> 4); + } + } diff --git a/src/itdelatrisu/opsu/OsuParser.java b/src/itdelatrisu/opsu/OsuParser.java index 08999f98..188a6acb 100644 --- a/src/itdelatrisu/opsu/OsuParser.java +++ b/src/itdelatrisu/opsu/OsuParser.java @@ -544,6 +544,7 @@ public class OsuParser { int comboNumber = 1; // combo number int objectIndex = 0; + boolean first = true; while ((line = in.readLine()) != null && objectIndex < osu.objects.length) { line = line.trim(); if (!isValidLine(line)) @@ -563,10 +564,16 @@ public class OsuParser { // set combo info // - new combo: get next combo index, reset combo number // - else: maintain combo index, increase combo number - if ((hitObject.isNewCombo() && !hitObject.isSpinner()) || objectIndex == 0) { - comboIndex = (comboIndex + 1) % osu.combo.length; - comboNumber = 1; + if (((hitObject.isNewCombo()|| first) && !hitObject.isSpinner()) ) { + int skip = 1 + hitObject.getComboSkip(); + + for(int i=0; i < skip; i++){ + comboIndex = (comboIndex + 1) % osu.combo.length; + comboNumber = 1; + } + first=false; } + hitObject.setComboIndex(comboIndex); hitObject.setComboNumber(comboNumber++); diff --git a/src/itdelatrisu/opsu/ScoreData.java b/src/itdelatrisu/opsu/ScoreData.java index a08ea718..ece9e71b 100644 --- a/src/itdelatrisu/opsu/ScoreData.java +++ b/src/itdelatrisu/opsu/ScoreData.java @@ -287,6 +287,8 @@ public class ScoreData implements Comparable { // time since if (getTimeSince() != null) { Image clock = GameImage.HISTORY.getImage(); + g.setColor((focus) ? BG_FOCUS : BG_NORMAL); + g.fillRect(baseX + buttonWidth, y, Utils.FONT_DEFAULT.getWidth(" "),buttonHeight); clock.drawCentered(baseX + buttonWidth * 1.02f + clock.getWidth() / 2f, midY); Utils.FONT_DEFAULT.drawString( baseX + buttonWidth * 1.03f + clock.getWidth(), @@ -313,4 +315,4 @@ public class ScoreData implements Comparable { else return Long.compare(this.timestamp, that.timestamp); } -} \ No newline at end of file +} diff --git a/src/itdelatrisu/opsu/Utils.java b/src/itdelatrisu/opsu/Utils.java index 9d3ce39c..0bde121f 100644 --- a/src/itdelatrisu/opsu/Utils.java +++ b/src/itdelatrisu/opsu/Utils.java @@ -70,8 +70,8 @@ public class Utils { COLOR_WHITE_ALPHA = new Color(255, 255, 255, 0.5f), COLOR_BLUE_DIVIDER = new Color(49, 94, 237), COLOR_BLUE_BACKGROUND = new Color(74, 130, 255), - COLOR_BLUE_BUTTON = new Color(50, 189, 237), - COLOR_ORANGE_BUTTON = new Color(230, 151, 87), + COLOR_BLUE_BUTTON = new Color(40, 129, 237), + COLOR_ORANGE_BUTTON = new Color(200, 90, 3), COLOR_GREEN_OBJECT = new Color(26, 207, 26), COLOR_BLUE_OBJECT = new Color(46, 136, 248), COLOR_RED_OBJECT = new Color(243, 48, 77), @@ -87,8 +87,8 @@ public class Utils { /** The default map colors, used when a map does not provide custom colors. */ public static final Color[] DEFAULT_COMBO = { - COLOR_GREEN_OBJECT, COLOR_BLUE_OBJECT, - COLOR_RED_OBJECT, COLOR_ORANGE_OBJECT + COLOR_ORANGE_OBJECT, COLOR_GREEN_OBJECT, + COLOR_BLUE_OBJECT, COLOR_RED_OBJECT, }; /** Game fonts. */ @@ -222,11 +222,21 @@ public class Utils { DownloadNode.init(width, height); // back button - Image back = GameImage.MENU_BACK.getImage(); - backButton = new MenuButton(back, - back.getWidth() / 2f, - height - (back.getHeight() / 2f)); + //TODO: this is annoying perhaps we can just pass in GameImage to MenuButton? + if (GameImage.MENU_BACK.getImages() != null){ + Animation back = GameImage.MENU_BACK.getAnimation(200); + backButton = new MenuButton(back, + back.getWidth() / 2f, + height - (back.getHeight() / 2f)); + }else{ + Image back = GameImage.MENU_BACK.getImage(); + backButton = new MenuButton(GameImage.MENU_BACK.getImage(), + back.getWidth() / 2f, + height - (back.getHeight() / 2f)); + } backButton.setHoverExpand(MenuButton.Expand.UP_RIGHT); + + } /** @@ -912,4 +922,4 @@ public class Utils { list.add(str); return list; } -} \ No newline at end of file +} diff --git a/src/itdelatrisu/opsu/states/Game.java b/src/itdelatrisu/opsu/states/Game.java index df4bbdd1..2bfdbed6 100644 --- a/src/itdelatrisu/opsu/states/Game.java +++ b/src/itdelatrisu/opsu/states/Game.java @@ -45,6 +45,7 @@ import java.util.Stack; import java.util.concurrent.TimeUnit; import org.lwjgl.input.Keyboard; +import org.newdawn.slick.Animation; import org.newdawn.slick.Color; import org.newdawn.slick.GameContainer; import org.newdawn.slick.Graphics; @@ -268,7 +269,6 @@ public class Game extends BasicGameState { // skip beginning if (objectIndex == 0 && - firstObjectTime - SKIP_OFFSET > 5000 && trackPosition < osu.objects[0].getTime() - SKIP_OFFSET) skipButton.draw(); @@ -782,7 +782,6 @@ public class Game extends BasicGameState { int firstObjectTime = osu.objects[0].getTime(); int trackPosition = MusicController.getPosition(); if (objectIndex == 0 && - firstObjectTime - SKIP_OFFSET > 4000 && trackPosition < firstObjectTime - SKIP_OFFSET) { if (isLeadIn()) { leadInTime = 0; @@ -810,12 +809,20 @@ public class Game extends BasicGameState { } // skip button - Image skip = GameImage.SKIP.getImage(); - skipButton = new MenuButton(skip, - width - (skip.getWidth() / 2f), - height - (skip.getHeight() / 2f)); + //TODO: this is annoying perhaps we can just pass in GameImage to MenuButton? + if (GameImage.SKIP.getImages() != null){ + Animation back = GameImage.SKIP.getAnimation(200); + skipButton = new MenuButton(back, + width - back.getWidth() / 2f, + height - (back.getHeight() / 2f)); + }else{ + Image back = GameImage.SKIP.getImage(); + skipButton = new MenuButton(GameImage.SKIP.getImage(), + width - back.getWidth() / 2f, + height - (back.getHeight() / 2f)); + } skipButton.setHoverExpand(MenuButton.Expand.UP_LEFT); - + // load other images... ((GamePauseMenu) game.getState(Opsu.STATE_GAMEPAUSEMENU)).loadImages(); data.loadImages(); diff --git a/src/itdelatrisu/opsu/states/GameRanking.java b/src/itdelatrisu/opsu/states/GameRanking.java index 0ad5d6ff..c1a1b315 100644 --- a/src/itdelatrisu/opsu/states/GameRanking.java +++ b/src/itdelatrisu/opsu/states/GameRanking.java @@ -95,8 +95,10 @@ public class GameRanking extends BasicGameState { OsuFile osu = MusicController.getOsuFile(); // background - if (!osu.drawBG(width, height, 0.7f, true)) - g.setBackground(Utils.COLOR_BLACK_ALPHA); + if (!osu.drawBG(width, height, 0.7f, true)) { + GameImage.MENU_BG.getImage().draw(0,0); + //g.setBackground(Utils.COLOR_BLACK_ALPHA); + } // ranking screen elements data.drawRankingElements(g, osu); diff --git a/src/itdelatrisu/opsu/states/OptionsMenu.java b/src/itdelatrisu/opsu/states/OptionsMenu.java index 4e4afc5a..53beaee2 100644 --- a/src/itdelatrisu/opsu/states/OptionsMenu.java +++ b/src/itdelatrisu/opsu/states/OptionsMenu.java @@ -176,13 +176,13 @@ public class OptionsMenu extends BasicGameState { float tabX = (width / 50) + (tabImage.getWidth() / 2f); float tabY = 15 + Utils.FONT_XLARGE.getLineHeight() + (tabImage.getHeight() / 2f); int tabOffset = Math.min(tabImage.getWidth(), - ((width - subtextWidth - tabImage.getWidth()) / 2) / OptionTab.SIZE); + (width/2) / OptionTab.SIZE); for (OptionTab tab : OptionTab.values()) tab.button = new MenuButton(tabImage, tabX + (tab.ordinal() * tabOffset), tabY); // game option coordinate modifiers textY = (int) (tabY + tabImage.getHeight()); - offsetY = (height - textY - GameImage.MENU_BACK.getImage().getHeight()) / maxOptionsScreen; + offsetY = (height - textY - GameImage.MENU_BACK.getAnimation(1).getHeight()) / maxOptionsScreen; } @Override diff --git a/src/itdelatrisu/opsu/states/SongMenu.java b/src/itdelatrisu/opsu/states/SongMenu.java index 84f0c1cd..3153d141 100644 --- a/src/itdelatrisu/opsu/states/SongMenu.java +++ b/src/itdelatrisu/opsu/states/SongMenu.java @@ -253,7 +253,7 @@ public class SongMenu extends BasicGameState { search.setMaxLength(60); // selection buttons - float selectX = GameImage.MENU_BACK.getImage().getWidth() * 1.75f; + float selectX = GameImage.MENU_BACK.getAnimation(1).getWidth() * 1.75f; float selectY = height - GameImage.SELECTION_MODS.getImage().getHeight() / 2f; float selectOffset = GameImage.SELECTION_MODS.getImage().getWidth() * 1.05f; selectModsButton = new MenuButton(GameImage.SELECTION_MODS_OVERLAY.getImage(), diff --git a/src/org/newdawn/slick/Image.java b/src/org/newdawn/slick/Image.java new file mode 100644 index 00000000..c2480ff4 --- /dev/null +++ b/src/org/newdawn/slick/Image.java @@ -0,0 +1,1431 @@ +/* + * Copyright (c) 2013, Slick2D + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the Slick2D nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. +*/ + +package org.newdawn.slick; + +import java.io.IOException; +import java.io.InputStream; + +import org.newdawn.slick.opengl.ImageData; +import org.newdawn.slick.opengl.InternalTextureLoader; +import org.newdawn.slick.opengl.Texture; +import org.newdawn.slick.opengl.TextureImpl; +import org.newdawn.slick.opengl.pbuffer.GraphicsFactory; +import org.newdawn.slick.opengl.renderer.Renderer; +import org.newdawn.slick.opengl.renderer.SGL; +import org.newdawn.slick.util.Log; + +/** + * An image loaded from a file and renderable to the canvas + * + * @author kevin + */ +public class Image implements Renderable { + /** The top left corner identifier */ + public static final int TOP_LEFT = 0; + /** The top right corner identifier */ + public static final int TOP_RIGHT = 1; + /** The bottom right corner identifier */ + public static final int BOTTOM_RIGHT = 2; + /** The bottom left corner identifier */ + public static final int BOTTOM_LEFT = 3; + + /** The renderer to use for all GL operations */ + protected static SGL GL = Renderer.get(); + + /** The sprite sheet currently in use */ + protected static Image inUse; + /** Use Linear Filtering */ + public static final int FILTER_LINEAR = 1; + /** Use Nearest Filtering */ + public static final int FILTER_NEAREST = 2; + + /** The OpenGL texture for this image */ + protected Texture texture; + /** The width of the image */ + protected int width; + /** The height of the image */ + protected int height; + /** The texture coordinate width to use to find our image */ + protected float textureWidth; + /** The texture coordinate height to use to find our image */ + protected float textureHeight; + /** The x texture offset to use to find our image */ + protected float textureOffsetX; + /** The y texture offset to use to find our image */ + protected float textureOffsetY; + /** Angle to rotate the image to. */ + protected float angle; + /** The alpha to draw the image at */ + protected float alpha = 1.0f; + /** The name given for the image */ + protected String ref; + /** True if this image's state has been initialised */ + protected boolean inited = false; + /** A pixelData holding the pixel data if it's been read for this texture */ + protected byte[] pixelData; + /** True if the image has been destroyed */ + protected boolean destroyed; + + /** The x coordinate of the centre of rotation */ + protected float centerX; + /** The y coordinate of the centre of rotation */ + protected float centerY; + + /** A meaningful name provided by the user of the image to tag it */ + protected String name; + + /** The colours for each of the corners */ + protected Color[] corners; + /** The OpenGL max filter */ + private int filter = SGL.GL_LINEAR; + + /** True if the image should be flipped vertically */ + private boolean flipped; + /** The transparent colour set if any */ + private Color transparent; + + /** + * Create a texture as a copy of another + * + * @param other The other texture to copy + */ + protected Image(Image other) { + this.width = other.getWidth(); + this.height = other.getHeight(); + this.texture = other.texture; + this.textureWidth = other.textureWidth; + this.textureHeight = other.textureHeight; + this.ref = other.ref; + this.textureOffsetX = other.textureOffsetX; + this.textureOffsetY = other.textureOffsetY; + + centerX = width / 2f; + centerY = height / 2f; + inited = true; + } + + /** + * Cloning constructor - only used internally. + */ + protected Image() { + } + + /** + * Creates an image using the specified texture + * + * @param texture + * The texture to use + */ + public Image(Texture texture) { + this.texture = texture; + ref = texture.toString(); + clampTexture(); + } + + /** + * Create an image based on a file at the specified location + * + * @param ref + * The location of the image file to load + * @throws SlickException + * Indicates a failure to load the image + */ + public Image(String ref) throws SlickException { + this(ref, false); + } + + /** + * Create an image based on a file at the specified location + * + * @param ref The location of the image file to load + * @param trans The color to be treated as transparent + * @throws SlickException Indicates a failure to load the image + */ + public Image(String ref, Color trans) throws SlickException { + this(ref, false, FILTER_LINEAR, trans); + } + + /** + * Create an image based on a file at the specified location + * + * @param ref The location of the image file to load + * @param flipped True if the image should be flipped on the y-axis on load + * @throws SlickException Indicates a failure to load the image + */ + public Image(String ref, boolean flipped) throws SlickException { + this(ref, flipped, FILTER_LINEAR); + } + + /** + * Create an image based on a file at the specified location + * + * @param ref The location of the image file to load + * @param flipped True if the image should be flipped on the y-axis on load + * @param filter The filtering method to use when scaling this image + * @throws SlickException Indicates a failure to load the image + */ + public Image(String ref, boolean flipped, int filter) throws SlickException { + this(ref, flipped, filter, null); + } + + /** + * Create an image based on a file at the specified location + * + * @param ref The location of the image file to load + * @param flipped True if the image should be flipped on the y-axis on load + * @param f The filtering method to use when scaling this image + * @param transparent The color to treat as transparent + * @throws SlickException Indicates a failure to load the image + */ + public Image(String ref, boolean flipped, int f, Color transparent) throws SlickException { + this.filter = f == FILTER_LINEAR ? SGL.GL_LINEAR : SGL.GL_NEAREST; + this.transparent = transparent; + this.flipped = flipped; + + try { + this.ref = ref; + int[] trans = null; + if (transparent != null) { + trans = new int[3]; + trans[0] = (int) (transparent.r * 255); + trans[1] = (int) (transparent.g * 255); + trans[2] = (int) (transparent.b * 255); + } + texture = InternalTextureLoader.get().getTexture(ref, flipped, filter, trans); + } catch (IOException e) { + Log.error(e); + throw new SlickException("Failed to load image from: "+ref, e); + } + } + + /** + * Set the image filtering to be used. Note that this will also affect any + * image that was derived from this one (i.e. sub-images etc) + * + * @param f The filtering mode to use + */ + public void setFilter(int f) { + this.filter = f == FILTER_LINEAR ? SGL.GL_LINEAR : SGL.GL_NEAREST; + + texture.bind(); + GL.glTexParameteri(SGL.GL_TEXTURE_2D, SGL.GL_TEXTURE_MIN_FILTER, filter); + GL.glTexParameteri(SGL.GL_TEXTURE_2D, SGL.GL_TEXTURE_MAG_FILTER, filter); + } + + /** + * Create an empty image + * + * @param width The width of the image + * @param height The height of the image + * @throws SlickException Indicates a failure to create the underlying resource + */ + public Image(int width, int height) throws SlickException { + this(width, height, FILTER_NEAREST); + } + + /** + * Create an empty image + * + * @param width The width of the image + * @param height The height of the image + * @param f The filter to apply to scaling the new image + * @throws SlickException Indicates a failure to create the underlying resource + */ + public Image(int width, int height, int f) throws SlickException { + ref = super.toString(); + this.filter = f == FILTER_LINEAR ? SGL.GL_LINEAR : SGL.GL_NEAREST; + + try { + texture = InternalTextureLoader.get().createTexture(width, height, this.filter); + } catch (IOException e) { + Log.error(e); + throw new SlickException("Failed to create empty image "+width+"x"+height); + } + + init(); + } + + /** + * Create an image based on a file at the specified location + * + * @param in The input stream to read the image from + * @param ref The name that should be assigned to the image + * @param flipped True if the image should be flipped on the y-axis on load + * @throws SlickException Indicates a failure to load the image + */ + public Image(InputStream in, String ref, boolean flipped) throws SlickException { + this(in, ref, flipped, FILTER_LINEAR); + } + + /** + * Create an image based on a file at the specified location + * + * @param in The input stream to read the image from + * @param ref The name that should be assigned to the image + * @param flipped True if the image should be flipped on the y-axis on load + * @param filter The filter to use when scaling this image + * @throws SlickException Indicates a failure to load the image + */ + public Image(InputStream in, String ref, boolean flipped,int filter) throws SlickException { + load(in, ref, flipped, filter, null); + } + + /** + * Create an image from a pixelData of pixels + * + * @param buffer The pixelData to use to create the image + */ + Image(ImageBuffer buffer) { + this(buffer, FILTER_LINEAR); + TextureImpl.bindNone(); + } + + /** + * Create an image from a pixelData of pixels + * + * @param buffer The pixelData to use to create the image + * @param filter The filter to use when scaling this image + */ + Image(ImageBuffer buffer, int filter) { + this((ImageData) buffer, filter); + TextureImpl.bindNone(); + } + + /** + * Create an image from a image data source + * + * @param data The pixelData to use to create the image + */ + public Image(ImageData data) { + this(data, FILTER_LINEAR); + } + + /** + * Create an image from a image data source. Note that this method uses + * + * @param data The pixelData to use to create the image + * @param f The filter to use when scaling this image + */ + public Image(ImageData data, int f) { + try { + this.filter = f == FILTER_LINEAR ? SGL.GL_LINEAR : SGL.GL_NEAREST; + texture = InternalTextureLoader.get().getTexture(data, this.filter); + ref = texture.toString(); + } catch (IOException e) { + Log.error(e); + } + } + + /** + * Get the OpenGL image filter in use + * + * @return The filter for magnification + */ + public int getFilter() { + return filter; + } + + /** + * Get the reference to the resource this image was loaded from, if any. Note that + * this can be null in the cases where an image was programatically generated. + * + * @return The reference to the resource the reference was loaded from + */ + public String getResourceReference() { + return ref; + } + + /** + * Set the filter to apply when drawing this image + * + * @param r The red component of the filter colour + * @param g The green component of the filter colour + * @param b The blue component of the filter colour + * @param a The alpha component of the filter colour + */ + public void setImageColor(float r, float g, float b, float a) { + setColor(TOP_LEFT, r, g, b, a); + setColor(TOP_RIGHT, r, g, b, a); + setColor(BOTTOM_LEFT, r, g, b, a); + setColor(BOTTOM_RIGHT, r, g, b, a); + } + + /** + * Set the filter to apply when drawing this image + * + * @param r The red component of the filter colour + * @param g The green component of the filter colour + * @param b The blue component of the filter colour + */ + public void setImageColor(float r, float g, float b) { + setColor(TOP_LEFT, r, g, b); + setColor(TOP_RIGHT, r, g, b); + setColor(BOTTOM_LEFT, r, g, b); + setColor(BOTTOM_RIGHT, r, g, b); + } + + /** + * Set the color of the given corner when this image is rendered. This is + * useful lots of visual effect but especially light maps + * + * @param corner The corner identifier for the corner to be set + * @param r The red component value to set (between 0 and 1) + * @param g The green component value to set (between 0 and 1) + * @param b The blue component value to set (between 0 and 1) + * @param a The alpha component value to set (between 0 and 1) + */ + public void setColor(int corner, float r, float g, float b, float a) { + if (corners == null) { + corners = new Color[] {new Color(1,1,1,1f),new Color(1,1,1,1f), new Color(1,1,1,1f), new Color(1,1,1,1f)}; + } + + corners[corner].r = r; + corners[corner].g = g; + corners[corner].b = b; + corners[corner].a = a; + } + + /** + * Set the color of the given corner when this image is rendered. This is + * useful lots of visual effect but especially light maps + * + * @param corner The corner identifier for the corner to be set + * @param r The red component value to set (between 0 and 1) + * @param g The green component value to set (between 0 and 1) + * @param b The blue component value to set (between 0 and 1) + */ + public void setColor(int corner, float r, float g, float b) { + if (corners == null) { + corners = new Color[] {new Color(1,1,1,1f),new Color(1,1,1,1f), new Color(1,1,1,1f), new Color(1,1,1,1f)}; + } + + corners[corner].r = r; + corners[corner].g = g; + corners[corner].b = b; + } + + /** + * Clamp the loaded texture to it's edges + */ + public void clampTexture() { + if (GL.canTextureMirrorClamp()) { + GL.glTexParameteri(SGL.GL_TEXTURE_2D, SGL.GL_TEXTURE_WRAP_S, SGL.GL_MIRROR_CLAMP_TO_EDGE_EXT); + GL.glTexParameteri(SGL.GL_TEXTURE_2D, SGL.GL_TEXTURE_WRAP_T, SGL.GL_MIRROR_CLAMP_TO_EDGE_EXT); + } else { + GL.glTexParameteri(SGL.GL_TEXTURE_2D, SGL.GL_TEXTURE_WRAP_S, SGL.GL_CLAMP); + GL.glTexParameteri(SGL.GL_TEXTURE_2D, SGL.GL_TEXTURE_WRAP_T, SGL.GL_CLAMP); + } + } + + /** + * Give this image a meaningful tagging name. Can be used as user data/identifier + * for the image. + * + * @param name The name to assign the image + */ + public void setName(String name) { + this.name = name; + } + + /** + * Return a meaningful tagging name that has been assigned to this image. + * + * @return A name or null if the name hasn't been set + */ + public String getName() { + return name; + } + + /** + * Get a graphics context that can be used to draw to this image + * + * @return The graphics context used to render to this image + * @throws SlickException Indicates a failure to create a graphics context + */ + public Graphics getGraphics() throws SlickException { + return GraphicsFactory.getGraphicsForImage(this); + } + + /** + * Load the image + * + * @param in The input stream to read the image from + * @param ref The name that should be assigned to the image + * @param flipped True if the image should be flipped on the y-axis on load + * @param f The filter to use when scaling this image + * @param transparent The color to treat as transparent + * @throws SlickException Indicates a failure to load the image + */ + private void load(InputStream in, String ref, boolean flipped, int f, Color transparent) throws SlickException { + this.filter = f == FILTER_LINEAR ? SGL.GL_LINEAR : SGL.GL_NEAREST; + + try { + this.ref = ref; + int[] trans = null; + if (transparent != null) { + trans = new int[3]; + trans[0] = (int) (transparent.r * 255); + trans[1] = (int) (transparent.g * 255); + trans[2] = (int) (transparent.b * 255); + } + texture = InternalTextureLoader.get().getTexture(in, ref, flipped, filter, trans); + } catch (IOException e) { + Log.error(e); + throw new SlickException("Failed to load image from: "+ref, e); + } + } + + /** + * Bind to the texture of this image + */ + public void bind() { + texture.bind(); + } + + /** + * Reinitialise internal data + */ + protected void reinit() { + inited = false; + init(); + } + + /** + * Initialise internal data + */ + protected final void init() { + if (inited) { + return; + } + + inited = true; + if (texture != null) { + width = texture.getImageWidth(); + height = texture.getImageHeight(); + textureOffsetX = 0; + textureOffsetY = 0; + textureWidth = texture.getWidth(); + textureHeight = texture.getHeight(); + } + + initImpl(); + + centerX = width / 2f; + centerY = height / 2f; + } + + /** + * Hook for subclasses to perform initialisation + */ + protected void initImpl() { + + } + + /** + * Draw this image at the current location + */ + public void draw() { + draw(0,0); + } + + /** + * Draw the image based on it's center + * + * @param x The x coordinate to place the image's center at + * @param y The y coordinate to place the image's center at + */ + public void drawCentered(float x, float y) { + draw(x-(getWidth()/2),y-(getHeight()/2)); + } + + /** + * Draw this image at the specified location + * + * @param x The x location to draw the image at + * @param y The y location to draw the image at + */ + public void draw(float x, float y) { + init(); + draw(x,y,width,height); + } + + /** + * Draw this image at the specified location + * + * @param x The x location to draw the image at + * @param y The y location to draw the image at + * @param filter The color to filter with when drawing + */ + public void draw(float x, float y, Color filter) { + init(); + draw(x,y,width,height, filter); + } + + /** + * Draw this image as part of a collection of images + * + * @param x The x location to draw the image at + * @param y The y location to draw the image at + * @param width The width to render the image at + * @param height The height to render the image at + */ + public void drawEmbedded(float x,float y,float width,float height) { + init(); + + if (corners == null) { + GL.glTexCoord2f(textureOffsetX, textureOffsetY); + GL.glVertex3f(x, y, 0); + GL.glTexCoord2f(textureOffsetX, textureOffsetY + textureHeight); + GL.glVertex3f(x, y + height, 0); + GL.glTexCoord2f(textureOffsetX + textureWidth, textureOffsetY + + textureHeight); + GL.glVertex3f(x + width, y + height, 0); + GL.glTexCoord2f(textureOffsetX + textureWidth, textureOffsetY); + GL.glVertex3f(x + width, y, 0); + } else { + corners[TOP_LEFT].bind(); + GL.glTexCoord2f(textureOffsetX, textureOffsetY); + GL.glVertex3f(x, y, 0); + corners[BOTTOM_LEFT].bind(); + GL.glTexCoord2f(textureOffsetX, textureOffsetY + textureHeight); + GL.glVertex3f(x, y + height, 0); + corners[BOTTOM_RIGHT].bind(); + GL.glTexCoord2f(textureOffsetX + textureWidth, textureOffsetY + + textureHeight); + GL.glVertex3f(x + width, y + height, 0); + corners[TOP_RIGHT].bind(); + GL.glTexCoord2f(textureOffsetX + textureWidth, textureOffsetY); + GL.glVertex3f(x + width, y, 0); + } + } + + /** + * Get the x offset in texels into the source texture + * + * @return The x offset + */ + public float getTextureOffsetX() { + init(); + + return textureOffsetX; + } + + /** + * Get the y offset in texels into the source texture + * + * @return The y offset + */ + public float getTextureOffsetY() { + init(); + + return textureOffsetY; + } + + /** + * Get the width in texels into the source texture + * + * @return The width + */ + public float getTextureWidth() { + init(); + + return textureWidth; + } + + /** + * Get the height in texels into the source texture + * + * @return The height + */ + public float getTextureHeight() { + init(); + + return textureHeight; + } + + /** + * Draw the image with a given scale + * + * @param x The x position to draw the image at + * @param y The y position to draw the image at + * @param scale The scaling to apply + */ + public void draw(float x,float y,float scale) { + init(); + draw(x,y,width*scale,height*scale,Color.white); + } + + /** + * Draw the image with a given scale + * + * @param x The x position to draw the image at + * @param y The y position to draw the image at + * @param scale The scaling to apply + * @param filter The colour filter to adapt the image with + */ + public void draw(float x,float y,float scale,Color filter) { + init(); + draw(x,y,width*scale,height*scale,filter); + } + + /** + * Draw this image at a specified location and size + * + * @param x + * The x location to draw the image at + * @param y + * The y location to draw the image at + * @param width + * The width to render the image at + * @param height + * The height to render the image at + */ + public void draw(float x,float y,float width,float height) { + init(); + draw(x,y,width,height,Color.white); + } + + /** + * Draw this image at a specified location and size + * + * @param x The x location to draw the image at + * @param y The y location to draw the image at + * @param hshear The amount to shear the bottom points by horizontally + * @param vshear The amount to shear the right points by vertically + */ + public void drawSheared(float x,float y, float hshear, float vshear) { + this.drawSheared(x, y, hshear, vshear, Color.white); + } + /** + * Draw this image at a specified location and size + * + * @param x The x location to draw the image at + * @param y The y location to draw the image at + * @param hshear The amount to shear the bottom points by horizontally + * @param vshear The amount to shear the right points by vertically + * @param filter The colour filter to apply + */ + public void drawSheared(float x,float y, float hshear, float vshear, Color filter) { + if (alpha != 1) { + if (filter == null) { + filter = Color.white; + } + + filter = new Color(filter); + filter.a *= alpha; + } + if (filter != null) { + filter.bind(); + } + + texture.bind(); + + GL.glTranslatef(x, y, 0); + if (angle != 0) { + GL.glTranslatef(centerX, centerY, 0.0f); + GL.glRotatef(angle, 0.0f, 0.0f, 1.0f); + GL.glTranslatef(-centerX, -centerY, 0.0f); + } + + GL.glBegin(SGL.GL_QUADS); + init(); + + GL.glTexCoord2f(textureOffsetX, textureOffsetY); + GL.glVertex3f(0, 0, 0); + GL.glTexCoord2f(textureOffsetX, textureOffsetY + textureHeight); + GL.glVertex3f(hshear, height, 0); + GL.glTexCoord2f(textureOffsetX + textureWidth, textureOffsetY + + textureHeight); + GL.glVertex3f(width + hshear, height + vshear, 0); + GL.glTexCoord2f(textureOffsetX + textureWidth, textureOffsetY); + GL.glVertex3f(width, vshear, 0); + GL.glEnd(); + + if (angle != 0) { + GL.glTranslatef(centerX, centerY, 0.0f); + GL.glRotatef(-angle, 0.0f, 0.0f, 1.0f); + GL.glTranslatef(-centerX, -centerY, 0.0f); + } + GL.glTranslatef(-x, -y, 0); + } + + /** + * Draw this image at a specified location and size + * + * @param x The x location to draw the image at + * @param y The y location to draw the image at + * @param width The width to render the image at + * @param height The height to render the image at + * @param filter The color to filter with while drawing + */ + public void draw(float x,float y,float width,float height,Color filter) { + if (alpha != 1) { + if (filter == null) { + filter = Color.white; + } + + filter = new Color(filter); + filter.a *= alpha; + } + if (filter != null) { + filter.bind(); + } + + texture.bind(); + + GL.glTranslatef(x, y, 0); + if (angle != 0) { + GL.glTranslatef(centerX, centerY, 0.0f); + GL.glRotatef(angle, 0.0f, 0.0f, 1.0f); + GL.glTranslatef(-centerX, -centerY, 0.0f); + } + + GL.glBegin(SGL.GL_QUADS); + drawEmbedded(0,0,width,height); + GL.glEnd(); + + if (angle != 0) { + GL.glTranslatef(centerX, centerY, 0.0f); + GL.glRotatef(-angle, 0.0f, 0.0f, 1.0f); + GL.glTranslatef(-centerX, -centerY, 0.0f); + } + GL.glTranslatef(-x, -y, 0); + } + + /** + * Draw this image at a specified location and size as a silohette + * + * @param x The x location to draw the image at + * @param y The y location to draw the image at + * @param width The width to render the image at + * @param height The height to render the image at + */ + public void drawFlash(float x,float y,float width,float height) { + drawFlash(x,y,width,height,Color.white); + } + + /** + * Set the centre of the rotation when applied to this image + * + * @param x The x coordinate of center of rotation relative to the top left corner of the image + * @param y The y coordinate of center of rotation relative to the top left corner of the image + */ + public void setCenterOfRotation(float x, float y) { + centerX = x; + centerY = y; + } + + /** + * Get the x component of the center of rotation of this image + * + * @return The x component of the center of rotation + */ + public float getCenterOfRotationX() { + init(); + + return centerX; + } + + /** + * Get the y component of the center of rotation of this image + * + * @return The y component of the center of rotation + */ + public float getCenterOfRotationY() { + init(); + + return centerY; + } + + /** + * Draw this image at a specified location and size as a silohette + * + * @param x The x location to draw the image at + * @param y The y location to draw the image at + * @param width The width to render the image at + * @param height The height to render the image at + * @param col The color for the sillohette + */ + public void drawFlash(float x,float y,float width,float height, Color col) { + init(); + + col.bind(); + texture.bind(); + + if (GL.canSecondaryColor()) { + GL.glEnable(SGL.GL_COLOR_SUM_EXT); + GL.glSecondaryColor3ubEXT((byte)(col.r * 255), + (byte)(col.g * 255), + (byte)(col.b * 255)); + } + + GL.glTexEnvi(SGL.GL_TEXTURE_ENV, SGL.GL_TEXTURE_ENV_MODE, SGL.GL_MODULATE); + + GL.glTranslatef(x, y, 0); + if (angle != 0) { + GL.glTranslatef(centerX, centerY, 0.0f); + GL.glRotatef(angle, 0.0f, 0.0f, 1.0f); + GL.glTranslatef(-centerX, -centerY, 0.0f); + } + + GL.glBegin(SGL.GL_QUADS); + drawEmbedded(0,0,width,height); + GL.glEnd(); + + if (angle != 0) { + GL.glTranslatef(centerX, centerY, 0.0f); + GL.glRotatef(-angle, 0.0f, 0.0f, 1.0f); + GL.glTranslatef(-centerX, -centerY, 0.0f); + } + GL.glTranslatef(-x, -y, 0); + + if (GL.canSecondaryColor()) { + GL.glDisable(SGL.GL_COLOR_SUM_EXT); + } + } + + /** + * Draw this image at a specified location and size in a white silohette + * + * @param x The x location to draw the image at + * @param y The y location to draw the image at + */ + public void drawFlash(float x,float y) { + drawFlash(x,y,getWidth(),getHeight()); + } + + /** + * Set the angle to rotate this image to. The angle will be normalized to + * be 0 <= angle < 360. The image will be rotated around its center. + * + * @param angle The angle to be set + */ + public void setRotation(float angle) { + this.angle = angle % 360.0f; + } + + /** + * Get the current angle of rotation for this image. + * The image will be rotated around its center. + * + * @return The current angle. + */ + public float getRotation() { + return angle; + } + + /** + * Get the alpha value to use when rendering this image + * + * @return The alpha value to use when rendering this image + */ + public float getAlpha() { + return alpha; + } + + /** + * Set the alpha value to use when rendering this image + * + * @param alpha The alpha value to use when rendering this image + */ + public void setAlpha(float alpha) { + this.alpha = alpha; + } + + /** + * Add the angle provided to the current rotation. The angle will be normalized to + * be 0 <= angle < 360. The image will be rotated around its center. + * + * @param angle The angle to add. + */ + public void rotate(float angle) { + this.angle += angle; + this.angle = this.angle % 360; + } + + /** + * Get a sub-part of this image. Note that the create image retains a reference to the + * image data so should anything change it will affect sub-images too. + * + * @param x The x coordinate of the sub-image + * @param y The y coordinate of the sub-image + * @param width The width of the sub-image + * @param height The height of the sub-image + * @return The image represent the sub-part of this image + */ + public Image getSubImage(int x,int y,int width,int height) { + init(); + + float newTextureOffsetX = ((x / (float) this.width) * textureWidth) + textureOffsetX; + float newTextureOffsetY = ((y / (float) this.height) * textureHeight) + textureOffsetY; + float newTextureWidth = ((width / (float) this.width) * textureWidth); + float newTextureHeight = ((height / (float) this.height) * textureHeight); + + Image sub = new Image(); + sub.inited = true; + sub.texture = this.texture; + sub.textureOffsetX = newTextureOffsetX; + sub.textureOffsetY = newTextureOffsetY; + sub.textureWidth = newTextureWidth; + sub.textureHeight = newTextureHeight; + + sub.width = width; + sub.height = height; + sub.ref = ref; + sub.centerX = width / 2f; + sub.centerY = height / 2f; + + return sub; + } + + /** + * Draw a section of this image at a particular location and scale on the screen + * + * @param x The x position to draw the image + * @param y The y position to draw the image + * @param srcx The x position of the rectangle to draw from this image (i.e. relative to this image) + * @param srcy The y position of the rectangle to draw from this image (i.e. relative to this image) + * @param srcx2 The x position of the bottom right cornder of rectangle to draw from this image (i.e. relative to this image) + * @param srcy2 The t position of the bottom right cornder of rectangle to draw from this image (i.e. relative to this image) + */ + public void draw(float x, float y, float srcx, float srcy, float srcx2, float srcy2) { + draw(x,y,x+width,y+height,srcx,srcy,srcx2,srcy2); + } + + /** + * Draw a section of this image at a particular location and scale on the screen + * + * @param x The x position to draw the image + * @param y The y position to draw the image + * @param x2 The x position of the bottom right corner of the drawn image + * @param y2 The y position of the bottom right corner of the drawn image + * @param srcx The x position of the rectangle to draw from this image (i.e. relative to this image) + * @param srcy The y position of the rectangle to draw from this image (i.e. relative to this image) + * @param srcx2 The x position of the bottom right cornder of rectangle to draw from this image (i.e. relative to this image) + * @param srcy2 The t position of the bottom right cornder of rectangle to draw from this image (i.e. relative to this image) + */ + public void draw(float x, float y, float x2, float y2, float srcx, float srcy, float srcx2, float srcy2) { + draw(x,y,x2,y2,srcx,srcy,srcx2,srcy2,Color.white); + } + + /** + * Draw a section of this image at a particular location and scale on the screen + * + * @param x The x position to draw the image + * @param y The y position to draw the image + * @param x2 The x position of the bottom right corner of the drawn image + * @param y2 The y position of the bottom right corner of the drawn image + * @param srcx The x position of the rectangle to draw from this image (i.e. relative to this image) + * @param srcy The y position of the rectangle to draw from this image (i.e. relative to this image) + * @param srcx2 The x position of the bottom right cornder of rectangle to draw from this image (i.e. relative to this image) + * @param srcy2 The t position of the bottom right cornder of rectangle to draw from this image (i.e. relative to this image) + * @param filter The colour filter to apply when drawing + */ + public void draw(float x, float y, float x2, float y2, float srcx, float srcy, float srcx2, float srcy2, Color filter) { + init(); + + if (alpha != 1) { + if (filter == null) { + filter = Color.white; + } + + filter = new Color(filter); + filter.a *= alpha; + } + filter.bind(); + texture.bind(); + + GL.glTranslatef(x, y, 0); + if (angle != 0) { + GL.glTranslatef(centerX, centerY, 0.0f); + GL.glRotatef(angle, 0.0f, 0.0f, 1.0f); + GL.glTranslatef(-centerX, -centerY, 0.0f); + } + + GL.glBegin(SGL.GL_QUADS); + drawEmbedded(0,0,x2-x,y2-y,srcx,srcy,srcx2,srcy2); + GL.glEnd(); + + if (angle != 0) { + GL.glTranslatef(centerX, centerY, 0.0f); + GL.glRotatef(-angle, 0.0f, 0.0f, 1.0f); + GL.glTranslatef(-centerX, -centerY, 0.0f); + } + GL.glTranslatef(-x, -y, 0); + +// GL.glBegin(SGL.GL_QUADS); +// drawEmbedded(x,y,x2,y2,srcx,srcy,srcx2,srcy2); +// GL.glEnd(); + } + + /** + * Draw a section of this image at a particular location and scale on the screen, while this + * is image is "in use", i.e. between calls to startUse and endUse. + * + * @param x The x position to draw the image + * @param y The y position to draw the image + * @param x2 The x position of the bottom right corner of the drawn image + * @param y2 The y position of the bottom right corner of the drawn image + * @param srcx The x position of the rectangle to draw from this image (i.e. relative to this image) + * @param srcy The y position of the rectangle to draw from this image (i.e. relative to this image) + * @param srcx2 The x position of the bottom right cornder of rectangle to draw from this image (i.e. relative to this image) + * @param srcy2 The t position of the bottom right cornder of rectangle to draw from this image (i.e. relative to this image) + */ + public void drawEmbedded(float x, float y, float x2, float y2, float srcx, float srcy, float srcx2, float srcy2) { + drawEmbedded(x,y,x2,y2,srcx,srcy,srcx2,srcy2,null); + } + + /** + * Draw a section of this image at a particular location and scale on the screen, while this + * is image is "in use", i.e. between calls to startUse and endUse. + * + * @param x The x position to draw the image + * @param y The y position to draw the image + * @param x2 The x position of the bottom right corner of the drawn image + * @param y2 The y position of the bottom right corner of the drawn image + * @param srcx The x position of the rectangle to draw from this image (i.e. relative to this image) + * @param srcy The y position of the rectangle to draw from this image (i.e. relative to this image) + * @param srcx2 The x position of the bottom right cornder of rectangle to draw from this image (i.e. relative to this image) + * @param srcy2 The t position of the bottom right cornder of rectangle to draw from this image (i.e. relative to this image) + * @param filter The colour filter to apply when drawing + */ + public void drawEmbedded(float x, float y, float x2, float y2, float srcx, float srcy, float srcx2, float srcy2, Color filter) { + if (filter != null) { + filter.bind(); + } + + float mywidth = x2 - x; + float myheight = y2 - y; + float texwidth = srcx2 - srcx; + float texheight = srcy2 - srcy; + + float newTextureOffsetX = (((srcx) / (width)) * textureWidth) + + textureOffsetX; + float newTextureOffsetY = (((srcy) / (height)) * textureHeight) + + textureOffsetY; + float newTextureWidth = ((texwidth) / (width)) + * textureWidth; + float newTextureHeight = ((texheight) / (height)) + * textureHeight; + + GL.glTexCoord2f(newTextureOffsetX, newTextureOffsetY); + GL.glVertex3f(x,y, 0.0f); + GL.glTexCoord2f(newTextureOffsetX, newTextureOffsetY + + newTextureHeight); + GL.glVertex3f(x,(y + myheight), 0.0f); + GL.glTexCoord2f(newTextureOffsetX + newTextureWidth, + newTextureOffsetY + newTextureHeight); + GL.glVertex3f((x + mywidth),(y + myheight), 0.0f); + GL.glTexCoord2f(newTextureOffsetX + newTextureWidth, + newTextureOffsetY); + GL.glVertex3f((x + mywidth),y, 0.0f); + } + + /** + * Draw the image in a warper rectangle. The effects this can + * have are many and varied, might be interesting though. + * + * @param x1 The top left corner x coordinate + * @param y1 The top left corner y coordinate + * @param x2 The top right corner x coordinate + * @param y2 The top right corner y coordinate + * @param x3 The bottom right corner x coordinate + * @param y3 The bottom right corner y coordinate + * @param x4 The bottom left corner x coordinate + * @param y4 The bottom left corner y coordinate + */ + public void drawWarped(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4) { + Color.white.bind(); + texture.bind(); + + GL.glTranslatef(x1, y1, 0); + if (angle != 0) { + GL.glTranslatef(centerX, centerY, 0.0f); + GL.glRotatef(angle, 0.0f, 0.0f, 1.0f); + GL.glTranslatef(-centerX, -centerY, 0.0f); + } + + GL.glBegin(SGL.GL_QUADS); + init(); + + GL.glTexCoord2f(textureOffsetX, textureOffsetY); + GL.glVertex3f(0, 0, 0); + GL.glTexCoord2f(textureOffsetX, textureOffsetY + textureHeight); + GL.glVertex3f(x2 - x1, y2 - y1, 0); + GL.glTexCoord2f(textureOffsetX + textureWidth, textureOffsetY + + textureHeight); + GL.glVertex3f(x3 - x1, y3 - y1, 0); + GL.glTexCoord2f(textureOffsetX + textureWidth, textureOffsetY); + GL.glVertex3f(x4 - x1, y4 - y1, 0); + GL.glEnd(); + + if (angle != 0) { + GL.glTranslatef(centerX, centerY, 0.0f); + GL.glRotatef(-angle, 0.0f, 0.0f, 1.0f); + GL.glTranslatef(-centerX, -centerY, 0.0f); + } + GL.glTranslatef(-x1, -y1, 0); + } + + /** + * Get the width of this image + * + * @return The width of this image + */ + public int getWidth() { + init(); + return width; + } + + /** + * Get the height of this image + * + * @return The height of this image + */ + public int getHeight() { + init(); + return height; + } + + /** + * Get a copy of this image. This is a shallow copy and does not + * duplicate image adata. + * + * @return The copy of this image + */ + public Image copy() { + init(); + return getSubImage(0,0,width,height); + } + + /** + * Get a scaled copy of this image with a uniform scale + * + * @param scale The scale to apply + * @return The new scaled image + */ + public Image getScaledCopy(float scale) { + init(); + return getScaledCopy((int) (width*scale),(int) (height*scale)); + } + + /** + * Get a scaled copy of this image + * + * @param width The width of the copy + * @param height The height of the copy + * @return The new scaled image + */ + public Image getScaledCopy(int width, int height) { + init(); + Image image = copy(); + image.width = width; + image.height = height; + image.centerX = width / 2f; + image.centerY = height / 2f; + return image; + } + + /** + * Make sure the texture cordinates are inverse on the y axis + */ + public void ensureInverted() { + if (textureHeight > 0) { + textureOffsetY = textureOffsetY + textureHeight; + textureHeight = -textureHeight; + } + } + + /** + * Get a copy image flipped on potentially two axis + * + * @param flipHorizontal True if we want to flip the image horizontally + * @param flipVertical True if we want to flip the image vertically + * @return The flipped image instance + */ + public Image getFlippedCopy(boolean flipHorizontal, boolean flipVertical) { + init(); + Image image = copy(); + + if (flipHorizontal) { + image.textureOffsetX = textureOffsetX + textureWidth; + image.textureWidth = -textureWidth; + } + if (flipVertical) { + image.textureOffsetY = textureOffsetY + textureHeight; + image.textureHeight = -textureHeight; + } + + return image; + } + + /** + * End the use of this sprite sheet and release the lock. + * + * @see #startUse + */ + public void endUse() { + if (inUse != this) { + throw new RuntimeException("The sprite sheet is not currently in use"); + } + inUse = null; + GL.glEnd(); + } + + /** + * Start using this sheet. This method can be used for optimal rendering of a collection + * of sprites from a single sprite sheet. First, startUse(). Then render each sprite by + * calling renderInUse(). Finally, endUse(). Between start and end there can be no rendering + * of other sprites since the rendering is locked for this sprite sheet. + */ + public void startUse() { + if (inUse != null) { + throw new RuntimeException("Attempt to start use of a sprite sheet before ending use with another - see endUse()"); + } + inUse = this; + init(); + + Color.white.bind(); + texture.bind(); + GL.glBegin(SGL.GL_QUADS); + } + + /** + * @see java.lang.Object#toString() + */ + public String toString() { + init(); + + return "[Image "+ref+" "+width+"x"+height+" "+textureOffsetX+","+textureOffsetY+","+textureWidth+","+textureHeight+"]"; + } + + /** + * Get the OpenGL texture holding this image + * + * @return The OpenGL texture holding this image + */ + public Texture getTexture() { + return texture; + } + + /** + * Set the texture used by this image + * + * @param texture The texture used by this image + */ + public void setTexture(Texture texture) { + this.texture = texture; + reinit(); + } + + /** + * Translate an unsigned int into a signed integer + * + * @param b The byte to convert + * @return The integer value represented by the byte + */ + private int translate(byte b) { + if (b < 0) { + return 256 + b; + } + + return b; + } + + /** + * Get the colour of a pixel at a specified location in this image + * + * @param x The x coordinate of the pixel + * @param y The y coordinate of the pixel + * @return The Color of the pixel at the specified location + */ + public Color getColor(int x, int y) { + if (pixelData == null) { + pixelData = texture.getTextureData(); + } + + int xo = (int) (textureOffsetX * texture.getTextureWidth()); + int yo = (int) (textureOffsetY * texture.getTextureHeight()); + + if (textureWidth < 0) { + x = xo - x; + } else { + x = xo + x; + } + + if (textureHeight < 0) { + y = yo - y; + } else { + y = yo + y; + } + + int offset = x + (y * texture.getTextureWidth()); + offset *= texture.hasAlpha() ? 4 : 3; + + if (texture.hasAlpha()) { + return new Color(translate(pixelData[offset]),translate(pixelData[offset+1]), + translate(pixelData[offset+2]),translate(pixelData[offset+3])); + } else { + return new Color(translate(pixelData[offset]),translate(pixelData[offset+1]), + translate(pixelData[offset+2])); + } + } + + /** + * Check if this image has been destroyed + * + * @return True if this image has been destroyed + */ + public boolean isDestroyed() { + return destroyed; + } + + /** + * Destroy the image and release any native resources. + * Calls on a destroyed image have undefined results + * + * @throws SlickException Indicates a failure to release resources on the graphics card + */ + public void destroy() throws SlickException { + if (isDestroyed()) { + return; + } + + destroyed = true; + texture.release(); + GraphicsFactory.releaseGraphicsForImage(this); + } + + /** + * Flush the current pixel data to force a re-read next update + */ + public void flushPixelData() { + pixelData = null; + } +}