From 5e5a09ad83259ba6cab48bb87a07ffb30b1d7d83 Mon Sep 17 00:00:00 2001 From: Andrea Cavalli Date: Sat, 6 Oct 2018 16:37:44 +0200 Subject: [PATCH 01/18] Disabled debug mode for TeaVM --- .../main/java/it/cavallium/warppi/Engine.java | 9 +- .../java/it/cavallium/warppi/Platform.java | 2 + .../it/cavallium/warppi/device/Keyboard.java | 377 ++++++++++-------- .../warppi/device/PIHardwareDisplay.java | 2 +- .../warppi/extra/mario/MarioScreen.java | 44 +- .../warppi/extra/tetris/TetrisGame.java | 12 +- .../warppi/extra/tetris/TetrisScreen.java | 170 ++------ core/src/main/resources/tetrisskin.png | Bin 0 -> 29408 bytes core/src/main/resources/tetrisskin.xcf | Bin 0 -> 96275 bytes .../warppi/desktop/DesktopPlatform.java | 11 + .../graphicengine/impl/swing/SwingWindow.java | 13 +- .../warppi/hardware/HardwarePlatform.java | 7 + .../gui/graphicengine/html/HtmlEngine.java | 96 +++-- .../gui/graphicengine/html/HtmlRenderer.java | 2 +- .../cavallium/warppi/teavm/TeaVMPlatform.java | 5 + .../cavallium/warppi/teavm/TeaVMSettings.java | 2 +- 16 files changed, 379 insertions(+), 373 deletions(-) create mode 100644 core/src/main/resources/tetrisskin.png create mode 100644 core/src/main/resources/tetrisskin.xcf diff --git a/core/src/main/java/it/cavallium/warppi/Engine.java b/core/src/main/java/it/cavallium/warppi/Engine.java index 39cb380c..ef863750 100644 --- a/core/src/main/java/it/cavallium/warppi/Engine.java +++ b/core/src/main/java/it/cavallium/warppi/Engine.java @@ -89,12 +89,17 @@ public class Engine { } Engine.platform.getConsoleUtils().out().println(ConsoleUtils.OUTPUTLEVEL_DEBUG_MIN, args); checkDeviceType(); - if (Engine.getPlatform().isRunningOnRaspberry() && args.isRaspberryModeAllowed()) { + if (args.isRaspberryModeAllowed() == false) { + Engine.getPlatform().setRunningOnRaspberry(false); + } + if (Engine.getPlatform().isRunningOnRaspberry()) { Engine.getPlatform().getGpio().wiringPiSetupPhys(); Engine.getPlatform().getGpio().pinMode(12, Engine.getPlatform().getGpio().valuePwmOutput()); } else { StaticVars.screenPos = new int[] { 0, 0 }; - Engine.getPlatform().getSettings().setDebugEnabled(true); + if (Engine.getPlatform().isJavascript() == false) { + Engine.getPlatform().getSettings().setDebugEnabled(true); + } } } diff --git a/core/src/main/java/it/cavallium/warppi/Platform.java b/core/src/main/java/it/cavallium/warppi/Platform.java index bf762917..5951cfaf 100644 --- a/core/src/main/java/it/cavallium/warppi/Platform.java +++ b/core/src/main/java/it/cavallium/warppi/Platform.java @@ -36,6 +36,8 @@ public interface Platform { boolean isJavascript(); + void setRunningOnRaspberry(boolean b); + boolean isRunningOnRaspberry(); String getOsName(); diff --git a/core/src/main/java/it/cavallium/warppi/device/Keyboard.java b/core/src/main/java/it/cavallium/warppi/device/Keyboard.java index f55af5ee..8aa67e43 100644 --- a/core/src/main/java/it/cavallium/warppi/device/Keyboard.java +++ b/core/src/main/java/it/cavallium/warppi/device/Keyboard.java @@ -12,6 +12,7 @@ import it.cavallium.warppi.event.KeyPressedEvent; import it.cavallium.warppi.event.KeyReleasedEvent; import it.cavallium.warppi.event.KeyboardEventListener; import it.cavallium.warppi.extra.mario.MarioScreen; +import it.cavallium.warppi.extra.tetris.TetrisScreen; import it.cavallium.warppi.gui.GUIErrorMessage; import it.cavallium.warppi.gui.screens.KeyboardDebugScreen; import it.cavallium.warppi.gui.screens.Screen; @@ -41,15 +42,15 @@ public class Keyboard { public synchronized void startKeyboard() { final Thread kt = new Thread(() -> { - if (Engine.getPlatform().getSettings().isDebugEnabled()) { + if (Engine.getPlatform().isRunningOnRaspberry() == false) { try { while (true) { if (Keyboard.debugKeyCode != -1) { - Keyboard.debugKeyPressed(Keyboard.debugKeyCode); + Keyboard.debugKey(Keyboard.debugKeyCode, false); Keyboard.debugKeyCode = -1; } if (Keyboard.debugKeyCodeRelease != -1) { - Keyboard.debugKeyReleased(Keyboard.debugKeyCodeRelease); + Keyboard.debugKey(Keyboard.debugKeyCodeRelease, true); Keyboard.debugKeyCodeRelease = -1; } Thread.sleep(50); @@ -88,9 +89,9 @@ public class Keyboard { if (data[row] == true && Keyboard.precedentStates[row][col] == false) { // System.out.println("Pressed button at " + (row + 1) + ", " + (col + 1)); // KeyboardDebugScreen.log("Pressed button at " + (row + 1) + ", " + (col + 1)); - Keyboard.keyPressedRaw(row, col); + Keyboard.keyRaw(row, col, false); } else if (data[row] == false && Keyboard.precedentStates[row][col] == true) { - Keyboard.keyReleasedRaw(row, col); + Keyboard.keyRaw(row, col, true); } // KeyboardDebugScreen.log("Released button at " + (row + 1) + ", " + (col + 1)); Keyboard.precedentStates[row][col] = data[row]; @@ -105,123 +106,141 @@ public class Keyboard { kt.start(); } - public static void debugKeyPressed(final int keyCode) { + /** + * + * @param k + * @param released true: released, false: pressed + */ + private static void debugKey(Key k, boolean released) { + if (released) { + Keyboard.keyReleased(k); + } else { + Keyboard.keyPressed(k); + } + } + + /** + * + * @param keyCode + * @param released true: released, false: pressed + */ + public static void debugKey(final int keyCode, boolean released) { switch (keyCode) { case KeyEvent.VK_ESCAPE: - Keyboard.keyPressed(Key.BACK); + debugKey(Key.BACK, released); break; case KeyEvent.VK_S: if (Keyboard.shift) { - Keyboard.keyPressed(Key.ARCSINE); + debugKey(Key.ARCSINE, released); } else if (Keyboard.alpha) { - Keyboard.keyPressed(Key.LETTER_S); + debugKey(Key.LETTER_S, released); } else { - Keyboard.keyPressed(Key.SINE); + debugKey(Key.SINE, released); } break; case KeyEvent.VK_C: if (Keyboard.shift) { - Keyboard.keyPressed(Key.ARCCOSINE); + debugKey(Key.ARCCOSINE, released); } else if (Keyboard.alpha) { - Keyboard.keyPressed(Key.LETTER_C); + debugKey(Key.LETTER_C, released); } else { - Keyboard.keyPressed(Key.COSINE); + debugKey(Key.COSINE, released); } break; case KeyEvent.VK_T: if (Keyboard.shift) { - Keyboard.keyPressed(Key.ARCTANGENT); + debugKey(Key.ARCTANGENT, released); } else if (Keyboard.alpha) { - Keyboard.keyPressed(Key.LETTER_T); + debugKey(Key.LETTER_T, released); } else { - Keyboard.keyPressed(Key.TANGENT); + debugKey(Key.TANGENT, released); } break; case KeyEvent.VK_D: if (!Keyboard.shift && !Keyboard.alpha) { - Keyboard.keyPressed(Key.debug_DEG); + debugKey(Key.debug_DEG, released); } else if (Keyboard.alpha) { - Keyboard.keyPressed(Key.LETTER_D); + debugKey(Key.LETTER_D, released); } else { - Keyboard.keyPressed(Key.NONE); + debugKey(Key.NONE, released); } break; case KeyEvent.VK_R: if (!Keyboard.shift && !Keyboard.alpha) { - Keyboard.keyPressed(Key.debug_RAD); + debugKey(Key.debug_RAD, released); } else if (Keyboard.alpha) { - Keyboard.keyPressed(Key.LETTER_R); + debugKey(Key.LETTER_R, released); } else { - Keyboard.keyPressed(Key.NONE); + debugKey(Key.NONE, released); } break; case KeyEvent.VK_G: if (!Keyboard.shift && !Keyboard.alpha) { - Keyboard.keyPressed(Key.debug_GRA); + debugKey(Key.debug_GRA, released); } else if (Keyboard.alpha) { - Keyboard.keyPressed(Key.LETTER_G); + debugKey(Key.LETTER_G, released); } else { - Keyboard.keyPressed(Key.NONE); + debugKey(Key.NONE, released); } break; case KeyEvent.VK_X: if (Keyboard.alpha) { - Keyboard.keyPressed(Key.LETTER_X); + debugKey(Key.LETTER_X, released); } else { - Keyboard.keyPressed(Key.NONE); + debugKey(Key.NONE, released); } break; case KeyEvent.VK_P: if (Keyboard.shift) { - Keyboard.keyPressed(Key.NONE); + debugKey(Key.NONE, released); } else if (Keyboard.alpha) { - Keyboard.keyPressed(Key.LETTER_P); + debugKey(Key.LETTER_P, released); } else { - Keyboard.keyPressed(Key.PI); + debugKey(Key.PI, released); } break; case KeyEvent.VK_E: if (Keyboard.shift) { - Keyboard.keyPressed(Key.NONE); + debugKey(Key.NONE, released); } else if (Keyboard.alpha) { - Keyboard.keyPressed(Key.LETTER_E); + debugKey(Key.LETTER_E, released); } else { - Keyboard.keyPressed(Key.EULER_NUMBER); + debugKey(Key.EULER_NUMBER, released); } break; case KeyEvent.VK_Y: if (Keyboard.alpha) { - Keyboard.keyPressed(Key.LETTER_Y); + debugKey(Key.LETTER_Y, released); } else { - Keyboard.keyPressed(Key.NONE); + debugKey(Key.NONE, released); } break; case KeyEvent.VK_B: if (Keyboard.shift) { - Keyboard.keyPressed(Key.BRIGHTNESS_CYCLE_REVERSE); + debugKey(Key.BRIGHTNESS_CYCLE_REVERSE, released); } else if (!Keyboard.shift && !Keyboard.alpha) { - Keyboard.keyPressed(Key.BRIGHTNESS_CYCLE); + debugKey(Key.BRIGHTNESS_CYCLE, released); } else { - Keyboard.keyPressed(Key.LETTER_B); + debugKey(Key.LETTER_B, released); } break; case KeyEvent.VK_L: if (Keyboard.shift) { - Keyboard.keyPressed(Key.LOGARITHM); + debugKey(Key.LOGARITHM, released); } else if (Keyboard.alpha) { - Keyboard.keyPressed(Key.LETTER_L); + debugKey(Key.LETTER_L, released); } else { - Keyboard.keyPressed(Key.LOGARITHM); + debugKey(Key.LOGARITHM, released); } break; case KeyboardJogampValues.VK_ENTER: case KeyEvent.VK_ENTER: if (Keyboard.shift) { - Keyboard.keyPressed(Key.STEP); + debugKey(Key.STEP, released); } else if (!Keyboard.shift && !Keyboard.alpha) { - Keyboard.keyPressed(Key.SIMPLIFY); + debugKey(Key.SIMPLIFY, released); } else { - Keyboard.keyPressed(Key.NONE); + debugKey(Key.NONE, released); } int row = 2; int col = 1; @@ -229,134 +248,134 @@ public class Keyboard { break; case KeyEvent.VK_1: if (!Keyboard.shift && !Keyboard.alpha) { - Keyboard.keyPressed(Key.NUM1); + debugKey(Key.NUM1, released); } else { - Keyboard.keyPressed(Key.NONE); + debugKey(Key.NONE, released); } break; case KeyEvent.VK_2: if (!Keyboard.shift && !Keyboard.alpha) { - Keyboard.keyPressed(Key.NUM2); + debugKey(Key.NUM2, released); } else { - Keyboard.keyPressed(Key.NONE); + debugKey(Key.NONE, released); } break; case KeyEvent.VK_3: if (!Keyboard.shift && !Keyboard.alpha) { - Keyboard.keyPressed(Key.NUM3); + debugKey(Key.NUM3, released); } else { - Keyboard.keyPressed(Key.NONE); + debugKey(Key.NONE, released); } break; case KeyEvent.VK_4: if (!Keyboard.shift && !Keyboard.alpha) { - Keyboard.keyPressed(Key.NUM4); + debugKey(Key.NUM4, released); } else { - Keyboard.keyPressed(Key.NONE); + debugKey(Key.NONE, released); } break; case KeyEvent.VK_5: if (!Keyboard.shift && !Keyboard.alpha) { - Keyboard.keyPressed(Key.NUM5); + debugKey(Key.NUM5, released); } else { - Keyboard.keyPressed(Key.NONE); + debugKey(Key.NONE, released); } break; case KeyEvent.VK_6: if (!Keyboard.shift && !Keyboard.alpha) { - Keyboard.keyPressed(Key.NUM6); + debugKey(Key.NUM6, released); } else { - Keyboard.keyPressed(Key.NONE); + debugKey(Key.NONE, released); } break; case KeyEvent.VK_7: if (!Keyboard.shift && !Keyboard.alpha) { - Keyboard.keyPressed(Key.NUM7); + debugKey(Key.NUM7, released); } else if (Keyboard.shift) { - if (Engine.getPlatform().getSettings().isDebugEnabled()) { - Keyboard.keyPressed(Key.DIVIDE); + if (Engine.getPlatform().isRunningOnRaspberry() == false) { + debugKey(Key.DIVIDE, released); } } break; case KeyEvent.VK_8: if (!Keyboard.shift && !Keyboard.alpha) { - Keyboard.keyPressed(Key.NUM8); + debugKey(Key.NUM8, released); } else if (Keyboard.shift) { - Keyboard.keyPressed(Key.PARENTHESIS_OPEN); + debugKey(Key.PARENTHESIS_OPEN, released); } else { - Keyboard.keyPressed(Key.NONE); + debugKey(Key.NONE, released); } break; case KeyEvent.VK_9: if (!Keyboard.shift && !Keyboard.alpha) { - Keyboard.keyPressed(Key.NUM9); + debugKey(Key.NUM9, released); } else if (Keyboard.shift) { - Keyboard.keyPressed(Key.PARENTHESIS_CLOSE); + debugKey(Key.PARENTHESIS_CLOSE, released); } else { - Keyboard.keyPressed(Key.NONE); + debugKey(Key.NONE, released); } break; case KeyEvent.VK_0: if (!Keyboard.shift && !Keyboard.alpha) { - Keyboard.keyPressed(Key.NUM0); + debugKey(Key.NUM0, released); } else if (Keyboard.shift) { - Keyboard.keyPressed(Key.EQUAL); + debugKey(Key.EQUAL, released); } else { - Keyboard.keyPressed(Key.NONE); + debugKey(Key.NONE, released); } break; case KeyEvent.VK_M: if (!Keyboard.shift && !Keyboard.alpha) { - Keyboard.keyPressed(Key.SURD_MODE); + debugKey(Key.SURD_MODE, released); } else if (Keyboard.shift) { - Keyboard.keyPressed(Key.NONE); + debugKey(Key.NONE, released); } else { - Keyboard.keyPressed(Key.LETTER_M); + debugKey(Key.LETTER_M, released); } break; case KeyboardJogampValues.VK_ADD: case KeyEvent.VK_ADD: if (!Keyboard.shift && !Keyboard.alpha) { - Keyboard.keyPressed(Key.PLUS); + debugKey(Key.PLUS, released); } else if (Keyboard.shift) { - Keyboard.keyPressed(Key.PLUS_MINUS); + debugKey(Key.PLUS_MINUS, released); } else { - Keyboard.keyPressed(Key.NONE); + debugKey(Key.NONE, released); } break; case KeyboardJogampValues.VK_SUBTRACT: case KeyEvent.VK_SUBTRACT: if (!Keyboard.shift && !Keyboard.alpha) { - Keyboard.keyPressed(Key.MINUS); + debugKey(Key.MINUS, released); } else { - Keyboard.keyPressed(Key.NONE); + debugKey(Key.NONE, released); } break; case KeyboardJogampValues.VK_MULTIPLY: case KeyEvent.VK_MULTIPLY: if (!Keyboard.shift && !Keyboard.alpha) { - Keyboard.keyPressed(Key.MULTIPLY); + debugKey(Key.MULTIPLY, released); } else { - Keyboard.keyPressed(Key.NONE); + debugKey(Key.NONE, released); } break; case KeyboardJogampValues.VK_DIVIDE: case KeyEvent.VK_DIVIDE: if (!Keyboard.shift && !Keyboard.alpha) { - Keyboard.keyPressed(Key.DIVIDE); + debugKey(Key.DIVIDE, released); } else { - Keyboard.keyPressed(Key.NONE); + debugKey(Key.NONE, released); } break; case KeyEvent.VK_BACK_SPACE: - Keyboard.keyPressed(Key.DELETE); + debugKey(Key.DELETE, released); break; case KeyboardJogampValues.VK_DELETE: case KeyEvent.VK_DELETE: if (!Keyboard.shift && !Keyboard.alpha) { - Keyboard.keyPressed(Key.RESET); + debugKey(Key.RESET, released); } else { - Keyboard.keyPressed(Key.NONE); + debugKey(Key.NONE, released); } break; case KeyboardJogampValues.VK_LEFT: @@ -366,9 +385,9 @@ public class Keyboard { col = 3; Keyboard.debugKeysDown[row - 1][col - 1] = true; if (!Keyboard.shift && !Keyboard.alpha) { - Keyboard.keyPressed(Key.LEFT); + debugKey(Key.LEFT, released); } else { - Keyboard.keyPressed(Key.NONE); + debugKey(Key.NONE, released); } break; case KeyboardJogampValues.VK_RIGHT: @@ -378,9 +397,9 @@ public class Keyboard { col = 5; Keyboard.debugKeysDown[row - 1][col - 1] = true; if (!Keyboard.shift && !Keyboard.alpha) { - Keyboard.keyPressed(Key.RIGHT); + debugKey(Key.RIGHT, released); } else { - Keyboard.keyPressed(Key.NONE); + debugKey(Key.NONE, released); } break; case KeyboardJogampValues.VK_UP: @@ -390,9 +409,9 @@ public class Keyboard { col = 4; Keyboard.debugKeysDown[row - 1][col - 1] = true; if (!Keyboard.shift && !Keyboard.alpha) { - Keyboard.keyPressed(Key.UP); + debugKey(Key.UP, released); } else { - Keyboard.keyPressed(Key.NONE); + debugKey(Key.NONE, released); } break; case KeyboardJogampValues.VK_DOWN: @@ -402,9 +421,9 @@ public class Keyboard { col = 4; Keyboard.debugKeysDown[row - 1][col - 1] = true; if (!Keyboard.shift && !Keyboard.alpha) { - Keyboard.keyPressed(Key.DOWN); + debugKey(Key.DOWN, released); } else { - Keyboard.keyPressed(Key.NONE); + debugKey(Key.NONE, released); } break; case (short) 12: @@ -413,199 +432,199 @@ public class Keyboard { col = 4; Keyboard.debugKeysDown[row - 1][col - 1] = true; if (!Keyboard.shift && !Keyboard.alpha) { - Keyboard.keyPressed(Key.OK); + debugKey(Key.OK, released); } else { - Keyboard.keyPressed(Key.NONE); + debugKey(Key.NONE, released); } break; case KeyboardJogampValues.VK_NUMPAD4: case KeyEvent.VK_NUMPAD4: if (!Keyboard.shift && !Keyboard.alpha) { - Keyboard.keyPressed(Key.HISTORY_BACK); + debugKey(Key.HISTORY_BACK, released); } else { - Keyboard.keyPressed(Key.NONE); + debugKey(Key.NONE, released); } break; case KeyboardJogampValues.VK_NUMPAD6: case KeyEvent.VK_NUMPAD6: if (!Keyboard.shift && !Keyboard.alpha) { - Keyboard.keyPressed(Key.HISTORY_FORWARD); + debugKey(Key.HISTORY_FORWARD, released); } else { - Keyboard.keyPressed(Key.NONE); + debugKey(Key.NONE, released); } break; case KeyEvent.VK_PERIOD: if (!Keyboard.shift && !Keyboard.alpha) { - Keyboard.keyPressed(Key.DOT); + debugKey(Key.DOT, released); } else { - Keyboard.keyPressed(Key.NONE); + debugKey(Key.NONE, released); } break; case KeyEvent.VK_A: if (!Keyboard.shift && !Keyboard.alpha) { - Keyboard.keyPressed(Key.NONE); + debugKey(Key.NONE, released); } else if (Keyboard.alpha && !Keyboard.shift) { - Keyboard.keyPressed(Key.LETTER_A); + debugKey(Key.LETTER_A, released); } else if (Keyboard.shift && !Keyboard.alpha) { - Keyboard.keyPressed(Key.NONE); + debugKey(Key.NONE, released); } else { - Keyboard.keyPressed(Key.NONE); + debugKey(Key.NONE, released); } break; case KeyEvent.VK_F: if (!Keyboard.shift && !Keyboard.alpha) { - Keyboard.keyPressed(Key.NONE); + debugKey(Key.NONE, released); } else if (Keyboard.alpha && !Keyboard.shift) { - Keyboard.keyPressed(Key.LETTER_F); + debugKey(Key.LETTER_F, released); } else if (Keyboard.shift && !Keyboard.alpha) { - Keyboard.keyPressed(Key.NONE); + debugKey(Key.NONE, released); } else { - Keyboard.keyPressed(Key.NONE); + debugKey(Key.NONE, released); } break; case KeyEvent.VK_H: if (!Keyboard.shift && !Keyboard.alpha) { - Keyboard.keyPressed(Key.NONE); + debugKey(Key.NONE, released); } else if (Keyboard.alpha && !Keyboard.shift) { - Keyboard.keyPressed(Key.LETTER_H); + debugKey(Key.LETTER_H, released); } else if (Keyboard.shift && !Keyboard.alpha) { - Keyboard.keyPressed(Key.NONE); + debugKey(Key.NONE, released); } else { - Keyboard.keyPressed(Key.NONE); + debugKey(Key.NONE, released); } break; case KeyEvent.VK_I: if (!Keyboard.shift && !Keyboard.alpha) { - Keyboard.keyPressed(Key.NONE); + debugKey(Key.NONE, released); } else if (Keyboard.alpha && !Keyboard.shift) { - Keyboard.keyPressed(Key.LETTER_I); + debugKey(Key.LETTER_I, released); } else if (Keyboard.shift && !Keyboard.alpha) { - Keyboard.keyPressed(Key.NONE); + debugKey(Key.NONE, released); } else { - Keyboard.keyPressed(Key.NONE); + debugKey(Key.NONE, released); } break; case KeyEvent.VK_J: if (!Keyboard.shift && !Keyboard.alpha) { - Keyboard.keyPressed(Key.NONE); + debugKey(Key.NONE, released); } else if (Keyboard.alpha && !Keyboard.shift) { - Keyboard.keyPressed(Key.LETTER_J); + debugKey(Key.LETTER_J, released); } else if (Keyboard.shift && !Keyboard.alpha) { - Keyboard.keyPressed(Key.NONE); + debugKey(Key.NONE, released); } else { - Keyboard.keyPressed(Key.NONE); + debugKey(Key.NONE, released); } break; case KeyEvent.VK_K: if (!Keyboard.shift && !Keyboard.alpha) { - Keyboard.keyPressed(Key.NONE); + debugKey(Key.NONE, released); } else if (Keyboard.alpha && !Keyboard.shift) { - Keyboard.keyPressed(Key.LETTER_K); + debugKey(Key.LETTER_K, released); } else if (Keyboard.shift && !Keyboard.alpha) { - Keyboard.keyPressed(Key.NONE); + debugKey(Key.NONE, released); } else { - Keyboard.keyPressed(Key.NONE); + debugKey(Key.NONE, released); } break; case KeyEvent.VK_N: if (!Keyboard.shift && !Keyboard.alpha) { - Keyboard.keyPressed(Key.NONE); + debugKey(Key.NONE, released); } else if (Keyboard.alpha && !Keyboard.shift) { - Keyboard.keyPressed(Key.LETTER_N); + debugKey(Key.LETTER_N, released); } else if (Keyboard.shift && !Keyboard.alpha) { - Keyboard.keyPressed(Key.NONE); + debugKey(Key.NONE, released); } else { - Keyboard.keyPressed(Key.NONE); + debugKey(Key.NONE, released); } break; case KeyEvent.VK_O: if (!Keyboard.shift && !Keyboard.alpha) { - Keyboard.keyPressed(Key.NONE); + debugKey(Key.NONE, released); } else if (Keyboard.alpha && !Keyboard.shift) { - Keyboard.keyPressed(Key.LETTER_O); + debugKey(Key.LETTER_O, released); } else if (Keyboard.shift && !Keyboard.alpha) { - Keyboard.keyPressed(Key.NONE); + debugKey(Key.NONE, released); } else { - Keyboard.keyPressed(Key.NONE); + debugKey(Key.NONE, released); } break; case KeyEvent.VK_Q: if (!Keyboard.shift && !Keyboard.alpha) { - Keyboard.keyPressed(Key.NONE); + debugKey(Key.NONE, released); } else if (Keyboard.alpha && !Keyboard.shift) { - Keyboard.keyPressed(Key.LETTER_Q); + debugKey(Key.LETTER_Q, released); } else if (Keyboard.shift && !Keyboard.alpha) { - Keyboard.keyPressed(Key.NONE); + debugKey(Key.NONE, released); } else { - Keyboard.keyPressed(Key.NONE); + debugKey(Key.NONE, released); } break; case KeyEvent.VK_U: if (!Keyboard.shift && !Keyboard.alpha) { - Keyboard.keyPressed(Key.NONE); + debugKey(Key.NONE, released); } else if (Keyboard.alpha && !Keyboard.shift) { - Keyboard.keyPressed(Key.LETTER_U); + debugKey(Key.LETTER_U, released); } else if (Keyboard.shift && !Keyboard.alpha) { - Keyboard.keyPressed(Key.NONE); + debugKey(Key.NONE, released); } else { - Keyboard.keyPressed(Key.NONE); + debugKey(Key.NONE, released); } break; case KeyEvent.VK_V: if (!Keyboard.shift && !Keyboard.alpha) { - Keyboard.keyPressed(Key.NONE); + debugKey(Key.NONE, released); } else if (Keyboard.alpha && !Keyboard.shift) { - Keyboard.keyPressed(Key.LETTER_V); + debugKey(Key.LETTER_V, released); } else if (Keyboard.shift && !Keyboard.alpha) { - Keyboard.keyPressed(Key.NONE); + debugKey(Key.NONE, released); } else { - Keyboard.keyPressed(Key.NONE); + debugKey(Key.NONE, released); } break; case KeyEvent.VK_W: if (!Keyboard.shift && !Keyboard.alpha) { - Keyboard.keyPressed(Key.NONE); + debugKey(Key.NONE, released); } else if (Keyboard.alpha && !Keyboard.shift) { - Keyboard.keyPressed(Key.LETTER_W); + debugKey(Key.LETTER_W, released); } else if (Keyboard.shift && !Keyboard.alpha) { - Keyboard.keyPressed(Key.NONE); + debugKey(Key.NONE, released); } else { - Keyboard.keyPressed(Key.NONE); + debugKey(Key.NONE, released); } break; case KeyEvent.VK_Z: if (!Keyboard.shift && !Keyboard.alpha) { - Keyboard.keyPressed(Key.NONE); + debugKey(Key.NONE, released); } else if (Keyboard.alpha && !Keyboard.shift) { - Keyboard.keyPressed(Key.LETTER_Z); + debugKey(Key.LETTER_Z, released); } else if (Keyboard.shift && !Keyboard.alpha) { - Keyboard.keyPressed(Key.ZOOM_MODE); + debugKey(Key.ZOOM_MODE, released); } else { - Keyboard.keyPressed(Key.NONE); + debugKey(Key.NONE, released); } break; case KeyboardJogampValues.VK_SHIFT: case KeyEvent.VK_SHIFT: - Keyboard.keyPressed(Key.SHIFT); + debugKey(Key.SHIFT, released); break; case KeyEvent.VK_CONTROL: - Keyboard.keyPressed(Key.ALPHA); + debugKey(Key.ALPHA, released); break; case KeyboardJogampValues.VK_NUMPAD1: case KeyEvent.VK_NUMPAD1: - Keyboard.keyPressed(Key.SQRT); + debugKey(Key.SQRT, released); break; case KeyboardJogampValues.VK_NUMPAD2: case KeyEvent.VK_NUMPAD2: - Keyboard.keyPressed(Key.ROOT); + debugKey(Key.ROOT, released); break; case KeyboardJogampValues.VK_NUMPAD3: case KeyEvent.VK_NUMPAD3: - Keyboard.keyPressed(Key.POWER_OF_2); + debugKey(Key.POWER_OF_2, released); break; case KeyboardJogampValues.VK_NUMPAD5: case KeyEvent.VK_NUMPAD5: - Keyboard.keyPressed(Key.POWER_OF_x); + debugKey(Key.POWER_OF_x, released); break; } } @@ -655,22 +674,21 @@ public class Keyboard { } } + /** + * + * @param row + * @param col + * @return + */ + @Deprecated public static boolean isKeyDown(final int row, final int col) { - if (Engine.getPlatform().getSettings().isDebugEnabled() == false) { + if (Engine.getPlatform().isRunningOnRaspberry()) { return Keyboard.precedentStates[row - 1][col - 1]; } else { return Keyboard.debugKeysDown[row - 1][col - 1]; } } - public synchronized static void keyReleasedRaw(final int row, final int col) { -// KeyboardDebugScreen.keyX = row; -// KeyboardDebugScreen.keyY = col; - if (row == 1 && col == 1) { - //keyReleased(Key.BRIGHTNESS_CYCLE); - } - } - static final String[][][] KeyLabelsMap = /* [ROW, COLUMN, (0:normal 1:shift 2:alpha)] */ { { /* ROW 0 */ { "⇪", "⇪", null }, /* 0,0 */ @@ -687,8 +705,8 @@ public class Keyboard { { "⇦", null, null }, /* 1,2 */ { "OK", null, null }, /* 1,3 */ { "⇨", null, null }, /* 1,4 */ - { "↤", null, null }, /* 1,5 */ - { "↦", null, null }, /* 1,6 */ + { "≪", null, null }, /* 1,5 */ + { "≫", null, null }, /* 1,6 */ { "", null, null } /* 1,7 */ }, { /* ROW 2 */ { "", null, null }, /* 2,0 */ @@ -850,19 +868,33 @@ public class Keyboard { } } - public static synchronized void keyPressedRaw(final int row, final int col) { + /** + * + * @param row + * @param col + * @param released true: released, false: pressed + */ + public static synchronized void keyRaw(final int row, final int col, final boolean released) { // KeyboardDebugScreen.keyX = row; // KeyboardDebugScreen.keyY = col; final Key k = Keyboard.keyMap[row][col][Keyboard.shift ? 1 : Keyboard.alpha ? 2 : 0]; if (k != null) { - Keyboard.keyPressed(k); + if (released) { + Keyboard.keyReleased(k); + } else { + Keyboard.keyPressed(k); + } } else { - Keyboard.keyPressed(Key.NONE); + if (released) { + Keyboard.keyReleased(Key.NONE); + } else { + Keyboard.keyPressed(Key.NONE); + } } } public static void stopKeyboard() { - if (Engine.getPlatform().getSettings().isDebugEnabled() == false) { + if (Engine.getPlatform().isRunningOnRaspberry()) { Engine.getPlatform().getGpio().digitalWrite(33, false); Engine.getPlatform().getGpio().digitalWrite(35, false); Engine.getPlatform().getGpio().digitalWrite(36, false); @@ -900,6 +932,7 @@ public class Keyboard { case NONE: break; case BRIGHTNESS_CYCLE: + Engine.INSTANCE.getHardwareDevice().getDisplayManager().setScreen(new TetrisScreen()); //TODO: rimuovere: prova Engine.INSTANCE.getHardwareDevice().getDisplayManager().cycleBrightness(false); refresh = true; break; diff --git a/core/src/main/java/it/cavallium/warppi/device/PIHardwareDisplay.java b/core/src/main/java/it/cavallium/warppi/device/PIHardwareDisplay.java index 8a506681..f652581e 100644 --- a/core/src/main/java/it/cavallium/warppi/device/PIHardwareDisplay.java +++ b/core/src/main/java/it/cavallium/warppi/device/PIHardwareDisplay.java @@ -13,7 +13,7 @@ public class PIHardwareDisplay implements HardwareDisplay { @Override public void setBrightness(final double value) { - if (Engine.getPlatform().getSettings().isDebugEnabled() == false) { + if (Engine.getPlatform().isRunningOnRaspberry()) { Engine.getPlatform().getGpio().pwmWrite(12, (int) Math.ceil(value * 1024f)); // SoftPwm.softPwmWrite(12, (int)(Math.ceil(brightness*10))); } else { diff --git a/core/src/main/java/it/cavallium/warppi/extra/mario/MarioScreen.java b/core/src/main/java/it/cavallium/warppi/extra/mario/MarioScreen.java index 70f9a000..8b5e00c2 100644 --- a/core/src/main/java/it/cavallium/warppi/extra/mario/MarioScreen.java +++ b/core/src/main/java/it/cavallium/warppi/extra/mario/MarioScreen.java @@ -7,6 +7,7 @@ import it.cavallium.warppi.StaticVars; import it.cavallium.warppi.Platform.ConsoleUtils; import it.cavallium.warppi.device.Keyboard; import it.cavallium.warppi.event.KeyPressedEvent; +import it.cavallium.warppi.event.KeyReleasedEvent; import it.cavallium.warppi.gui.HistoryBehavior; import it.cavallium.warppi.gui.graphicengine.BinaryFont; import it.cavallium.warppi.gui.graphicengine.Skin; @@ -87,12 +88,49 @@ public class MarioScreen extends Screen { } } + boolean rightPressed, leftPressed, jumpPressed; + + @Override + public boolean onKeyPressed(KeyPressedEvent k) { + switch(k.getKey()) { + case OK: + case SIMPLIFY: + case STEP: + jumpPressed = true; + return true; + case LEFT: + leftPressed = true; + return true; + case RIGHT: + rightPressed = true; + return true; + default: + return false; + } + } + + @Override + public boolean onKeyReleased(KeyReleasedEvent k) { + switch(k.getKey()) { + case OK: + case SIMPLIFY: + case STEP: + jumpPressed = false; + return true; + case LEFT: + leftPressed = false; + return true; + case RIGHT: + rightPressed = false; + return true; + default: + return false; + } + } + @Override public void beforeRender(final float dt) { if (!errored) { - final boolean rightPressed = Keyboard.isKeyDown(2, 5); - final boolean leftPressed = Keyboard.isKeyDown(2, 3); - final boolean jumpPressed = Keyboard.isKeyDown(2, 1); final boolean upPressed = false, downPressed = false, runPressed = false; g.gameTick(dt, upPressed, downPressed, leftPressed, rightPressed, jumpPressed, runPressed); diff --git a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrisGame.java b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrisGame.java index 3a3bc5a9..151a74b8 100644 --- a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrisGame.java +++ b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrisGame.java @@ -2,17 +2,17 @@ package it.cavallium.warppi.extra.tetris; public class TetrisGame { - private static final int WIDTH = 10, HEIGHT = 22; - private BlockType[] grid; - private BlockType[] hovergrid; - private GameStatus gameStatus = GameStatus.INITIAL; - private int score = 0; + static final int WIDTH = 10, HEIGHT = 22; + BlockType[] grid; + BlockType[] hovergrid; + GameStatus gameStatus = GameStatus.INITIAL; + int score = 0; public TetrisGame() { } - private void playAgain() { + void playAgain() { grid = new BlockType[WIDTH * HEIGHT]; hovergrid = new BlockType[WIDTH * HEIGHT]; score = 0; diff --git a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrisScreen.java b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrisScreen.java index 23dae466..76ba5e4f 100644 --- a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrisScreen.java +++ b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrisScreen.java @@ -5,8 +5,12 @@ import java.io.IOException; import it.cavallium.warppi.Engine; import it.cavallium.warppi.StaticVars; import it.cavallium.warppi.device.Keyboard; +import it.cavallium.warppi.event.KeyPressedEvent; +import it.cavallium.warppi.event.KeyReleasedEvent; import it.cavallium.warppi.gui.HistoryBehavior; import it.cavallium.warppi.gui.graphicengine.BinaryFont; +import it.cavallium.warppi.gui.graphicengine.GraphicEngine; +import it.cavallium.warppi.gui.graphicengine.Renderer; import it.cavallium.warppi.gui.graphicengine.Skin; import it.cavallium.warppi.gui.screens.Screen; @@ -14,6 +18,10 @@ public class TetrisScreen extends Screen { private TetrisGame g; + private GraphicEngine e; + + private Renderer r; + private static Skin skin; public TetrisScreen() { @@ -23,153 +31,32 @@ public class TetrisScreen extends Screen { @Override public void initialized() { -// try { -// if (TetrisScreen.skin == null) { -// TetrisScreen.skin = Engine.INSTANCE.getHardwareDevice().getDisplayManager().engine.loadSkin("/marioskin.png"); -// } -// if (TetrisScreen.groundskin == null) { -// TetrisScreen.groundskin = Engine.INSTANCE.getHardwareDevice().getDisplayManager().engine.loadSkin("/marioground.png"); -// } -// if (TetrisScreen.gpuTest2 == null) { -// try { -// TetrisScreen.gpuTest2 = Engine.INSTANCE.getHardwareDevice().getDisplayManager().engine.loadFont("N:\\gputest\\gputest2"); -// } catch (final Exception ex) {} -// } -// if (TetrisScreen.gpuTest1 == null) { -// try { -// TetrisScreen.gpuTest1 = Engine.INSTANCE.getHardwareDevice().getDisplayManager().engine.loadFont("N:\\gputest\\gputest12"); -// TetrisScreen.gpuTest12 = true; -// } catch (final Exception ex) { -// TetrisScreen.gpuTest12 = false; -// try { -// TetrisScreen.gpuTest1 = Engine.INSTANCE.getHardwareDevice().getDisplayManager().engine.loadFont("N:\\gputest\\gputest1"); -// } catch (final Exception ex2) {} -// } -// } -// if (TetrisScreen.gpuTest3 == null) { -// try { -// TetrisScreen.gpuTest3 = Engine.INSTANCE.getHardwareDevice().getDisplayManager().engine.loadSkin("N:\\gputest\\font_gputest3.png"); -// } catch (final Exception ex) { -// ex.printStackTrace(); -// } -// } -// } catch (final IOException e) { -// e.printStackTrace(); -// } + try { + e = d.engine; + r = d.renderer; + if (TetrisScreen.skin == null) { + TetrisScreen.skin = Engine.INSTANCE.getHardwareDevice().getDisplayManager().engine.loadSkin("/tetrisskin.png"); + } + } catch (final IOException e) { + e.printStackTrace(); + } } @Override public void created() throws InterruptedException { -// if (!errored) { -// g = new MarioGame(); -// } + g = new TetrisGame(); } @Override public void beforeRender(final float dt) { -// if (!errored) { -// final boolean rightPressed = Keyboard.isKeyDown(2, 5); -// final boolean leftPressed = Keyboard.isKeyDown(2, 3); -// final boolean jumpPressed = Keyboard.isKeyDown(2, 1); -// final boolean upPressed = false, downPressed = false, runPressed = false; -// g.gameTick(dt, upPressed, downPressed, leftPressed, rightPressed, jumpPressed, runPressed); -// -// gpuTestElapsed += dt; -// while (gpuTestElapsed >= 0.04) { -// gpuTestNum = (gpuTestNum + 1) % gpuTestMax; -// gpuTestElapsed -= 0.04; -// } -// gpuCharTestt1Elapsed += dt; -// while (gpuCharTestt1Elapsed >= 1.5) { -// gpuCharTest1Num = (gpuCharTest1Num + 1) % gpuCharTest1.length; -// gpuCharTestt1Elapsed -= 1.5; -// } -// -// Engine.INSTANCE.getHardwareDevice().getDisplayManager().renderer.glClearColor(0xff000000); -// } + Engine.INSTANCE.getHardwareDevice().getDisplayManager().renderer.glClearColor(0xff000000); } @Override public void render() { -// if (errored) { -// Engine.INSTANCE.getHardwareDevice().getDisplayManager().renderer.glDrawStringLeft(0, 20, "ERROR"); -// } else { -// if (TetrisScreen.groundskin != null) { -// final double playerX = g.getPlayer().getX(); -// final double playerY = g.getPlayer().getY(); -// TetrisScreen.groundskin.use(Engine.INSTANCE.getHardwareDevice().getDisplayManager().engine); -// final MarioWorld w = g.getCurrentWorld(); -// final int width = w.getWidth(); -// final int height = w.getHeight(); -// final float screenX = Engine.INSTANCE.getHardwareDevice().getDisplayManager().engine.getWidth() / 2f - 8f; -// final float screenY = Engine.INSTANCE.getHardwareDevice().getDisplayManager().engine.getHeight() / 2f - 8f; -// final float shiftX = -8 + 16 * (float) playerX; -// final float shiftY = -8 + 16 * (height - (float) playerY); -// int blue = -1; -// for (int ix = 0; ix < width; ix++) { -// for (int iy = 0; iy < height; iy++) { -// final double distX = Math.abs(playerX - ix); -// final double distY = Math.abs(playerY - iy - 1.5d); -// if (distX * distX + distY * distY / 2d < 25d) { -// final byte b = w.getBlockIdAt(ix, iy); -// if (b == 0) { -// if (blue != 1) { -// blue = 1; -// Engine.INSTANCE.getHardwareDevice().getDisplayManager().renderer.glColor(0xff9290ff); -// } -// Engine.INSTANCE.getHardwareDevice().getDisplayManager().renderer.glFillColor(screenX - shiftX + 16 * ix, screenY - shiftY + 16 * (height - iy), 16, 16); -// } else { -// if (blue != 0) { -// blue = 0; -// Engine.INSTANCE.getHardwareDevice().getDisplayManager().renderer.glColor(0xffffffff); -// } -// Engine.INSTANCE.getHardwareDevice().getDisplayManager().renderer.glFillRect(screenX - shiftX + 16 * ix, screenY - shiftY + 16 * (height - iy), 16, 16, 0, 0, 16, 16); -// } -// } -// } -// } -// if (blue != 0) { -// blue = 0; -// Engine.INSTANCE.getHardwareDevice().getDisplayManager().renderer.glColor(0xffffffff); -// } -// -// //DRAW MARIO -// TetrisScreen.skin.use(Engine.INSTANCE.getHardwareDevice().getDisplayManager().engine); -// Engine.INSTANCE.getHardwareDevice().getDisplayManager().renderer.glFillRect(screenX - (g.getPlayer().flipped ? 3 : 0), screenY, 35, 27, 35 * (g.getPlayer().marioSkinPos[0] + (g.getPlayer().flipped ? 2 : 1)), 27 * g.getPlayer().marioSkinPos[1], 35 * (g.getPlayer().flipped ? -1 : 1), 27); -//// PIDisplay.renderer.glDrawSkin(getPosX() - 18, 25 + getPosY(), 35 * (marioSkinPos[0] + (flipped ? 2 : 1)), 27 * marioSkinPos[1], 35 * (marioSkinPos[0] + (flipped ? 1 : 2)), 27 * (marioSkinPos[1] + 1), true); -// } -// -//// GPU PERFORMANCE TEST -// if (TetrisScreen.gpuTest1 != null) { -// Engine.INSTANCE.getHardwareDevice().getDisplayManager().renderer.glColor3f(1, 1, 1); -// Engine.INSTANCE.getHardwareDevice().getDisplayManager().renderer.glFillColor(Engine.INSTANCE.getHardwareDevice().getDisplayManager().engine.getWidth() - (TetrisScreen.gpuTest12 ? 512 : 256), Engine.INSTANCE.getHardwareDevice().getDisplayManager().engine.getHeight() / 2 - (TetrisScreen.gpuTest12 ? 256 : 128), TetrisScreen.gpuTest12 ? 512 : 256, TetrisScreen.gpuTest12 ? 512 : 256); -// TetrisScreen.gpuTest1.use(Engine.INSTANCE.getHardwareDevice().getDisplayManager().engine); -// Engine.INSTANCE.getHardwareDevice().getDisplayManager().renderer.glColor3f(0, 0, 0); -// Engine.INSTANCE.getHardwareDevice().getDisplayManager().renderer.glDrawStringRight(Engine.INSTANCE.getHardwareDevice().getDisplayManager().engine.getWidth(), Engine.INSTANCE.getHardwareDevice().getDisplayManager().engine.getHeight() / 2 - (TetrisScreen.gpuTest12 ? 256 : 128), gpuCharTest1[gpuCharTest1Num]); -// } -// if (TetrisScreen.gpuTest3 != null) { -// TetrisScreen.gpuTest3.use(Engine.INSTANCE.getHardwareDevice().getDisplayManager().engine); -// Engine.INSTANCE.getHardwareDevice().getDisplayManager().renderer.glColor4f(1, 1, 1, 0.7f); -// Engine.INSTANCE.getHardwareDevice().getDisplayManager().renderer.glFillRect(0, StaticVars.screenSize[1] - 128, 224, 128, gpuTestNum * 224, 0, 224, 128); -// } -// if (TetrisScreen.gpuTest2 != null) { -// TetrisScreen.gpuTest2.use(Engine.INSTANCE.getHardwareDevice().getDisplayManager().engine); -// Engine.INSTANCE.getHardwareDevice().getDisplayManager().renderer.glColor(0xFF000000); -// Engine.INSTANCE.getHardwareDevice().getDisplayManager().renderer.glDrawStringRight(StaticVars.screenSize[0], Engine.INSTANCE.getHardwareDevice().getDisplayManager().engine.getHeight() - TetrisScreen.gpuTest2.getCharacterHeight(), "A"); -// Engine.INSTANCE.getHardwareDevice().getDisplayManager().renderer.glColor(0xFF800000); -// Engine.INSTANCE.getHardwareDevice().getDisplayManager().renderer.glDrawStringRight(StaticVars.screenSize[0], Engine.INSTANCE.getHardwareDevice().getDisplayManager().engine.getHeight() - TetrisScreen.gpuTest2.getCharacterHeight(), "B"); -// Engine.INSTANCE.getHardwareDevice().getDisplayManager().renderer.glColor(0xFFeea28e); -// Engine.INSTANCE.getHardwareDevice().getDisplayManager().renderer.glDrawStringRight(StaticVars.screenSize[0], Engine.INSTANCE.getHardwareDevice().getDisplayManager().engine.getHeight() - TetrisScreen.gpuTest2.getCharacterHeight(), "C"); -// Engine.INSTANCE.getHardwareDevice().getDisplayManager().renderer.glColor(0xFFee7255); -// Engine.INSTANCE.getHardwareDevice().getDisplayManager().renderer.glDrawStringRight(StaticVars.screenSize[0], Engine.INSTANCE.getHardwareDevice().getDisplayManager().engine.getHeight() - TetrisScreen.gpuTest2.getCharacterHeight(), "D"); -// Engine.INSTANCE.getHardwareDevice().getDisplayManager().renderer.glColor(0xFFeac0b0); -// Engine.INSTANCE.getHardwareDevice().getDisplayManager().renderer.glDrawStringRight(StaticVars.screenSize[0], Engine.INSTANCE.getHardwareDevice().getDisplayManager().engine.getHeight() - TetrisScreen.gpuTest2.getCharacterHeight(), "E"); -// Engine.INSTANCE.getHardwareDevice().getDisplayManager().renderer.glColor(0xFFf3d8ce); -// Engine.INSTANCE.getHardwareDevice().getDisplayManager().renderer.glDrawStringRight(StaticVars.screenSize[0], Engine.INSTANCE.getHardwareDevice().getDisplayManager().engine.getHeight() - TetrisScreen.gpuTest2.getCharacterHeight(), "F"); -// Engine.INSTANCE.getHardwareDevice().getDisplayManager().renderer.glColor(0xFFffede7); -// Engine.INSTANCE.getHardwareDevice().getDisplayManager().renderer.glDrawStringRight(StaticVars.screenSize[0], Engine.INSTANCE.getHardwareDevice().getDisplayManager().engine.getHeight() - TetrisScreen.gpuTest2.getCharacterHeight(), "G"); -// } -// } + if (TetrisScreen.skin != null) { + TetrisScreen.skin.use(e); + } } @Override @@ -179,7 +66,18 @@ public class TetrisScreen extends Screen { @Override public String getSessionTitle() { - return "Absolutely not Super Mario"; + return "Absolutely Not Tetris"; } + @Override + public boolean onKeyPressed(KeyPressedEvent k) { + System.out.println("pr:"+k.getKey()); + return super.onKeyPressed(k); + } + + @Override + public boolean onKeyReleased(KeyReleasedEvent k) { + System.out.println("re:"+k.getKey()); + return super.onKeyReleased(k); + } } diff --git a/core/src/main/resources/tetrisskin.png b/core/src/main/resources/tetrisskin.png new file mode 100644 index 0000000000000000000000000000000000000000..11cfd9964fcf305ce9223dedf03f5b91c3e93028 GIT binary patch literal 29408 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

yxZxsqS2hIC;eQN!t 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 pOa&+J=L6_H;D!p2@mn2Z+5Kv&z!!hJ<~bDHFeM zjx%<%VGAuLE{BV4siGCDqP z(zr}wH$S$Se`Y49HNcJFtAk9NN|MNMT%m;^2re^e35pwDYh73N{3B^^$-cVbjWkA6 zUT6>SpbblgI2YBALLROL$4?sjD^PEs$eD)uJLJl8^F#<$Ey=q;)F)vT#pvL1_Hfv; zs(|_MjLSNvW64Iluu)XO^4zWYYip?rw=(OH*BecceNZhHt^4Agc1>c!Hv*}^whQPd_+q_OL;HT5M0ClV`N`Zjj+$rU&j=3mhgXL0PJ9l6ip7^y* zh|&UA(lcJC29a&e#Q@r)owyAJ@|g;Xj- z40+UXYrcW0CWnv?8>=y|D4UW-<(a`>aR(T>bYa*)O<#9gCn|~Hj+m5ccoZwtJikNp zNiU=Q5R$f&kM2-hjLv|R0klrASw(&d!#7FI-LW}^zPN?bhBXpwS08s?Ltf9m1tPrf ziQ~W{d1sOu>vF~&#RAAv*?Ne(ny<$a)%3KM%h_mRC*O-13XUiyKjv<*^>Xj-1bW;~ z)qTEEb=r-P&@s&XGN?1IYPjev-WaP z#p{j3o{ZkxbyxsI^#eOV_KuuiI*6rov*H9WIbz1+D*4>-`EVmE<1ew)yXnNe;CAr` z_;2JAy4}%J=7}osiG`jAZ7`+}8EdGrpyny|~G>MBRp4){GnZ39~7jRNy})lAg=6&k}OGHy3*qbzkx@X~U5KMrt# z_!3Ncb*)~;P*kOfwBQe#=3|*Hg$U$KEK@FIF&DN_4BVENgeOA`zEK@gY1`k&g+z`8 z-(Y6nk)&zP8ig&3ashN!-cMgbG}*dujDhV^By6%sVwwpKzSlAfc}vhd#a_}Z;3?1e zBeHVDWM|6P@5T-wJV;#pr$a_@s`J>?N$4(5rX7~-XC_FE?sK;OlO>pYEZ|aL9 zSxtCnp9k8~Ay9zwJ_m?DDC9=!}-_@RP|B zgUOZmxoG#Fm2pV1G+M7$LAq_4lqu3m@5C861DSH}#J z5%!cK_y|2cr5|O@H!hP5+(5j9hGb;Q&2tG{@9ZY)#nz!83-fpxf`W^q zffW$*#ktTgXV-dKppfO>&zrYaa2W+KIuF9P?8=O!!fy~I�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 el; private final DesktopSettings settings; + private Boolean runningOnRaspberryOverride = null; public DesktopPlatform() { cu = new DesktopConsoleUtils(); @@ -205,8 +206,18 @@ public class DesktopPlatform implements Platform { return org.eclipse.jdt.internal.compiler.batch.Main.compile(command, printWriter, errors, null); } + @Override + public void setRunningOnRaspberry(boolean b) { + if (isRunningOnRaspberry()) { + runningOnRaspberryOverride = b; + } else { + runningOnRaspberryOverride = false; + } + } + @Override public boolean isRunningOnRaspberry() { + if (runningOnRaspberryOverride != null) return runningOnRaspberryOverride; return CacheUtils.get("isRunningOnRaspberry", 24 * 60 * 60 * 1000, () -> { if (Engine.getPlatform().isJavascript()) return false; diff --git a/desktop/src/main/java/it/cavallium/warppi/gui/graphicengine/impl/swing/SwingWindow.java b/desktop/src/main/java/it/cavallium/warppi/gui/graphicengine/impl/swing/SwingWindow.java index 666a56dc..a417bf3e 100644 --- a/desktop/src/main/java/it/cavallium/warppi/gui/graphicengine/impl/swing/SwingWindow.java +++ b/desktop/src/main/java/it/cavallium/warppi/gui/graphicengine/impl/swing/SwingWindow.java @@ -244,21 +244,16 @@ public class SwingWindow extends JFrame { b.setFont(f); b.setBackground(new Color(200, 200, 200)); b.setFocusable(true); - b.addActionListener(e -> { - Keyboard.keyPressedRaw(row, col); - Keyboard.keyReleasedRaw(row, col); - c.grabFocus(); - }); b.addMouseListener(new MouseListener() { @Override public void mouseReleased(MouseEvent e) { - // TODO Auto-generated method stub - + Keyboard.keyRaw(row, col, true); + c.grabFocus(); } @Override public void mousePressed(MouseEvent e) { - // TODO Auto-generated method stub - + Keyboard.keyRaw(row, col, false); + c.grabFocus(); } @Override public void mouseExited(MouseEvent e) { diff --git a/hardware/src/main/java/it/cavallium/warppi/hardware/HardwarePlatform.java b/hardware/src/main/java/it/cavallium/warppi/hardware/HardwarePlatform.java index e1ab9c94..75aa321c 100644 --- a/hardware/src/main/java/it/cavallium/warppi/hardware/HardwarePlatform.java +++ b/hardware/src/main/java/it/cavallium/warppi/hardware/HardwarePlatform.java @@ -25,6 +25,7 @@ public class HardwarePlatform implements Platform { private final String on; private final Map el; private final HardwareSettings settings; + private Boolean runningOnRaspberryOverride = null; public HardwarePlatform() { cu = new HardwareConsoleUtils(); @@ -194,8 +195,14 @@ public class HardwarePlatform implements Platform { return org.eclipse.jdt.internal.compiler.batch.Main.compile(command, printWriter, errors, null); } + @Override + public void setRunningOnRaspberry(boolean b) { + runningOnRaspberryOverride = b; + } + @Override public boolean isRunningOnRaspberry() { + if (runningOnRaspberryOverride != null) return runningOnRaspberryOverride; return true; /* return CacheUtils.get("isRunningOnRaspberry", 24 * 60 * 60 * 1000, () -> { diff --git a/teavm/src/main/java/it/cavallium/warppi/gui/graphicengine/html/HtmlEngine.java b/teavm/src/main/java/it/cavallium/warppi/gui/graphicengine/html/HtmlEngine.java index e9f8d0fa..8843fddc 100644 --- a/teavm/src/main/java/it/cavallium/warppi/gui/graphicengine/html/HtmlEngine.java +++ b/teavm/src/main/java/it/cavallium/warppi/gui/graphicengine/html/HtmlEngine.java @@ -70,15 +70,10 @@ public class HtmlEngine implements GraphicEngine { } private String previousValue = ""; - - @JSBody(params = { "ctx", "enabled" }, script = "" - + "ctx.mozImageSmoothingEnabled = enabled;" - + "ctx.oImageSmoothingEnabled = enabled;" - + "ctx.webkitImageSmoothingEnabled = enabled;" - + "ctx.msImageSmoothingEnabled = enabled;" - + "ctx.imageSmoothingEnabled = enabled;") + + @JSBody(params = { "ctx", "enabled" }, script = "" + "ctx.mozImageSmoothingEnabled = enabled;" + "ctx.oImageSmoothingEnabled = enabled;" + "ctx.webkitImageSmoothingEnabled = enabled;" + "ctx.msImageSmoothingEnabled = enabled;" + "ctx.imageSmoothingEnabled = enabled;") public static native void setImageSmoothingEnabled(CanvasRenderingContext2D ctx, boolean enabled); - + @Override public void create(final Runnable onInitialized) { exitSemaphore = Engine.getPlatform().newSemaphore(0); @@ -93,14 +88,14 @@ public class HtmlEngine implements GraphicEngine { onZoom.subscribe((windowZoom) -> { if (windowZoom != 0) { if (suppportsZoom()) { - canvas.setWidth((int)(480 / 1)); - canvas.setHeight((int)(320 / 1)); + canvas.setWidth((int) (480 / 1)); + canvas.setHeight((int) (320 / 1)); canvas.getStyle().setProperty("zoom", "" + (1 + 1)); } else { - canvas.setWidth((int)(480 * 2)); - canvas.setHeight((int)(320 * 2)); + canvas.setWidth((int) (480 * 2)); + canvas.setHeight((int) (320 * 2)); } - canvas.getStyle().setProperty("max-height", (int)(44 / (1+1)) + "vh"); + canvas.getStyle().setProperty("max-height", (int) (44 / (1 + 1)) + "vh"); width = 480 / windowZoom.intValue(); height = 320 / windowZoom.intValue(); this.mult = windowZoom.intValue(); @@ -114,11 +109,13 @@ public class HtmlEngine implements GraphicEngine { HtmlEngine.document.getElementById("container").appendChild(canvas); HtmlEngine.document.getBody().appendChild(keyInput); keyInput.setTabIndex(0); + keyInput.setValue("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); keyInput.addEventListener("keydown", (final KeyboardEvent evt) -> { evt.preventDefault(); new Thread(() -> { previousValue = keyInput.getValue(); - Keyboard.debugKeyPressed(evt.getKeyCode()); + Keyboard.debugKey(evt.getKeyCode(), false); + Keyboard.debugKey(evt.getKeyCode(), true); System.out.println(evt.getKeyCode()); System.out.println("" + (int) evt.getKey().charAt(0)); }).start(); @@ -133,14 +130,17 @@ public class HtmlEngine implements GraphicEngine { new Thread(() -> { if (newLen == prevLen) { - } else if (newLen - prevLen == 1) - Keyboard.debugKeyPressed(newValue.toUpperCase().charAt(newLen - 1)); + } else if (newLen - prevLen == 1) { + Keyboard.debugKey(newValue.toUpperCase().charAt(newLen - 1), false); + Keyboard.debugKey(newValue.toUpperCase().charAt(newLen - 1), true);} else if (newLen - prevLen > 1) - for (int i = 0; i < newLen - prevLen; i++) - Keyboard.debugKeyPressed(newValue.toUpperCase().charAt(prevLen + i)); + for (int i = 0; i < newLen - prevLen; i++) { + Keyboard.debugKey(newValue.toUpperCase().charAt(prevLen + i), false); + Keyboard.debugKey(newValue.toUpperCase().charAt(prevLen + i), true);} else if (newLen - prevLen < 1) - for (int i = 0; i < prevLen - newLen; i++) - Keyboard.debugKeyPressed(8); + for (int i = 0; i < prevLen - newLen; i++) { + Keyboard.debugKey(8, false); + Keyboard.debugKey(8, true);} }).start(); }); canvas.addEventListener("click", (final Event evt) -> { @@ -163,36 +163,48 @@ public class HtmlEngine implements GraphicEngine { final String[] coordinates = code.split(",", 2); final boolean removeshift = Keyboard.shift && Integer.parseInt(coordinates[0]) != 0 && Integer.parseInt(coordinates[1]) != 0; final boolean removealpha = Keyboard.alpha && Integer.parseInt(coordinates[0]) != 0 && Integer.parseInt(coordinates[1]) != 1; - Keyboard.keyPressedRaw(Integer.parseInt(coordinates[0]), Integer.parseInt(coordinates[1])); + Keyboard.keyRaw(Integer.parseInt(coordinates[0]), Integer.parseInt(coordinates[1]), false); if (removeshift) - Keyboard.keyPressedRaw(0, 0); + Keyboard.keyRaw(0, 0, false); if (removealpha) - Keyboard.keyPressedRaw(0, 1); + Keyboard.keyRaw(0, 1, false); Thread.sleep(100); - Keyboard.keyReleasedRaw(Integer.parseInt(coordinates[0]), Integer.parseInt(coordinates[1])); + Keyboard.keyRaw(Integer.parseInt(coordinates[0]), Integer.parseInt(coordinates[1]), true); if (removeshift) - Keyboard.keyReleasedRaw(0, 0); + Keyboard.keyRaw(0, 0, true); if (removealpha) - Keyboard.keyReleasedRaw(0, 1); + Keyboard.keyRaw(0, 1, true); } else if (Keyboard.alpha && !Keyboard.shift) { - if (button.hasAttribute("keycodea")) - Keyboard.debugKeyPressed(Integer.parseInt(button.getAttribute("keycodea"))); - else - Keyboard.debugKeyPressed(Integer.parseInt(button.getAttribute("keycode"))); + if (button.hasAttribute("keycodea")) { + Keyboard.debugKey(Integer.parseInt(button.getAttribute("keycodea")), false); + Keyboard.debugKey(Integer.parseInt(button.getAttribute("keycodea")), true); + } else { + Keyboard.debugKey(Integer.parseInt(button.getAttribute("keycode")), false); + Keyboard.debugKey(Integer.parseInt(button.getAttribute("keycode")), true); + } } else if (!Keyboard.alpha && Keyboard.shift) { - if (button.hasAttribute("keycodes")) - Keyboard.debugKeyPressed(Integer.parseInt(button.getAttribute("keycodes"))); - else - Keyboard.debugKeyPressed(Integer.parseInt(button.getAttribute("keycode"))); + if (button.hasAttribute("keycodes")) { + Keyboard.debugKey(Integer.parseInt(button.getAttribute("keycodes")), false); + Keyboard.debugKey(Integer.parseInt(button.getAttribute("keycodes")), true); + } else { + Keyboard.debugKey(Integer.parseInt(button.getAttribute("keycode")), false); + Keyboard.debugKey(Integer.parseInt(button.getAttribute("keycode")), true); + } } else if (Keyboard.alpha && Keyboard.shift) { - if (button.hasAttribute("keycodesa")) - Keyboard.debugKeyPressed(Integer.parseInt(button.getAttribute("keycodesa"))); - else if (button.hasAttribute("keycodes")) - Keyboard.debugKeyPressed(Integer.parseInt(button.getAttribute("keycodes"))); - else - Keyboard.debugKeyPressed(Integer.parseInt(button.getAttribute("keycode"))); - } else - Keyboard.debugKeyPressed(Integer.parseInt(button.getAttribute("keycode"))); + if (button.hasAttribute("keycodesa")) { + Keyboard.debugKey(Integer.parseInt(button.getAttribute("keycodesa")), false); + Keyboard.debugKey(Integer.parseInt(button.getAttribute("keycodesa")), true); + } else if (button.hasAttribute("keycodes")) { + Keyboard.debugKey(Integer.parseInt(button.getAttribute("keycodes")), false); + Keyboard.debugKey(Integer.parseInt(button.getAttribute("keycodes")), true); + } else { + Keyboard.debugKey(Integer.parseInt(button.getAttribute("keycode")), false); + Keyboard.debugKey(Integer.parseInt(button.getAttribute("keycode")), true); + } + } else { + Keyboard.debugKey(Integer.parseInt(button.getAttribute("keycode")), false); + Keyboard.debugKey(Integer.parseInt(button.getAttribute("keycode")), true); + } } catch (final Exception ex) { ex.printStackTrace(); } diff --git a/teavm/src/main/java/it/cavallium/warppi/gui/graphicengine/html/HtmlRenderer.java b/teavm/src/main/java/it/cavallium/warppi/gui/graphicengine/html/HtmlRenderer.java index cf42b542..5c124516 100644 --- a/teavm/src/main/java/it/cavallium/warppi/gui/graphicengine/html/HtmlRenderer.java +++ b/teavm/src/main/java/it/cavallium/warppi/gui/graphicengine/html/HtmlRenderer.java @@ -7,7 +7,7 @@ import it.cavallium.warppi.gui.graphicengine.Renderer; public class HtmlRenderer implements Renderer { private String currentColor = "#000000ff"; - private String clearColor = "#000000ff"; + private String clearColor = "#c5c2afff"; HtmlFont f = null; HtmlSkin currentSkin = null; private final CanvasRenderingContext2D g; diff --git a/teavm/src/main/java/it/cavallium/warppi/teavm/TeaVMPlatform.java b/teavm/src/main/java/it/cavallium/warppi/teavm/TeaVMPlatform.java index a053be53..d63899ae 100644 --- a/teavm/src/main/java/it/cavallium/warppi/teavm/TeaVMPlatform.java +++ b/teavm/src/main/java/it/cavallium/warppi/teavm/TeaVMPlatform.java @@ -23,6 +23,7 @@ public class TeaVMPlatform implements Platform { private final Map el; private final TeaVMPngUtils pu; private final TeaVMSettings settings; + private Boolean runningOnRaspberryOverride = null; public TeaVMPlatform() { cu = new TeaVMConsoleUtils(); @@ -208,6 +209,10 @@ public class TeaVMPlatform implements Platform { throw new java.lang.UnsupportedOperationException("Not implemented."); } + @Override + public void setRunningOnRaspberry(boolean b) { + } + @Override public boolean isRunningOnRaspberry() { return false; diff --git a/teavm/src/main/java/it/cavallium/warppi/teavm/TeaVMSettings.java b/teavm/src/main/java/it/cavallium/warppi/teavm/TeaVMSettings.java index c4ef8ea3..1102dbf3 100644 --- a/teavm/src/main/java/it/cavallium/warppi/teavm/TeaVMSettings.java +++ b/teavm/src/main/java/it/cavallium/warppi/teavm/TeaVMSettings.java @@ -7,7 +7,7 @@ public class TeaVMSettings implements Settings { private boolean debug; public TeaVMSettings() { - debug = true; + debug = false; } @Override From a2939b6940994e57d67385de8ce97ed0636ef742 Mon Sep 17 00:00:00 2001 From: Andrea Cavalli Date: Sat, 6 Oct 2018 16:41:10 +0200 Subject: [PATCH 02/18] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 71ff635e..558a7b4c 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ # WarpPI Calculator Step-by-step algebra calculator for Raspberry PI. -**This project is experimental and strictly related to my calculator, designed to run on an embedded hardware. -It works but many fundamental features aren't complete.**
+**This project is experimental and strictly related to my calculator, designed to run on a raspberry PI Zero.** +**Many fundamental features are missing, this project is still in its infancy.**
**If you really want to build and test it on your computer take account of that.** From d9541908824da2ef8297aa7c116794cf47ebc039 Mon Sep 17 00:00:00 2001 From: Andrea Cavalli Date: Sat, 6 Oct 2018 16:41:21 +0200 Subject: [PATCH 03/18] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 558a7b4c..1f76a5a2 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # WarpPI Calculator Step-by-step algebra calculator for Raspberry PI. -**This project is experimental and strictly related to my calculator, designed to run on a raspberry PI Zero.** +**This project is experimental and strictly related to my calculator, designed to run on a raspberry PI Zero.**
**Many fundamental features are missing, this project is still in its infancy.**
**If you really want to build and test it on your computer take account of that.** From b90faa13b8589bfda1a2693a35cee8c68bba4fe0 Mon Sep 17 00:00:00 2001 From: Cavallium Date: Tue, 9 Oct 2018 17:45:20 +0200 Subject: [PATCH 04/18] Updated extra feature --- .../warppi/extra/tetris/TetrisGame.java | 22 +++++-- .../warppi/extra/tetris/TetrisScreen.java | 66 ++++++++++++++++++- .../graphicengine/impl/common/RFTFont.java | 2 +- 3 files changed, 83 insertions(+), 7 deletions(-) diff --git a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrisGame.java b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrisGame.java index 151a74b8..220c30f8 100644 --- a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrisGame.java +++ b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrisGame.java @@ -2,11 +2,12 @@ package it.cavallium.warppi.extra.tetris; public class TetrisGame { - static final int WIDTH = 10, HEIGHT = 22; - BlockType[] grid; - BlockType[] hovergrid; - GameStatus gameStatus = GameStatus.INITIAL; - int score = 0; + private static final int WIDTH = 10, HEIGHT = 22; + private BlockType[] grid; + private BlockType[] hovergrid; + private GameStatus gameStatus = GameStatus.INITIAL; + private int score = 0; + private double currentTime = 0; public TetrisGame() { @@ -16,6 +17,17 @@ public class TetrisGame { grid = new BlockType[WIDTH * HEIGHT]; hovergrid = new BlockType[WIDTH * HEIGHT]; score = 0; + currentTime = 0; gameStatus = GameStatus.PLAYING; } + + public void gameTick(float dt, boolean leftPressed, boolean rightPressed, boolean downPressed, boolean okPressed, + boolean backPressed) { + currentTime += dt; + if (gameStatus == GameStatus.INITIAL) { + playAgain(); + } else { + + } + } } diff --git a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrisScreen.java b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrisScreen.java index 76ba5e4f..24a79ea2 100644 --- a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrisScreen.java +++ b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrisScreen.java @@ -18,6 +18,16 @@ public class TetrisScreen extends Screen { private TetrisGame g; + private boolean leftPressed; + + private boolean rightPressed; + + private boolean downPressed; + + private boolean okPressed; + + private boolean backPressed; + private GraphicEngine e; private Renderer r; @@ -50,6 +60,7 @@ public class TetrisScreen extends Screen { @Override public void beforeRender(final float dt) { Engine.INSTANCE.getHardwareDevice().getDisplayManager().renderer.glClearColor(0xff000000); + g.gameTick(dt, leftPressed, rightPressed, downPressed, okPressed, backPressed); } @Override @@ -58,7 +69,60 @@ public class TetrisScreen extends Screen { TetrisScreen.skin.use(e); } } - + + @Override + public boolean onKeyPressed(KeyPressedEvent k) { + switch (k.getKey()) { + case LEFT: { + leftPressed = true; + return true; + } + case RIGHT: { + rightPressed = true; + return true; + } + case DOWN: { + downPressed = true; + return true; + } + case OK: { + okPressed = true; + return true; + } + case BACK: { + backPressed = true; + return true; + } + default: return false; + } + } + + @Override + public boolean onKeyReleased(KeyReleasedEvent k) { + switch (k.getKey()) { + case LEFT: { + leftPressed = false; + return true; + } + case RIGHT: { + rightPressed = false; + return true; + } + case DOWN: { + downPressed = false; + return true; + } + case OK: { + okPressed = false; + return true; + } + case BACK: { + backPressed = false; + return true; + } + default: return false; + } + } @Override public boolean mustBeRefreshed() { return true; diff --git a/core/src/main/java/it/cavallium/warppi/gui/graphicengine/impl/common/RFTFont.java b/core/src/main/java/it/cavallium/warppi/gui/graphicengine/impl/common/RFTFont.java index 1e7ea476..af68f9cd 100644 --- a/core/src/main/java/it/cavallium/warppi/gui/graphicengine/impl/common/RFTFont.java +++ b/core/src/main/java/it/cavallium/warppi/gui/graphicengine/impl/common/RFTFont.java @@ -19,7 +19,7 @@ import it.cavallium.warppi.gui.graphicengine.GraphicEngine; import it.cavallium.warppi.util.Utils; public abstract class RFTFont implements BinaryFont { - + public boolean[][] rawchars; public int[] chars32; public int minBound = 10; From 35d878fba7ba26bcf64ed563c39535e90f9e0425 Mon Sep 17 00:00:00 2001 From: Cavallium Date: Tue, 9 Oct 2018 19:37:48 +0200 Subject: [PATCH 05/18] Updated extra feature --- .../warppi/extra/tetris/BlockType.java | 2 +- .../warppi/extra/tetris/TetrisGame.java | 22 +++++++++++-- .../warppi/extra/tetris/TetrisScreen.java | 29 ++++++++++-------- core/src/main/resources/tetrisskin.png | Bin 29408 -> 1722 bytes core/src/main/resources/tetrisskin.xcf | Bin 96275 -> 1494 bytes 5 files changed, 37 insertions(+), 16 deletions(-) diff --git a/core/src/main/java/it/cavallium/warppi/extra/tetris/BlockType.java b/core/src/main/java/it/cavallium/warppi/extra/tetris/BlockType.java index c359f9dd..ce042e1d 100644 --- a/core/src/main/java/it/cavallium/warppi/extra/tetris/BlockType.java +++ b/core/src/main/java/it/cavallium/warppi/extra/tetris/BlockType.java @@ -3,8 +3,8 @@ package it.cavallium.warppi.extra.tetris; public enum BlockType { RED, GREEN, - YELLOW, BLUE, + YELLOW, ORANGE, VIOLET } diff --git a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrisGame.java b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrisGame.java index 220c30f8..a53419f4 100644 --- a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrisGame.java +++ b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrisGame.java @@ -2,9 +2,10 @@ package it.cavallium.warppi.extra.tetris; public class TetrisGame { - private static final int WIDTH = 10, HEIGHT = 22; + public static final int WIDTH = 10, HEIGHT = 22; private BlockType[] grid; private BlockType[] hovergrid; + private BlockType[] renderedGrid; private GameStatus gameStatus = GameStatus.INITIAL; private int score = 0; private double currentTime = 0; @@ -16,18 +17,33 @@ public class TetrisGame { void playAgain() { grid = new BlockType[WIDTH * HEIGHT]; hovergrid = new BlockType[WIDTH * HEIGHT]; + renderedGrid = new BlockType[WIDTH * HEIGHT]; score = 0; currentTime = 0; gameStatus = GameStatus.PLAYING; } - public void gameTick(float dt, boolean leftPressed, boolean rightPressed, boolean downPressed, boolean okPressed, - boolean backPressed) { + public void gameTick(float dt, boolean leftPressed, boolean rightPressed, boolean downPressed, boolean okPressed, boolean backPressed) { currentTime += dt; if (gameStatus == GameStatus.INITIAL) { playAgain(); } else { } + renderGrid(); + } + + public void renderGrid() { + this.renderedGrid = new BlockType[WIDTH*HEIGHT]; + for (int y = 0; y < HEIGHT; y++) { + for (int x = 0; x < WIDTH; x++) { + final int offset = x+y*WIDTH; + renderedGrid[offset] = hovergrid[offset] != null ? hovergrid[offset] : grid[offset]; + } + } + } + + public BlockType[] getRenderedGrid() { + return renderedGrid; } } diff --git a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrisScreen.java b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrisScreen.java index 24a79ea2..3d1ead18 100644 --- a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrisScreen.java +++ b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrisScreen.java @@ -2,6 +2,8 @@ package it.cavallium.warppi.extra.tetris; import java.io.IOException; +import org.nevec.rjm.Wigner3j; + import it.cavallium.warppi.Engine; import it.cavallium.warppi.StaticVars; import it.cavallium.warppi.device.Keyboard; @@ -47,6 +49,7 @@ public class TetrisScreen extends Screen { if (TetrisScreen.skin == null) { TetrisScreen.skin = Engine.INSTANCE.getHardwareDevice().getDisplayManager().engine.loadSkin("/tetrisskin.png"); } + StaticVars.windowZoom.onNext(1f); } catch (final IOException e) { e.printStackTrace(); } @@ -68,6 +71,20 @@ public class TetrisScreen extends Screen { if (TetrisScreen.skin != null) { TetrisScreen.skin.use(e); } + r.glColor3f(1, 1, 1); + BlockType[] renderedGrid = g.getRenderedGrid(); + int centerScreen = StaticVars.screenSize[0]/2; + int centerGrid = TetrisGame.WIDTH*5/2-1; + final int leftOffset = centerScreen - centerGrid; + final int topOffset = StaticVars.screenSize[1] - TetrisGame.HEIGHT*5-1; + for (int y = 0; y < TetrisGame.HEIGHT; y++) { + for (int x = 0; x < TetrisGame.WIDTH; x++) { + final int offset = x+y*TetrisGame.WIDTH; + final BlockType type = renderedGrid[offset]; + if (type != null) r.glFillRect(leftOffset + x * 4, y * 4, 4, 4, renderedGrid[offset].ordinal() * 4, 0, 4, 4); + else r.glFillRect(leftOffset + x * 5, topOffset + y * 5, 4, 4, 2 * 4, 0, 4, 4); + } + } } @Override @@ -132,16 +149,4 @@ public class TetrisScreen extends Screen { public String getSessionTitle() { return "Absolutely Not Tetris"; } - - @Override - public boolean onKeyPressed(KeyPressedEvent k) { - System.out.println("pr:"+k.getKey()); - return super.onKeyPressed(k); - } - - @Override - public boolean onKeyReleased(KeyReleasedEvent k) { - System.out.println("re:"+k.getKey()); - return super.onKeyReleased(k); - } } diff --git a/core/src/main/resources/tetrisskin.png b/core/src/main/resources/tetrisskin.png index 11cfd9964fcf305ce9223dedf03f5b91c3e93028..7cc22a6636fc1910f38a1767f435415fb3ef1d9c 100644 GIT binary patch delta 1696 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

yxZxsqS2hIC;eQN!t z7Q_;pj&!TsPu|0@^V5;s3FQ?Sn)L>shXtZBMIum8fy?#;Q0OQuVWb>WOHq3W zj3!rJX1&Xp5B|t*zY}S{mm%x;NXt+=h+@xnU4L8-RX?S~CGE#g<){s&hbqrRn$F*e zBEq}xnCV&}$D2#vUxehVXG5Ui$C^`qVw&XJEOyxT<;R_vK2R?*CuES5db1s7rOz== z702sI)pPoEi%U62_F2%NPUfJtSD7@II4}f%Y;_H%>dy_7V3b*$+*>Efgm#1Xit7d> zU=zIp-gbkVt-J1=5<~p&v^$Y2-V%2>aO?|@?!V^`&0IS2zigEj$*&)>$lf(is?49| zi@Y0i&2%6RUP*keOlY(!!7cdtWWFQdT`@Y#tBPru0HDb)VDqZ5D?>a9T6R7L2+oc% zZb#!Tcgt^0QoF-UMwKiv0d+7OLaX;?AUbN%ci;oofp$iY_QdW?Ks)1fZhM+tOuo2) zccs;3jFNCOVgUMAlCFz5Nv!j4{<-$OD7O6QyUOKM-&f@i8Z->2Lb>UeKhTzo=&{V! z{rv9pIm6S4QVz~#Jdlf(`pvpLn52z53*q z?9L4rRj2dh+uv%cWZWLf$;B?}YlrzJ(pTYwK73W40jJAnP1?-LE~|&otBF!tSFyNd z!L2G#sqv$#h@=MTi_1&A2RjUlNLAPB0Uy1KH@!Z(R1@8~Gv}L{U-eF+0Qi?wCflPs zM?%*&T=hY!*-W<(hRf1AnuufI?ysFk6xdp)cbC)4%7jsjLep+SDzEj^sZQ^hMmwPmL;slc5=W)QrX zL4V1mTfqB;3d9X!hE+z)JEno)P>YWiS!*(RmJ5Bmg|xDrykYf9+z<4_O)5y2LA00A z6EBoZON6|F2{Vx@9`9{ss@>UiPXbMOxOSxH<$w*1oDCBhHHiFpsO`CPC1Kpby zGE2Mv6q3su!R&Ss`};b|@Wrang__Eqd68N~_taX9Sl>%R3CGLMlYX01FVR(JO(MV% zzkS67CXonYSexHP*Zs^wvf@GjQD=+orPfK?XXNf0{Y+jU3_r=Dhxk!9?mBkT>dy&F zIkAr}Jitqw&zy(Z%SuXs3cPKFF9erjBh^4xe|thrv%^+b{pHLCj-&r6y5XXtFUP{} zMPCQddC-sgXGInT3Q9cER?xXDk!7#&#+qK?1zW5?|AhYyNMgKM(>)Ckzwxzyp5a!U zZ5k55M3b)HW3fM}l~nj0ziibe*LwubXB>B{w{F_o(C?dQ?aEVGh_Is+0ik`wDd^Ae4= zV4fQnh76qGC4@aw*Y2s!>$*et8#qtC*j?Ld;>IP@EPoG7P*e!I?~{pua@Oo*zru_=22hH*T8EJ3L! zZfc4XRMcaBc#&@gdfSA%@rpDT+dW3r^>696LYAZPN~oFInORbMv1uIxy1ecdmNox! zCN9(7%FoJrA%E;N@YRATC&g7wAS4;D$r9m4fU#ChRFiC}9ef8#j)7Xdw%4$1N%64m zMh$KM0x0JPe>MNRA4dzae~&kG`){VJ)MfLg{5`gk+}nB;u!txV_409Nn$M#pVU7Xe z{yPx7N7ij9;zWZ<>Gc6;1s1~3cDrj~5#hRlJp9@*-T$+vZcZHHhU~S)@^eNt5I z%h7{hj(Rhmu=dhDNB64}a_UbdK9p=V z-44uKz>4Im2fIW-$AwjHZUt+r2r1%RzD3S^ld}iDMPLSHY}Kki5}d5bs4_iP0>%eL z$!Y&g>q@ovmzXpp)DTg1TR0$s_fHTdwt6xpYYWl@Ux!BhP(KYcLu^sos1A0gNR~-< zbTFF>`?5rAm7E@x+<(wL7ON8$x7@IfvOBC1)$|wxp7qKFR8@vj-`uP*VE>wUS(U=* z5GgBhDWpZB-;+(0c>-34Fgc2OrEgvJRV>pLq~BiW7bnB_U`UUERX5C?zGe!Q87a@& zQrUdJr|fvTU9RaL1#^GRw8KO)eDOa&g2Rd6)E9@pHqO0s zw_xO7T9>1rvL4+{lqc4A*E(hLZqE7$MRs{g*Q}~V*WkhHCylEmAct}-BQ==)Y=+>3oVf*}&&OQ)Q;Q30i_= z5bv0|&jj^&-^ne(KaGwfbv#&?tR<3#6yI;`VCIc!?q;+lN^*fgQ#m7&qg|_Y^jkiLP!c1?{cxU{gGo3?NHAf82N)doF$s+BF z>IjX8$ZjqK@-F$tlFg5r&ez#BZf`LG?gY-+lrD$U3IgY*WPt5&;^m$HAj#S8) zOxm%+EOEowMK}+Jxxbz%aB><}{^rfds@-pWG?FEWTb(lFmn-cI4FC=4o|@2cWi*bm z&sH7tmLVB8Jm(mtOJ6M^(}q`2&&ZEKy%?dl6T5ZmW;Jm1M;qIo_*_*gn3TXbTKibKuL7--_HW8!~vCM`Zt7 z+HCN)B6wY*CD&a(hs90b12N1Zbs_QcB`s;zDB)hN$>~^k`FHcCYmnutb?6+>hYSO| zf_EU>3G09mFdw{jcddqkZv*nFhnOEREq55wCvsvs0!0Aqg`PBW?Eb#?)>LfZv8XG7 z1JA;4j%}!le76XLx}Rs?oM@q14@(@}%|Nz%Epdh-n~FTo?yruU>gMW2zLQ-GDXPDk zySeKctm%B9oR5$Wc?JgcGmao~beLRlzlq!5RQ--N|zHK@8tiB~Z#ZG`m5mAegaX(`W?OAQ;OXpSC3&<|uv+zt#lG z#pG{Qt?8sY1K$bNlyC{yEiR4}JSa}ZjL2`ufd`{_1@Jlhu=|E@1&>z5F`P7NI{_+~s>!lzBLEtt17e)*}jr3E~dScb1iD z-Lmh#BHT-}o{vGOsNZ&uv3-dV3oXxOT-sNtdUH#BfxXX)-`!%hD@ohRv)9!CJMSpE zp6gHTiFd=G$}Vcl=N;1CAlIr(X@;o4;(;0SB>TmMW7|O1jZ8UC(Q*%Ebz(gXfue<- z^Gf|&Zu&MC9}ac;{yxaw2Y;V`DP+nu2Jb4OiG%DC7T0O)ZOd|X)_*XKP)M;iF?q<+> zh>94$30MR^pcPiPk7h5YY<0E&y*N}64V}ARuEu<)uU0*84+?87I!+yd7w&}kY;L;+W zv^7QIZ{$xIkDB(?PI;cYf(eJ(AyVtek8E$)BV~PvZr3ay680GO@yx$qlkllaFmi8}z{cY}35A(*tpVS3M zkHB9J{3=x49q;$Jkf~3pmyf{~o;#XO=HTCIoSZ6Oi!Fbs@vl}yNQ9WvOmmN0ZNnQ{ z>ApGsq9lr+Z|pZ%o2%tNZKcx)bxW?j^j2;Oj96|TeWQ!26=x{TsBY1q|FoXp%LakV zZzE7U$B3IO&;nfuUifO66nXXC*U~QA9QEb&?r*%BZI0vb+)dYtgD8Yp7Gkx*lAn2kCPy(E*`YxSzd$K_qR=tT#FJw^ahvToF)#Y z!b16CHfcD!g*d8-#$Ku7r|ebkWpqt!=?B93p3;9EdVXlg0|59KUa%|~<=3{URc zo8P>$V8Vs!Va{0F5ANLIoD^SEuW`E3lpEV>R*p`&a&k&^j@x6p&}zASV8_Jclj&Kj zQ7Gb+j}Q^lMZONk@zl5qdYMPQJZ*m6xKhJw$xU(XS^wzOYgnej@ zds8c$r1AGO)g=&g8CtTKxxZA^r`FVX}W-VW&qQn*Pn1_tp*fJj3S{xyTcFwG{jE!!Sg*rv_w7XKs&DUc z+)A?5I}hUOo1~v{O1K4J#o;^2m;toEK#V?1%q#R0;$VsUrcz{DBvGMV!oFhX#kq%^ z9?$W`3Yi7`eCE9Ft@S3Pu|m{(+79GKcdh83`?$dbre*K=5tiAMrG*%1X=97FWLZjm zT%T4qX?L8L-^r0oo}P+4IQT0!qO=N*$a|db?RF;Hrp;K#VIah$6;&7y-;~rmG^e+6TNv*ew%vZ+KEf^uB9T zO9C0Iy{{n2B$TUt6xl1_L7sg9Tj}cRCE9Z<3G+pthbXtHHn2`>H);yTqRv(JS^avo zL&z|p-|-pRTiCvs=|{A7n)T1E8;b(}i~HbBCBt?vfvqTsp(c>`7N+cC(BhZMR+Go9Ukzu=5x^r^)fVx0b=Jqd&~`jaogdQF(D*iy z;>(XMTs9h82~g9ujI<;$QMkqCuDb5DIBs*@&}(u@Yw;63_1sSI%n3O0nmP!Q^kF=qxEkPrV(^9E?4KF71YSWUv zy>@=I43NLLdG6ej%(NwruqNo?S0rlD3iM@5@t!*3?D#>qPpQXB7vje=k z@Z`A8PG+~`on6lv=~#D#f|OtNgZYM~-n~n1f0fKaN+48^qWe4F$8wsjWgEm=&l@*03~eJ@0kI{WTg$xxur&9Il8`H?&n9rH^T zKTRXfx#7t{`@Bcy33>7x*lBcvL!nasz6vS0t$LQ%D$*qr6Rm&l?H@uhAuQm?L+#kX z)oT_3@{FfbEk=d49pR#y3n^uJ#IM)o9_(hLPd37oG`0PD)P{C%oQW*B(z26^IGKEn z_}nsrg;e{?*Yd?#$=)X#okKDO2kQp4p*$YG!UZeM1CfjDyNGBK(~eZz=2K{z7n_%k zBw)29Y6g)R(3V_J_N$YfVW!|kD>x>gx_E?Z@tGIU{#F*7i%M22BbaDEB=bT`rd~a! zNcdK z-4==B77HBdVlNtM`^)|z5z5b=M7xEXLG6<-II00()Nhx5yifbJS4F);5A0Yx%;6l= zP=oA4SAk*Pv%<{;#~W;AHCr&u_dMsBp9#;!m_>lFwT*Inu8j~x)fRv*H)A5eVkt}5 zDh(2YN7cA+t`jAIWW|txr`&6o<61j4pOu&}(HQTDyURMy`r?jU6@am=+%R)LSb_|l z7NCwlOKXc^y>}?0-dYgmeU?9EGCNhwRnNUu}2d%%9gD{o38+>Vt#PJD}x56AuZWx#dF=x$tY1roQbO>G}vIYN9{{Fah!|rsur= z-#hTQ^c$yM{pBsdb$U z{@@Gc#R<5C zYDZ{u$zd(6?FzL!{yfl$t1N}r?LI#py2NXVt>E=c_lCwsl6~3^Cewc6T7%5iS@DIC zD*SdNI;LrJgQ*b39*1`6YKz*tLx)|jx!fCO@%Q7VD44wmz`B6 zss(SSk356@wEx)Iqi^ZjwOo3sogVG=s}au9O%A~^tSlD#AbNKR$_VU5`EUQB< zFbr_HTrD8uMTD+U*qZKhv4!76;3=U!vaFtEsr?#pnPT3v&Bh^e_gT{Zb@5w_|xSp^08p0tIH%=APhBUT)S%bo?h6H zlv&@kTCY0ry{fq#1F^T(GinbTaJj6Do%nVP03spZHVhjavr?Qv5l8kp8&MTWl%G}`lA_|@>GULm4AtR1 z4lG^r#WWZ0f=Uw%1E`BM?7x8dhKo^#e!j_&-Qu_QHKr}AJVtmkuacH4wAZC;Z&VO` zX;Gy3Ohf?%5IlBis)a=Dyd`d^DJe!hwIou}O$GZDq_WQNK&BO~Civy;uv}ZCp&bkO zyOh4j4+wWzw|CTH&>eqmP+?eE3y)V0-P??fRvKoBr$$+>eL{T;g{~O=^b2<|?l_%96{cy2CQBkkGEwNe@PuA*% zvtuQV{hnDT0X3|Yu$5alT<7QwMAzY-XSb!zfE4k+iGo{X(&9_H?(kzDAr2Uxde~%W z8a~hu2>E--n%3amn%sU|x$}9Uqy!kAOkdrGUcYQVAlh*R91q0pCp~ zAK@R*pA|H%oDWPAYihmDKWWgN@Tu-F)vH`nvMTAo!&l3%IzO)V#3@d8qAbEg6(8A% zY)#D>vOKme2p3x~%-C@;(c`Vup@^v5Evxx}pc`8-X4^w<1p6wgDGE7=`&q+_uBG1` zQ)xNN)8SrI+b|_~To}=)fDnzk13jYg(Db25N4jO^SD=Qr3unS7IJuj7*7?NAB%Ty4 z_PZ#j71=lq{;yO*T>`%igSwIKN>b+ccJ#0o_w8Icyp)cLs3902c2!>s=ki{H2=t6^>JRj=Lpu6 zkt%o;<0xUz&*Q(XO>4E*9MV?MTtX*hwZ92EW=enW;_X5J%VaHx$fr+0V$z%@IN(tN z{eAU`uDsSlz9LwzUa67aLHm4p3_Bm*K#W-lQXyZ)0vBB#nM$1*A+j!Z@@OOQE6H$q zGr)vv14`WcF1!d^OzRk%Ph2L!i?>I23SMdLvJzIx$>nR8KrRSbVh+^C`ZvB6eLKT` z%_RGibpcUK{B+lhX})tvWoVcpLOZ`K`X07;?k@*qh@TN86%^oL|Io=cwOtMGZ< zH-?Utp8A1*11AM^P_(dATJYBx>7kmwE>RAJ14(aE4TQ#APirmI&M|*u{UY8JI15OW zJ3c(fejWUH-d+0^!K2^kt1B;T#{=_rkDR0J)(XV%D~9pp@7Z?rU_<*DCxt)66SOo_ zpJ^U5Na>i|Z21$-I)HK^z{bN9MdD(C!gZ$Gr_jFl+jB8tm7Z)|#+I08XR7|mfV#4k zH%aKBUp}Mc7Ju6ADmTHHa{<40cFd^Fa)UB89fjGwRX_Q*kly0s;bN*IJ?}116uFRqn zcQW?^eCNGcn%wFw(N(Sr;Y^~xDezGlEvB&h0tCN^=~6)4iOx89mb8#Hin})ZnVUX# z<@}QJ-Z4}G|2puVe==+g*fpkInsD38*-VEoZb3dlI;S#$qTsmd9^&a*lA7}KG!IAp zoYGIR__|))^DjO;J3^azf#Po*>C*GUp0kh zr>?F*VwRjS;m&z`mp70v7UEiD%46pd?@@07b%O&)8KIr8aqPD0bM?lP4&34|AC}}; zlWnM^IC51U3jfoA`wwb2J6?Mq*IzUv;tq~nl`&b6nA7#4dXxv7DC+``oaw6R@$J3( zE_Dtf!?73M=iLm%g&ECSI{p?3@YZ|$`&Zx90`2jxO1*?}+CA^BPyUb8fMmqrqZ|l9 z(sVs~Lzw9Z7}<_m z2u$EEsc4oPh>8MhkY4(Af4e3ZyTuOeF`0d!?%DA6jya0Mq(mHr1@RyKH|uXt)wL16 zQ-lTZ#B;od3SH5tSb!1m-k-(V8R(J~kmC(yx*mig5GOx_PWJdQ9QuV;TNIrfzRu}9 zXwhR!(bj*gM5yRAX`vSd<)Y>1Z7#@k85;Rb8JV;eYkR2+u}7Lm%pSj8u_XUPfJF9l zA9CA+W1E`?iT0rUt4eORne9EJ>R%;p#QP(@jxS}1-_egg(Qzl> zFlwzdK(LUtGXg1A;HV!;)E)qz)h^h&zCXBHyWAOa-{1Rz_IhW=a{u^`O7_>Nq>&Mt zJ3}EA#?3Rc#YjHHn$@wCi*{rbZKVx3cGX8ch8K@FWOJJpr!ijcq>S<+ns0w^QCtgGNrzbH7$d(bz6RnA8)zv$^00*uQ?4N+aWRa zq?WK%E5A#XV!9qOJ9qf-um6HM$)QKj6I@FVO?HP$Wuu2~YpeOY-nref?p9+!P>#Xr#VYV8T zS}>7GjbE6lRbT4w-qGJ<7%gIA%CtpmYepLHK*uVn{8^fl<=1sY(TZGk%$m%d+9kKW zF~t4ZMAHwece{nU^PW5Cb$63T2Sy_QU6AC&epjq&JU{cs=BA@e0i9+b%T*N0x-S)3 zb=0=1yA`@6NpXl(EO;Qr8eq$J^*ShtBmD{3<^w2>aqdCxk#Iv~F?rz}+SrsKu(otB=+O%1y8)?IdV@lNv0tOP`jc(Z?ECvG9``Im^k zv$S?Kv-HnpvLg5+W1X3QF}%FnBJTxg_u*MPD5VuZviBNiVpVwz~-9 zGPlTbuYRHVsmI(Ms5ue(?{J|`1c5JMJ!>FE-ssrB7AFn=pw6jl{vm{E1KBM6QU9;z zWAuvo10RT5Q1ps})B&NU?{|g%krSI(D=yo3pMMA3$_<5TjRBLD%|uT(EJD*`<0>>O zwkI2802`i$b6Gn>4mAxJ#k#Vh3ljZv^1H5cF_EY_$L!x zPLc%4ykp{>*jc;S3p)&ZgmcbGn>wyi&5z*xwQBQy?ARe3uWgI9#I=_jFmphvcYMpxsKJ-${e3EZ7-Heo?nrMf zx{gtJgJr{)nA13g=U3(+M%GawJP+5qfNGb?EeIGhAMZ zzt-{U3}tMYh9ZxZ$~?R|+?#|Nvym5{>snH9S5eRm(^T2uMF8>R)-C{_&N6mkSS~@^ z2Ykxs|6#rEN{l?RC$c+L;yRSvwqvQIYVoC%L-o$ZtM5+r%r~0Ci4OGZk>oO4z6k6s zNHCh{O^O;nuZ>adfXoTcp<)RJY zs)z-Y-qo2u9s62L&b&Z6jmKei3KkFY<76HwN7#&Dd|{0ouHC-WS+Z#*TA!I0R9kZn zKehbb_c%A0jvN6V-9W*CEQTw7gar+9qM<;I8)wDd1UdmOIJ?>UOhaY|4`K(-?dIU0 z&j7ca>|=6>KfM_FzxwFwphQ-m8SJYH0^_mF{TO+qZvM_F>RZD$DQMO(gJ@D7`+Ms6 zcHq*_)i1TDWCY;Zo_i1`$oQDHnHf-=VWM$t_jJHkV~dPPz+tr=RrPx6KTfKxRjEtI z3vK{k`v3kqY!F)&UG{Xy_i@M@a`ARxh#~w0KkeXino3_jS6KmpF&1w)jj&~o%!Tku zX6k`u#_1GD%`N=tk;Cy=IWo9?3(J_nla|}@*vU`~IEWB?6(Blq5pP_ZBy0<2)$3&&Mus`uv8Tkuz zGgLUesV&eJ8lbl`HZreGZK3CG+T}vjQ~DJXK#C|vPQ9(?Q7u}E0U1)0vQpeci@DEL zO;;^@v#Ps`Y&LL1qq_R%A4VK!PmIn7_&@@@Wc5~`*H1WGAdjTnf%}BCq-9wGDbL?3OS#E~gOPu%$nG2eZw$`7TKM?*#0illStT{|_5#CvzlMztex}!m67zPdGo>4m z#6eNEW@bU?b>K^&gjUr-373Z-8Gk4DHIf4>^`{Fm`)c6or`J+IA2geRvn_VGI%f^e zCmE@O=_)w}96&8Mc&EtoTGmum#-OJPnEu%?ytzi4`n zdZX_Nngv|S)5MCXC#Z+Zi+@J`(I349Xr}u^&T|)zCp;KKlm!$omY7j1dUFxfLTJw} z7z{8oR>L4M8&%9i5rS`9Vl(wi%wtX>k8)+FB%|jv7Vv2fn!Lp$LnDELY|*fG+NcBi zxdB3fcSw7a?_GNs)vLi(iMI8)_AaAbUL3=1b{UVk$gGj-*%MS>G z*<+-sg!;4G0Io~zSd5$gPBZnY$P<3U{|OXJlmFbb60`5M+s>wUYw~-9c1%+pdfFI-)e?*bHM>+;J>nE!Gm(DboVX=96I*tWtG63U`? zg_l5ULm`nCr?^(G^rjcGS*m;6=a>=T-@$&4t@Utm9 zF_Y%jI*a>z?K5uhG!wh>%I$Wx8ZKqF!2@Z)zlMVMCFfM&mQl&sH&DM5^gxRkX58v; zK$N>B>+VMd&S9q9?_dSRX0OU-$|)iKAttiO0RgW95>{maY6qcDnBNU$mUtd5a<@B ziS;{B^&Q3KP(A>Fi|JZS9d^ZXtj;y$GB2T4`TGMbD%`YSGasJ(5I(liB^I%kork`v zr<=R4e+>$ih&S~xtR|v#H;h2~rjJp@ZjWNHA-ByekTw}oS~kK64Gi$X_1V~RNG9LW zC=o;z&)YK{?Z=ZVJJX!j0&OJE;QhI0R#FkiF`VJ}%`tPtSRGSuD`lMCt1{_nxdjntQ>|{s6PMD0Z>-;T^VY=Vb>*4N zZkzlGUey0p>gHV$AfUs;`oSFLTgUCo380jd-}`$PMy- z!`2j7^~{PoTx)EYra*S|{9JDhE&$K_SEiXiZ|Y%=Z9~k*Te{Z9oTrO;!^CfM+`w;- zIve2b+l{{3JJ6&xXW%dO`<&)C+SMC#+8{S80tp5{ejoByf|*upNAE{N0x_Tw|eT-r&^ShgTm z6Dy&mVgB2b3M~r!$-#k9s4*#o%~a||02`t0ejtUSlQy?Mf%A;V@{Rn17^ZGhb75Gp zZRPgDmrXptbO;;4ls`!JSlMs1r{k$PUzh#2;(gL6s^e&DcCd_*@T?}04e;2`sTAfk zveZ&&bKfYc+wZo_Je#gVNL`nzBIDo{j7xk+-VSttTpQbUc5Uz=t>Lja9=AccQS&!C z2c4!Fa#gS_~DrVk?d zjP+R8)r>3r+gc}ViYfM5ZwTL9skkphDwmwd%J$1vn469#_ZV@wRT}msKh>-NI-gMO zu!a#DepWrz_aN#~64+Ohpu*Hr_-uNAq7gWQ8RdvKcFh;CU&YAr1?=D}i%uSoW`LKw zs|5Tza|$n&WyKuh;0zD`Xj=*G z0v_xmPcjlN?GH8G6%Pi@pbNDBE`3fz^D%zp{K%NwY0Ap|z=_#v2&+EhvrJ^%K?7dOjH0$+a;V#|{f;p*21Nt<%CZ61 zNGJ)7ut@E(YZ`-LQ!0=_CiPv{Vf@kAp|D*y-F3U^;edp#r3Z78o?jY`1u#>Sbsg6N zJ!-Y3v%c7EGo$s_{37gS*Ry8fNZ?+&WlrtZQ!rdQ;^t17{|X2AOn+0gg=Sr{iQ_i>-#Dkf{I39HDZ62toqO&@nUJ2q98doOW&Q`YYX_N?#K){ALyO*HdFR08@2>MEOQ}K3^3hZ=b&PkOurx(FXTPa30tczlt$6 zLlxtn3WcuS02gN;$wCKKKzDD9JsAacQ-dYMOXjjG&SGBBM6z?LZ^0dMbV-X}nXcKE zG2|YWR}zT@rXQ~WZ5FVO2#;9Gwwq4@X*d-(JEm&E>Xk|5owFLR0p->k4R5JGy!)v? zm&`Oxw|GK*bsDc zyChoz^0aMaK1!?is)j;&nYbNEecsG~HQ84LPKZ4Dc%?;gYh0gs5U#P=C^(9HH_{&3T+u~M=GZ(8=U z{s@cgWre8L+(Mb8n&b`#c3Z+HuQ6vbww259{aF}cgSv8~fE+)9ICejm84T+AW(v$+ z>m(VB0Ak?C_RzX*Ec!Je zr_~p{cywAILkX(@LS5-tl!QvWkwEt8u!#z+ABlJfYDRsBxWSPhJmWx-JaM_%nzd-% zU?w=??9v<1ao76AHkM*4!D? zHoo-xc-^|J8@e{OAQbOwJ`bIzaXKLcq)dM67vp{DG5k_yv?;_EE_DYV-%gA9pyaDN z!@1tTAu-sHdy>cdm^fo?*;(dH4(m8WA!rsWW-0eG(fP3e6Rmxx) zq7;_d)Ox&L;KQfhtiWza^*_oLfDJ6hr;&xgf{`9jWnrAV^i z`m`9=L9Q_76)7{o?$&zsN=HKgIWbN`EQSp8RyF{ zLrm4MCV|$(gbXIv3a=9L(_15;8NhD0-xs|>x7YZ52H(_v1rIh#*hWAL$ILJt;JZD_ znKy%r4JFn>Y#5>}82;?hPQUhxc$%Or?$7M%KrV~g$&pZB#e0jD)g|H_xwPO$^NgUG zhgBq0kh8JB9dOBi_wG{)JXoRm9vBgtz1_#cMEu25F~3=vrlg1)it09^>n7rk0FVfX zb5o`yGRZ>Q4kcb`#r297dM7!x^1m_qpE~k1U*Ho}`0?aW;pu})*PreaTrZL=6kEUb z`D};ot+iZ|DwzDHULIC}@~VCvO9>(h?6b*s>l-QCI7JRt zo2*qAd*)VEDWX1KG;0wx+`b->VJ3BR<2k18l^* zjcU${^(ttG4U#n)3NK`A9m7$6xqm_0giE0*w)5C~&xwod0#MdwZgX8RPLM;X;14J~ z?g=(;yJ~9P;FS2x@GIgMY>+5u3ATV2c_wKYYOcbjfE_w-K%PG!t05K%lr+!9Xt$L| zOm#Rz=iUjBSm#^Krh&g7r6Idf6(9Lm)|huUr>>&vRF7h+X2HY9d-0D+HJ0Y7Sd<~S zDcgEhHFl95ZUXiOBV!`{r)tgi0@t6w`XFo-Q%e;2{$N72kG5m47>c@|8pc*@wy}FQ zHDYUQ!$j{&p<>G?Em4d68Q^&)XhqNl zq79L4w`?{a-Yw#D8b!;kznOVbbDfu(=|az1+JM(0>!l;nyQ<)xN@iBHRBA^C;|eo* z1t76;XjJ~y)K`#fug|D%_jFW1?px@@O${5R@X8{t9Y4G#@GMPAOhT*dsw7~UU?06v z&g|`jTN=Kx(KMf%F1spKUKG%fnQw^oDXXw1p8>6r*;Hmuzh8eM5vRD+X-A_@>i@VZ z18LW)t{I#D2|bJ(6z@-81rhSv-c^8Hfx}`2R_#h$lXi${qwGJhy9j%(8lnk#p)5vv zap`m))YSiGFG@y*UfC{_;z(CR9T)e=mbI7=WWG|?V{X6?tZP+H*U8*T?F0Ys%wIZn zUGm?eqssqjO3F|6E5ys#bngGpz**S*=A*EskLzerF|sN`q-5Y2nAfn=pz-=cS^6qn zU50M9x_@dT`y9y}*yLdlBpSd pOa&+J=L6_H;D!p2@mn2Z+5Kv&z!!hJ<~bDHFeM zjx%<%VGAuLE{BV4siGCDqP z(zr}wH$S$Se`Y49HNcJFtAk9NN|MNMT%m;^2re^e35pwDYh73N{3B^^$-cVbjWkA6 zUT6>SpbblgI2YBALLROL$4?sjD^PEs$eD)uJLJl8^F#<$Ey=q;)F)vT#pvL1_Hfv; zs(|_MjLSNvW64Iluu)XO^4zWYYip?rw=(OH*BecceNZhHt^4Agc1>c!Hv*}^whQPd_+q_OL;HT5M0ClV`N`Zjj+$rU&j=3mhgXL0PJ9l6ip7^y* zh|&UA(lcJC29a&e#Q@r)owyAJ@|g;Xj- z40+UXYrcW0CWnv?8>=y|D4UW-<(a`>aR(T>bYa*)O<#9gCn|~Hj+m5ccoZwtJikNp zNiU=Q5R$f&kM2-hjLv|R0klrASw(&d!#7FI-LW}^zPN?bhBXpwS08s?Ltf9m1tPrf ziQ~W{d1sOu>vF~&#RAAv*?Ne(ny<$a)%3KM%h_mRC*O-13XUiyKjv<*^>Xj-1bW;~ z)qTEEb=r-P&@s&XGN?1IYPjev-WaP z#p{j3o{ZkxbyxsI^#eOV_KuuiI*6rov*H9WIbz1+D*4>-`EVmE<1ew)yXnNe;CAr` z_;2JAy4}%J=7}osiG`jAZ7`+}8EdGrpyny|~G>MBRp4){GnZ39~7jRNy})lAg=6&k}OGHy3*qbzkx@X~U5KMrt# z_!3Ncb*)~;P*kOfwBQe#=3|*Hg$U$KEK@FIF&DN_4BVENgeOA`zEK@gY1`k&g+z`8 z-(Y6nk)&zP8ig&3ashN!-cMgbG}*dujDhV^By6%sVwwpKzSlAfc}vhd#a_}Z;3?1e zBeHVDWM|6P@5T-wJV;#pr$a_@s`J>?N$4(5rX7~-XC_FE?sK;OlO>pYEZ|aL9 zSxtCnp9k8~Ay9zwJ_m?DDC9=!}-_@RP|B zgUOZmxoG#Fm2pV1G+M7$LAq_4lqu3m@5C861DSH}#J z5%!cK_y|2cr5|O@H!hP5+(5j9hGb;Q&2tG{@9ZY)#nz!83-fpxf`W^q zffW$*#ktTgXV-dKppfO>&zrYaa2W+KIuF9P?8=O!!fy~I�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 Date: Tue, 9 Oct 2018 23:51:42 +0200 Subject: [PATCH 06/18] Fixed bugs in input handling --- .../warppi/extra/tetris/TetrisGame.java | 15 +++++--- .../warppi/extra/tetris/TetrisScreen.java | 2 +- .../warppi/gui/expression/ExtraMenu.java | 3 +- .../warppi/gui/expression/blocks/Block.java | 18 +++++++++- .../gui/expression/blocks/BlockChar.java | 15 +++++--- .../gui/expression/blocks/BlockContainer.java | 34 +++++++++++-------- .../gui/expression/blocks/BlockDivision.java | 16 +++++---- .../blocks/BlockExponentialNotation.java | 21 ++++++++++++ .../gui/expression/blocks/BlockLogarithm.java | 21 ++++++++---- .../expression/blocks/BlockNumericChar.java | 10 ++++++ .../expression/blocks/BlockParenthesis.java | 8 ++--- .../blocks/BlockParenthesisAbstract.java | 16 ++++++--- .../gui/expression/blocks/BlockPower.java | 9 ++--- .../gui/expression/blocks/BlockPower2.java | 9 ++--- .../gui/expression/blocks/BlockSine.java | 8 ++--- .../expression/blocks/BlockSquareRoot.java | 11 +++--- .../gui/expression/blocks/BlockUndefined.java | 8 ++--- .../gui/expression/blocks/BlockVariable.java | 14 ++++---- .../expression/containers/InputContainer.java | 5 ++- .../containers/NormalInputContainer.java | 1 - .../graphicengine/impl/swing/SwingEngine.java | 2 +- 21 files changed, 165 insertions(+), 81 deletions(-) diff --git a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrisGame.java b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrisGame.java index a53419f4..c230c1c0 100644 --- a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrisGame.java +++ b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrisGame.java @@ -6,21 +6,26 @@ public class TetrisGame { private BlockType[] grid; private BlockType[] hovergrid; private BlockType[] renderedGrid; - private GameStatus gameStatus = GameStatus.INITIAL; - private int score = 0; - private double currentTime = 0; + private GameStatus gameStatus; + private int score; + private double currentTime; public TetrisGame() { - + resetVariables(); } void playAgain() { + resetVariables(); + gameStatus = GameStatus.PLAYING; + } + + private void resetVariables() { grid = new BlockType[WIDTH * HEIGHT]; hovergrid = new BlockType[WIDTH * HEIGHT]; renderedGrid = new BlockType[WIDTH * HEIGHT]; score = 0; currentTime = 0; - gameStatus = GameStatus.PLAYING; + gameStatus = GameStatus.INITIAL; } public void gameTick(float dt, boolean leftPressed, boolean rightPressed, boolean downPressed, boolean okPressed, boolean backPressed) { diff --git a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrisScreen.java b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrisScreen.java index 3d1ead18..04606048 100644 --- a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrisScreen.java +++ b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrisScreen.java @@ -49,7 +49,7 @@ public class TetrisScreen extends Screen { if (TetrisScreen.skin == null) { TetrisScreen.skin = Engine.INSTANCE.getHardwareDevice().getDisplayManager().engine.loadSkin("/tetrisskin.png"); } - StaticVars.windowZoom.onNext(1f); + StaticVars.windowZoom.onNext(2f); } catch (final IOException e) { e.printStackTrace(); } diff --git a/core/src/main/java/it/cavallium/warppi/gui/expression/ExtraMenu.java b/core/src/main/java/it/cavallium/warppi/gui/expression/ExtraMenu.java index e28d6c5c..5c8b2f56 100644 --- a/core/src/main/java/it/cavallium/warppi/gui/expression/ExtraMenu.java +++ b/core/src/main/java/it/cavallium/warppi/gui/expression/ExtraMenu.java @@ -5,6 +5,7 @@ import java.util.Arrays; import it.cavallium.warppi.event.KeyboardEventListener; import it.cavallium.warppi.gui.expression.blocks.Block; import it.cavallium.warppi.gui.expression.blocks.BlockVariable; +import it.cavallium.warppi.gui.expression.blocks.TreeContainer; import it.cavallium.warppi.gui.graphicengine.GraphicEngine; import it.cavallium.warppi.gui.graphicengine.Renderer; @@ -45,7 +46,7 @@ public abstract class ExtraMenu implements KeyboardEventListene return false; } - public abstract ExtraMenu clone(InputContext ic); + public abstract ExtraMenu clone(final TreeContainer parent, InputContext ic); public abstract ExtraMenu clone(T newBlockVariable); diff --git a/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/Block.java b/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/Block.java index 6dda310c..638c596d 100644 --- a/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/Block.java +++ b/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/Block.java @@ -18,6 +18,22 @@ public abstract class Block implements TreeBlock, GraphicalElement { protected int height; protected int line; protected TreeContainer parent; + + public Block() { + + } + + /** + * Copy + * @param b + */ + public Block(TreeContainer parent, Block b) { + this.small = b.small; + this.width = b.width; + this.height = b.height; + this.line = b.line; + this.parent = parent; + } /** * @@ -95,5 +111,5 @@ public abstract class Block implements TreeBlock, GraphicalElement { this.parent = parent; } - public abstract Block clone(InputContext ic); + public abstract Block clone(TreeContainer parent, InputContext ic); } diff --git a/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/BlockChar.java b/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/BlockChar.java index b4a1a2cb..e9db9436 100644 --- a/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/BlockChar.java +++ b/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/BlockChar.java @@ -18,9 +18,14 @@ public class BlockChar extends Block { recomputeDimensions(); } - public BlockChar(final char ch, InputContext ic) { - this.ch = ch; - recomputeDimensions(); + /** + * Copy + * @param b + * @param ic + */ + protected BlockChar(final TreeContainer parent, final BlockChar b, InputContext ic) { + super(parent, b); + this.ch = b.ch; } @Override @@ -83,8 +88,8 @@ public class BlockChar extends Block { } @Override - public BlockChar clone(InputContext ic) { - return new BlockChar(ch, ic); + public BlockChar clone(final TreeContainer parent, InputContext ic) { + return new BlockChar(parent, this, ic); } } diff --git a/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/BlockContainer.java b/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/BlockContainer.java index 5554fdfa..96abf8ce 100644 --- a/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/BlockContainer.java +++ b/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/BlockContainer.java @@ -79,24 +79,25 @@ public class BlockContainer implements TreeContainer, GraphicalElement { recomputeDimensions(); } - private BlockContainer(BlockContainer old, InputContext ic) { + private BlockContainer(final TreeBlock parent, BlockContainer old, InputContext ic) { this.autoMinimums = old.autoMinimums; this.content = new ObjectArrayList<>(); for (Block b : old.content) { - this.content.add(b.clone(ic)); + this.content.add(b.clone(this, ic)); } + this.height = old.height; this.line = old.line; this.minHeight = old.minHeight; this.minWidth = old.minWidth; - this.parent = old.parent; + this.parent = parent; this.small = old.small; this.width = old.width; this.withBorder = old.withBorder; } - public BlockContainer clone(InputContext ic) { - return new BlockContainer(this, ic); + public BlockContainer clone(final TreeBlock parent, InputContext ic) { + return new BlockContainer(parent, this, ic); } @Override @@ -261,18 +262,21 @@ public class BlockContainer implements TreeContainer, GraphicalElement { innerContainersBeforeCaret++; } } - removeAt(pos - 1); - if (blocks != null) { - ObjectListIterator blocksIterator = blocks.iterator(); - int blockNum = 0; - while (blocksIterator.hasNext()) { - Block block = blocksIterator.next(); - addBlockUnsafe(pos - 1 + blockNum, block); - blockNum++; + // If the caret is at the end of a block with inner containers don't delete anything and enter into that block. + if (innerContainers == null || (innerContainers.size() - innerContainersBeforeCaret != 0)) { + removeAt(pos - 1); + if (blocks != null) { + ObjectListIterator blocksIterator = blocks.iterator(); + int blockNum = 0; + while (blocksIterator.hasNext()) { + Block block = blocksIterator.next(); + addBlockUnsafe(pos - 1 + blockNum, block); + blockNum++; + } } + caret.setPosition(caretOldPos - innerContainersBeforeCaret); + removed = true; } - caret.setPosition(caretOldPos - innerContainersBeforeCaret); - removed = true; } } caret.skip(1); diff --git a/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/BlockDivision.java b/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/BlockDivision.java index f3774d06..3702cec5 100644 --- a/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/BlockDivision.java +++ b/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/BlockDivision.java @@ -27,10 +27,14 @@ public class BlockDivision extends Block { recomputeDimensions(); } - private BlockDivision(BlockDivision old, InputContext ic) { - containerUp = old.containerUp.clone(ic); - containerDown = old.containerDown.clone(ic); - recomputeDimensions(); + private BlockDivision(final TreeContainer parent, BlockDivision old, InputContext ic) { + super(parent, old); + containerUp = old.containerUp.clone(this, ic); + containerDown = old.containerDown.clone(this, ic); + paddingLeftLower = old.paddingLeftLower; + paddingLeftUpper = old.paddingLeftUpper; + h1 = old.h1; + System.out.println(String.join(",", ""+h1, ""+old.h1, ""+line, ""+old.line)); } @Override @@ -144,7 +148,7 @@ public class BlockDivision extends Block { } @Override - public BlockDivision clone(InputContext ic) { - return new BlockDivision(this, ic); + public BlockDivision clone(final TreeContainer parent, InputContext ic) { + return new BlockDivision(parent, this, ic); } } diff --git a/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/BlockExponentialNotation.java b/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/BlockExponentialNotation.java index d19d4074..cdb25f9d 100644 --- a/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/BlockExponentialNotation.java +++ b/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/BlockExponentialNotation.java @@ -1,6 +1,7 @@ package it.cavallium.warppi.gui.expression.blocks; import it.cavallium.warppi.gui.expression.Caret; +import it.cavallium.warppi.gui.expression.InputContext; import it.cavallium.warppi.gui.graphicengine.GraphicEngine; import it.cavallium.warppi.gui.graphicengine.Renderer; @@ -8,6 +9,21 @@ public class BlockExponentialNotation extends BlockPower { private int bw; private int bh; + public BlockExponentialNotation() { + super(); + } + + /** + * Copy + * @param old + * @param ic + */ + private BlockExponentialNotation(final TreeContainer parent, BlockExponentialNotation old, InputContext ic) { + super(parent, old, ic); + this.bw = old.bw; + this.bh = old.bh; + } + @Override public void draw(final GraphicEngine ge, final Renderer r, final int x, final int y, final Caret caret) { BlockContainer.getDefaultFont(small).use(ge); @@ -23,4 +39,9 @@ public class BlockExponentialNotation extends BlockPower { bh = BlockContainer.getDefaultCharHeight(small); width += bw; } + + @Override + public BlockExponentialNotation clone(final TreeContainer parent, InputContext ic) { + return new BlockExponentialNotation(parent, this, ic); + } } diff --git a/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/BlockLogarithm.java b/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/BlockLogarithm.java index feba80af..8699ed4d 100644 --- a/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/BlockLogarithm.java +++ b/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/BlockLogarithm.java @@ -40,10 +40,19 @@ public class BlockLogarithm extends Block implements IParenthesis { recomputeDimensions(); } - private BlockLogarithm(BlockLogarithm old, InputContext ic) { - containerBase = old.containerBase.clone(ic); - containerNumber = old.containerNumber.clone(ic); - recomputeDimensions(); + private BlockLogarithm(final TreeContainer parent, BlockLogarithm old, InputContext ic) { + super(parent, old); + containerBase = old.containerBase.clone(this, ic); + containerNumber = old.containerNumber.clone(this, ic); + this.prw = old.prw; + this.bw = old.bw; + this.bh = old.bh; + this.bl = old.bl; + this.chw = old.chw; + this.chh = old.chh; + this.schh = old.schh; + this.nmbh = old.nmbh; + this.toph = old.toph; } @Override @@ -183,8 +192,8 @@ public class BlockLogarithm extends Block implements IParenthesis { } @Override - public BlockLogarithm clone(InputContext ic) { - return new BlockLogarithm(this, ic); + public BlockLogarithm clone(final TreeContainer parent, InputContext ic) { + return new BlockLogarithm(parent, this, ic); } } diff --git a/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/BlockNumericChar.java b/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/BlockNumericChar.java index 7976c2af..ed44c709 100644 --- a/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/BlockNumericChar.java +++ b/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/BlockNumericChar.java @@ -1,9 +1,19 @@ package it.cavallium.warppi.gui.expression.blocks; +import it.cavallium.warppi.gui.expression.InputContext; + public class BlockNumericChar extends BlockChar { public BlockNumericChar(final char ch) { super(ch); } + private BlockNumericChar(final TreeContainer parent, BlockNumericChar old, InputContext ic) { + super(parent, old, ic); + } + + @Override + public BlockNumericChar clone(final TreeContainer parent, InputContext ic) { + return new BlockNumericChar(parent, this, ic); + } } diff --git a/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/BlockParenthesis.java b/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/BlockParenthesis.java index 395021a7..e4cfd2cf 100644 --- a/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/BlockParenthesis.java +++ b/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/BlockParenthesis.java @@ -15,8 +15,8 @@ public class BlockParenthesis extends BlockParenthesisAbstract { super(blocks); } - private BlockParenthesis(BlockParenthesis old, InputContext ic) { - super(old, ic); + private BlockParenthesis(final TreeContainer parent, BlockParenthesis old, InputContext ic) { + super(parent, old, ic); } @Override @@ -38,8 +38,8 @@ public class BlockParenthesis extends BlockParenthesisAbstract { } @Override - public BlockParenthesis clone(InputContext ic) { - return new BlockParenthesis(this, ic); + public BlockParenthesis clone(final TreeContainer parent, InputContext ic) { + return new BlockParenthesis(parent, this, ic); } } diff --git a/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/BlockParenthesisAbstract.java b/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/BlockParenthesisAbstract.java index eefe3b39..bfff55c8 100644 --- a/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/BlockParenthesisAbstract.java +++ b/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/BlockParenthesisAbstract.java @@ -31,10 +31,18 @@ public abstract class BlockParenthesisAbstract extends Block implements IParenth recomputeDimensions(); } - BlockParenthesisAbstract(BlockParenthesisAbstract old, InputContext ic) { - containerNumber = old.containerNumber.clone(ic); - prefix = old.prefix == null ? null : new String(old.prefix); - recomputeDimensions(); + /** + * Copy + * @param old + * @param ic + */ + BlockParenthesisAbstract(final TreeContainer parent, BlockParenthesisAbstract old, InputContext ic) { + super(parent, old); + containerNumber = old.containerNumber.clone(this, ic); + prefix = old.prefix; + prw = old.prw; + chw = old.chw; + chh = old.chh; } public BlockParenthesisAbstract(final ObjectArrayList blocks) { diff --git a/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/BlockPower.java b/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/BlockPower.java index 7530d25a..a665fa6d 100644 --- a/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/BlockPower.java +++ b/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/BlockPower.java @@ -20,8 +20,9 @@ public class BlockPower extends Block { recomputeDimensions(); } - private BlockPower(BlockPower old, InputContext ic) { - this.containerExponent = old.containerExponent.clone(ic); + protected BlockPower(final TreeContainer parent, BlockPower old, InputContext ic) { + super(parent, old); + this.containerExponent = old.containerExponent.clone(this, ic); } @Override @@ -100,7 +101,7 @@ public class BlockPower extends Block { } @Override - public BlockPower clone(InputContext ic) { - return new BlockPower(this, ic); + public BlockPower clone(final TreeContainer parent, InputContext ic) { + return new BlockPower(parent, this, ic); } } diff --git a/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/BlockPower2.java b/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/BlockPower2.java index bc152ab4..e586a463 100644 --- a/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/BlockPower2.java +++ b/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/BlockPower2.java @@ -21,8 +21,9 @@ public class BlockPower2 extends Block { recomputeDimensions(); } - private BlockPower2(BlockPower2 old, InputContext ic) { - this.containerExponent = old.containerExponent.clone(ic); + private BlockPower2(final TreeContainer parent, BlockPower2 old, InputContext ic) { + super(parent, old); + this.containerExponent = old.containerExponent.clone(this, ic); } @Override @@ -106,7 +107,7 @@ public class BlockPower2 extends Block { } @Override - public BlockPower2 clone(InputContext ic) { - return new BlockPower2(this, ic); + public BlockPower2 clone(final TreeContainer parent, InputContext ic) { + return new BlockPower2(parent, this, ic); } } diff --git a/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/BlockSine.java b/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/BlockSine.java index 468e6736..08998ba9 100644 --- a/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/BlockSine.java +++ b/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/BlockSine.java @@ -13,8 +13,8 @@ public class BlockSine extends BlockParenthesisAbstract { super("SIN"); } - private BlockSine(BlockSine old, InputContext ic) { - super(old, ic); + private BlockSine(final TreeContainer parent, BlockSine old, InputContext ic) { + super(parent, old, ic); } @Override @@ -24,7 +24,7 @@ public class BlockSine extends BlockParenthesisAbstract { } @Override - public Block clone(InputContext ic) { - return new BlockSine(this, ic); + public BlockSine clone(final TreeContainer parent, InputContext ic) { + return new BlockSine(parent, this, ic); } } diff --git a/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/BlockSquareRoot.java b/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/BlockSquareRoot.java index 004d06bd..4bdfe9ea 100644 --- a/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/BlockSquareRoot.java +++ b/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/BlockSquareRoot.java @@ -22,9 +22,10 @@ public class BlockSquareRoot extends Block { recomputeDimensions(); } - private BlockSquareRoot(BlockSquareRoot old, InputContext ic) { - this.containerNumber = old.containerNumber.clone(ic); - recomputeDimensions(); + private BlockSquareRoot(final TreeContainer parent, BlockSquareRoot old, InputContext ic) { + super(parent, old); + this.containerNumber = old.containerNumber.clone(this, ic); + this.h1 = old.h1; } @Override @@ -118,7 +119,7 @@ public class BlockSquareRoot extends Block { } @Override - public BlockSquareRoot clone(InputContext ic) { - return new BlockSquareRoot(this, ic); + public BlockSquareRoot clone(final TreeContainer parent, InputContext ic) { + return new BlockSquareRoot(parent, this, ic); } } diff --git a/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/BlockUndefined.java b/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/BlockUndefined.java index 67ee0877..46289191 100644 --- a/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/BlockUndefined.java +++ b/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/BlockUndefined.java @@ -16,8 +16,8 @@ public class BlockUndefined extends Block { recomputeDimensions(); } - private BlockUndefined(BlockUndefined old, InputContext ic) { - recomputeDimensions(); + private BlockUndefined(final TreeContainer parent, BlockUndefined old, InputContext ic) { + super(parent, old); } @Override @@ -76,8 +76,8 @@ public class BlockUndefined extends Block { } @Override - public BlockUndefined clone(InputContext ic) { - return new BlockUndefined(this, ic); + public BlockUndefined clone(final TreeContainer parent, InputContext ic) { + return new BlockUndefined(parent, this, ic); } } diff --git a/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/BlockVariable.java b/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/BlockVariable.java index 412b4d33..aa59d8ee 100644 --- a/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/BlockVariable.java +++ b/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/BlockVariable.java @@ -45,7 +45,8 @@ public class BlockVariable extends Block { recomputeDimensions(); } - private BlockVariable(BlockVariable old, InputContext ic) { + private BlockVariable(final TreeContainer parent, BlockVariable old, InputContext ic) { + super(parent, old); this.ic = ic; this.ch = old.ch; type = old.type; @@ -53,8 +54,7 @@ public class BlockVariable extends Block { typeDirtyID = old.typeDirtyID; this.typeLocked = old.typeLocked; menu = old.menu == null ? null : new VariableMenu(old.menu, this); - retrieveValue(); - recomputeDimensions(); + mustRefresh = old.mustRefresh; } private void retrieveValue() { @@ -273,8 +273,8 @@ public class BlockVariable extends Block { } @Override - public VariableMenu clone(InputContext ic) { - return new VariableMenu(this, block.clone(ic)); + public VariableMenu clone(final TreeContainer parent, InputContext ic) { + return new VariableMenu(this, block.clone(parent, ic)); } } @@ -295,7 +295,7 @@ public class BlockVariable extends Block { } @Override - public BlockVariable clone(InputContext ic) { - return new BlockVariable(this, ic); + public BlockVariable clone(final TreeContainer parent, InputContext ic) { + return new BlockVariable(parent, this, ic); } } diff --git a/core/src/main/java/it/cavallium/warppi/gui/expression/containers/InputContainer.java b/core/src/main/java/it/cavallium/warppi/gui/expression/containers/InputContainer.java index 72a88e35..f31b413e 100644 --- a/core/src/main/java/it/cavallium/warppi/gui/expression/containers/InputContainer.java +++ b/core/src/main/java/it/cavallium/warppi/gui/expression/containers/InputContainer.java @@ -18,7 +18,6 @@ import it.cavallium.warppi.util.Error; import it.unimi.dsi.fastutil.objects.ObjectArrayList; public abstract class InputContainer implements GraphicalElement, InputLayout { - private static final long serialVersionUID = 923589369317765667L; protected BlockContainer root; protected Caret caret; private static final float CARET_DURATION = 0.5f; @@ -61,11 +60,11 @@ public abstract class InputContainer implements GraphicalElement, InputLayout { */ protected InputContainer(InputContainer old, InputContext ic) { this.caretTime = old.caretTime; - this.extra = old.extra == null ? null : old.extra.clone(ic); + this.extra = old.extra == null ? null : old.extra.clone(null, ic); this.maxPosition = old.maxPosition; this.caret = old.caret == null ? null : new Caret(old.caret); this.inputContext = ic; - this.root = old.root == null ? null : old.root.clone(ic); + this.root = old.root == null ? null : old.root.clone(null, ic); this.parsed = old.parsed; } diff --git a/core/src/main/java/it/cavallium/warppi/gui/expression/containers/NormalInputContainer.java b/core/src/main/java/it/cavallium/warppi/gui/expression/containers/NormalInputContainer.java index 1de13251..fa3d6ad5 100644 --- a/core/src/main/java/it/cavallium/warppi/gui/expression/containers/NormalInputContainer.java +++ b/core/src/main/java/it/cavallium/warppi/gui/expression/containers/NormalInputContainer.java @@ -46,7 +46,6 @@ public class NormalInputContainer extends InputContainer { * @param userInput * @param ic */ - @SuppressWarnings("deprecation") public NormalInputContainer(InputContainer old, InputContext ic) { super(old, ic); } diff --git a/desktop/src/main/java/it/cavallium/warppi/gui/graphicengine/impl/swing/SwingEngine.java b/desktop/src/main/java/it/cavallium/warppi/gui/graphicengine/impl/swing/SwingEngine.java index 745c50a9..2af34820 100644 --- a/desktop/src/main/java/it/cavallium/warppi/gui/graphicengine/impl/swing/SwingEngine.java +++ b/desktop/src/main/java/it/cavallium/warppi/gui/graphicengine/impl/swing/SwingEngine.java @@ -54,7 +54,7 @@ public class SwingEngine implements GraphicEngine { exitSemaphore = new Semaphore(0); INSTANCE = new SwingWindow(this); setResizable(Engine.getPlatform().getSettings().isDebugEnabled()); - setDisplayMode(StaticVars.screenSize[0], StaticVars.screenSize[1]); + setDisplayMode((int) (StaticVars.screenSize[0] / StaticVars.windowZoom.getLastValue()), (int) (StaticVars.screenSize[1] / StaticVars.windowZoom.getLastValue())); INSTANCE.setVisible(true); initialized = true; if (onInitialized != null) From b7407886dd0c1e37f3bb80abbd2407de97ffd855 Mon Sep 17 00:00:00 2001 From: Cavallium Date: Fri, 12 Oct 2018 23:20:12 +0200 Subject: [PATCH 07/18] Caching results --- .../it/cavallium/warppi/math/Function.java | 2 +- .../warppi/math/FunctionDynamic.java | 4 +- .../warppi/math/FunctionOperator.java | 2 +- .../cavallium/warppi/math/FunctionSingle.java | 2 + .../warppi/math/functions/EmptyNumber.java | 59 ------------------- .../warppi/math/functions/Variable.java | 1 + .../warppi/math/solver/MathSolver.java | 51 +++++++++++++--- 7 files changed, 51 insertions(+), 70 deletions(-) delete mode 100644 core/src/main/java/it/cavallium/warppi/math/functions/EmptyNumber.java diff --git a/core/src/main/java/it/cavallium/warppi/math/Function.java b/core/src/main/java/it/cavallium/warppi/math/Function.java index 43bfdc32..3ed2c9fc 100644 --- a/core/src/main/java/it/cavallium/warppi/math/Function.java +++ b/core/src/main/java/it/cavallium/warppi/math/Function.java @@ -71,7 +71,7 @@ public interface Function { * @throws InterruptedException */ ObjectArrayList simplify(Rule rule) throws Error, InterruptedException; - + /** * * @param context diff --git a/core/src/main/java/it/cavallium/warppi/math/FunctionDynamic.java b/core/src/main/java/it/cavallium/warppi/math/FunctionDynamic.java index 5c0bbd24..5c959bc0 100644 --- a/core/src/main/java/it/cavallium/warppi/math/FunctionDynamic.java +++ b/core/src/main/java/it/cavallium/warppi/math/FunctionDynamic.java @@ -9,6 +9,8 @@ import it.cavallium.warppi.util.Utils; import it.unimi.dsi.fastutil.objects.ObjectArrayList; public abstract class FunctionDynamic implements Function { + private boolean simplified; + public FunctionDynamic(final MathContext root) { this.root = root; functions = new Function[] {}; @@ -131,7 +133,7 @@ public abstract class FunctionDynamic implements Function { @Override public abstract FunctionDynamic clone(); - + @Override public int hashCode() { return functions.hashCode() + 883 * super.hashCode(); diff --git a/core/src/main/java/it/cavallium/warppi/math/FunctionOperator.java b/core/src/main/java/it/cavallium/warppi/math/FunctionOperator.java index 91d79819..c43de8b2 100644 --- a/core/src/main/java/it/cavallium/warppi/math/FunctionOperator.java +++ b/core/src/main/java/it/cavallium/warppi/math/FunctionOperator.java @@ -7,7 +7,7 @@ import it.cavallium.warppi.util.Utils; import it.unimi.dsi.fastutil.objects.ObjectArrayList; public abstract class FunctionOperator implements Function { - + /** * Create a new instance of FunctionOperator. The Math Context will be the * same of value1's. diff --git a/core/src/main/java/it/cavallium/warppi/math/FunctionSingle.java b/core/src/main/java/it/cavallium/warppi/math/FunctionSingle.java index 1f7ee462..9f17d25f 100644 --- a/core/src/main/java/it/cavallium/warppi/math/FunctionSingle.java +++ b/core/src/main/java/it/cavallium/warppi/math/FunctionSingle.java @@ -6,6 +6,8 @@ import it.unimi.dsi.fastutil.objects.ObjectArrayList; public abstract class FunctionSingle implements Function { + private boolean simplified; + /** * Create a new instance of FunctionSingle. The Math Context will be the * same of value's. diff --git a/core/src/main/java/it/cavallium/warppi/math/functions/EmptyNumber.java b/core/src/main/java/it/cavallium/warppi/math/functions/EmptyNumber.java deleted file mode 100644 index 7b0327f0..00000000 --- a/core/src/main/java/it/cavallium/warppi/math/functions/EmptyNumber.java +++ /dev/null @@ -1,59 +0,0 @@ -package it.cavallium.warppi.math.functions; - -import it.cavallium.warppi.gui.expression.blocks.Block; -import it.cavallium.warppi.math.Function; -import it.cavallium.warppi.math.MathContext; -import it.cavallium.warppi.math.rules.Rule; -import it.cavallium.warppi.util.Error; -import it.unimi.dsi.fastutil.objects.ObjectArrayList; - -public class EmptyNumber implements Function { - - public EmptyNumber(final MathContext root) { - this.root = root; - } - - private final MathContext root; - - @Override - public ObjectArrayList simplify(final Rule rule) throws Error, InterruptedException { - return rule.execute(this); - } - - @Override - public MathContext getMathContext() { - return root; - } - - @Override - public boolean equals(final Object o) { - return o instanceof EmptyNumber; - } - - @Override - public Function clone() { - return new EmptyNumber(root); - } - - @Override - public Function clone(MathContext c) { - return new EmptyNumber(c); - } - - @Override - public Function setParameter(final int index, final Function var) throws IndexOutOfBoundsException { - throw new IndexOutOfBoundsException(); - } - - @Override - public Function getParameter(final int index) throws IndexOutOfBoundsException { - throw new IndexOutOfBoundsException(); - } - - @Override - public ObjectArrayList toBlock(final MathContext context) { - // TODO Auto-generated method stub - return null; - } - -} diff --git a/core/src/main/java/it/cavallium/warppi/math/functions/Variable.java b/core/src/main/java/it/cavallium/warppi/math/functions/Variable.java index 119f8631..83c88b2a 100644 --- a/core/src/main/java/it/cavallium/warppi/math/functions/Variable.java +++ b/core/src/main/java/it/cavallium/warppi/math/functions/Variable.java @@ -13,6 +13,7 @@ public class Variable implements Function { protected char var; protected final MathContext root; protected V_TYPE type = V_TYPE.CONSTANT; + private boolean simplified; public Variable(final MathContext root, final char val, final V_TYPE type) { this.root = root; diff --git a/core/src/main/java/it/cavallium/warppi/math/solver/MathSolver.java b/core/src/main/java/it/cavallium/warppi/math/solver/MathSolver.java index 30ca1fb1..8394621e 100644 --- a/core/src/main/java/it/cavallium/warppi/math/solver/MathSolver.java +++ b/core/src/main/java/it/cavallium/warppi/math/solver/MathSolver.java @@ -1,6 +1,7 @@ package it.cavallium.warppi.math.solver; import java.util.List; +import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; import it.cavallium.warppi.Engine; @@ -9,6 +10,9 @@ import it.cavallium.warppi.math.Function; import it.cavallium.warppi.math.rules.Rule; import it.cavallium.warppi.math.rules.RuleType; import it.cavallium.warppi.util.Error; +import it.unimi.dsi.fastutil.objects.Object2ObjectMaps; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenCustomHashMap; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import it.unimi.dsi.fastutil.objects.ObjectArrayList; public class MathSolver { @@ -17,6 +21,7 @@ public class MathSolver { private final AtomicInteger stepState = new AtomicInteger(0); private int stepStateRepetitions = 0; private int consecutiveNullSteps = 0; + private final Object2ObjectOpenHashMap> simplificationCache; private enum StepState { _1_CALCULATION, _2_EXPANSION, _3_CALCULATION, _4_REDUCTION @@ -28,6 +33,7 @@ public class MathSolver { public MathSolver(final Function initialFunction) { this.initialFunction = initialFunction; + this.simplificationCache = new Object2ObjectOpenHashMap>(); } @SuppressWarnings("unchecked") @@ -214,15 +220,18 @@ public class MathSolver { for (final Function fnc : fncs) { boolean didSomething = false; for (final Rule rule : rules) { - final List ruleResults = fnc.simplify(rule); - if (ruleResults != null && !ruleResults.isEmpty()) { - if (results == null) { - results = new ObjectArrayList<>(); + if (isSimplified(fnc, rule) == false) { + final List ruleResults = fnc.simplify(rule); + if (ruleResults != null && !ruleResults.isEmpty()) { + if (results == null) { + results = new ObjectArrayList<>(); + } + results.addAll(ruleResults); + appliedRules.add(rule); + setSimplified(fnc, rule); + didSomething = true; + break; } - results.addAll(ruleResults); - appliedRules.add(rule); - didSomething = true; - break; } } if (!didSomething && fncs.size() > 1) { @@ -248,4 +257,30 @@ public class MathSolver { } return results; } + + private boolean isSimplified(Function fnc, Rule rule) { + if (simplificationCache.containsKey(fnc)) { + List alreadySimplifiedRules = simplificationCache.get(fnc); + if (alreadySimplifiedRules.contains(rule)) { + return true; + } else { + return false; + } + } else { + simplificationCache.put(fnc, new ObjectArrayList()); + } + return false; + } + + private void setSimplified(Function fnc, Rule rule) { + ObjectArrayList oar; + if (simplificationCache.containsKey(fnc)) { + oar = new ObjectArrayList<>(); + simplificationCache.put(fnc, oar); + } else { + oar = simplificationCache.get(fnc); + if (oar.contains(rule)) return; + } + oar.add(rule); + } } From 5e1d1dde52118b1e4f34865d4e3a55233d736942 Mon Sep 17 00:00:00 2001 From: Andrea Cavalli Date: Fri, 12 Oct 2018 23:23:16 +0200 Subject: [PATCH 08/18] New extra features --- .../{BlockType.java => BlockColor.java} | 5 +- .../warppi/extra/tetris/TetrisGame.java | 72 ++++++++-- .../warppi/extra/tetris/TetrisScreen.java | 17 ++- .../warppi/extra/tetris/Tetromino.java | 126 ++++++++++++++++++ .../warppi/extra/tetris/TetrominoICyan.java | 48 +++++++ .../warppi/extra/tetris/TetrominoJBlue.java | 44 ++++++ .../warppi/extra/tetris/TetrominoLOrange.java | 44 ++++++ .../warppi/extra/tetris/TetrominoOYellow.java | 19 +++ .../warppi/extra/tetris/TetrominoSGreen.java | 44 ++++++ .../warppi/extra/tetris/TetrominoTPurple.java | 44 ++++++ .../warppi/extra/tetris/TetrominoType.java | 11 ++ .../warppi/extra/tetris/TetrominoZRed.java | 44 ++++++ core/src/main/resources/tetrisskin.png | Bin 1722 -> 2289 bytes core/src/main/resources/tetrisskin.xcf | Bin 1494 -> 1600 bytes 14 files changed, 499 insertions(+), 19 deletions(-) rename core/src/main/java/it/cavallium/warppi/extra/tetris/{BlockType.java => BlockColor.java} (67%) create mode 100644 core/src/main/java/it/cavallium/warppi/extra/tetris/Tetromino.java create mode 100644 core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoICyan.java create mode 100644 core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoJBlue.java create mode 100644 core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoLOrange.java create mode 100644 core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoOYellow.java create mode 100644 core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoSGreen.java create mode 100644 core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoTPurple.java create mode 100644 core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoType.java create mode 100644 core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoZRed.java diff --git a/core/src/main/java/it/cavallium/warppi/extra/tetris/BlockType.java b/core/src/main/java/it/cavallium/warppi/extra/tetris/BlockColor.java similarity index 67% rename from core/src/main/java/it/cavallium/warppi/extra/tetris/BlockType.java rename to core/src/main/java/it/cavallium/warppi/extra/tetris/BlockColor.java index ce042e1d..903667cc 100644 --- a/core/src/main/java/it/cavallium/warppi/extra/tetris/BlockType.java +++ b/core/src/main/java/it/cavallium/warppi/extra/tetris/BlockColor.java @@ -1,10 +1,11 @@ package it.cavallium.warppi.extra.tetris; -public enum BlockType { +public enum BlockColor { RED, GREEN, BLUE, YELLOW, ORANGE, - VIOLET + PURPLE, + CYAN } diff --git a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrisGame.java b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrisGame.java index c230c1c0..410f718c 100644 --- a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrisGame.java +++ b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrisGame.java @@ -1,14 +1,20 @@ package it.cavallium.warppi.extra.tetris; +import java.util.Arrays; + public class TetrisGame { public static final int WIDTH = 10, HEIGHT = 22; - private BlockType[] grid; - private BlockType[] hovergrid; - private BlockType[] renderedGrid; + public static final double TICK_TIME = 0.25; + private BlockColor[] grid; + private BlockColor[] hovergrid; + private volatile BlockColor[] renderedGrid; private GameStatus gameStatus; private int score; private double currentTime; + private double tickTimer; + private Tetromino currentTetromino; + private Tetromino nextTetromino; public TetrisGame() { resetVariables(); @@ -17,19 +23,29 @@ public class TetrisGame { void playAgain() { resetVariables(); gameStatus = GameStatus.PLAYING; + placeNextTetromino(); } private void resetVariables() { - grid = new BlockType[WIDTH * HEIGHT]; - hovergrid = new BlockType[WIDTH * HEIGHT]; - renderedGrid = new BlockType[WIDTH * HEIGHT]; + grid = new BlockColor[WIDTH * HEIGHT]; + hovergrid = new BlockColor[WIDTH * HEIGHT]; + renderedGrid = new BlockColor[WIDTH * HEIGHT]; score = 0; currentTime = 0; + tickTimer = 0; gameStatus = GameStatus.INITIAL; + currentTetromino = null; + nextTetromino = generateRandomTetromino(); + nextTetromino.fixInitialPosition(); } - public void gameTick(float dt, boolean leftPressed, boolean rightPressed, boolean downPressed, boolean okPressed, boolean backPressed) { + public void update(float dt, boolean leftPressed, boolean rightPressed, boolean downPressed, boolean okPressed, boolean backPressed) { currentTime += dt; + tickTimer += dt; + while (tickTimer >= TICK_TIME) { + tickTimer -= TICK_TIME; + gameTick(leftPressed, rightPressed, downPressed, okPressed, backPressed); + } if (gameStatus == GameStatus.INITIAL) { playAgain(); } else { @@ -37,18 +53,54 @@ public class TetrisGame { } renderGrid(); } + + public void gameTick(boolean leftPressed, boolean rightPressed, boolean downPressed, boolean okPressed, boolean backPressed) { + this.currentTetromino.setY((byte) (this.currentTetromino.getY() - 1)); + } public void renderGrid() { - this.renderedGrid = new BlockType[WIDTH*HEIGHT]; + this.renderedGrid = Arrays.copyOf(grid, grid.length); + drawCurrentTetromino(this.renderedGrid); for (int y = 0; y < HEIGHT; y++) { for (int x = 0; x < WIDTH; x++) { final int offset = x+y*WIDTH; - renderedGrid[offset] = hovergrid[offset] != null ? hovergrid[offset] : grid[offset]; + renderedGrid[offset] = hovergrid[offset] != null ? hovergrid[offset] : renderedGrid[offset]; } } } - public BlockType[] getRenderedGrid() { + private void placeNextTetromino() { + currentTetromino = nextTetromino; + nextTetromino = generateRandomTetromino(); + nextTetromino.fixInitialPosition(); + } + + private Tetromino generateRandomTetromino() { + int s = (int) (Math.random() * 7); + final byte middleX = (byte)((WIDTH - 1)/2), middleY = (byte)(HEIGHT - 1), rotation = (byte) (Math.random() * 4); + switch (s) { + case 0: + return new TetrominoICyan(middleX, middleY, rotation); + case 1: + return new TetrominoJBlue(middleX, middleY, rotation); + case 2: + return new TetrominoLOrange(middleX, middleY, rotation); + case 3: + return new TetrominoOYellow(middleX, middleY, rotation); + case 4: + return new TetrominoSGreen(middleX, middleY, rotation); + case 5: + return new TetrominoTPurple(middleX, middleY, rotation); + default: + return new TetrominoZRed(middleX, middleY, rotation); + } + } + + private void drawCurrentTetromino(BlockColor[] grid) { + currentTetromino.draw(grid, WIDTH); + } + + public BlockColor[] getRenderedGrid() { return renderedGrid; } } diff --git a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrisScreen.java b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrisScreen.java index 04606048..2604a6cd 100644 --- a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrisScreen.java +++ b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrisScreen.java @@ -63,7 +63,7 @@ public class TetrisScreen extends Screen { @Override public void beforeRender(final float dt) { Engine.INSTANCE.getHardwareDevice().getDisplayManager().renderer.glClearColor(0xff000000); - g.gameTick(dt, leftPressed, rightPressed, downPressed, okPressed, backPressed); + g.update(dt, leftPressed, rightPressed, downPressed, okPressed, backPressed); } @Override @@ -72,17 +72,20 @@ public class TetrisScreen extends Screen { TetrisScreen.skin.use(e); } r.glColor3f(1, 1, 1); - BlockType[] renderedGrid = g.getRenderedGrid(); + BlockColor[] renderedGrid = g.getRenderedGrid(); int centerScreen = StaticVars.screenSize[0]/2; - int centerGrid = TetrisGame.WIDTH*5/2-1; + int centerGrid = TetrisGame.WIDTH*6/2-1; final int leftOffset = centerScreen - centerGrid; - final int topOffset = StaticVars.screenSize[1] - TetrisGame.HEIGHT*5-1; + final int topOffset = StaticVars.screenSize[1] - TetrisGame.HEIGHT*6-1; for (int y = 0; y < TetrisGame.HEIGHT; y++) { for (int x = 0; x < TetrisGame.WIDTH; x++) { final int offset = x+y*TetrisGame.WIDTH; - final BlockType type = renderedGrid[offset]; - if (type != null) r.glFillRect(leftOffset + x * 4, y * 4, 4, 4, renderedGrid[offset].ordinal() * 4, 0, 4, 4); - else r.glFillRect(leftOffset + x * 5, topOffset + y * 5, 4, 4, 2 * 4, 0, 4, 4); + final BlockColor type = renderedGrid[offset]; + if (type != null) { + r.glFillRect(leftOffset + x * 5, topOffset + (TetrisGame.HEIGHT+3-y) * 5, 5, 5, renderedGrid[offset].ordinal() * 5, 0, 5, 5); + } else { +// r.glFillRect(leftOffset + x * 5, topOffset + y * 5, 5, 5, 1 * 5, 0, 5, 5); + } } } } diff --git a/core/src/main/java/it/cavallium/warppi/extra/tetris/Tetromino.java b/core/src/main/java/it/cavallium/warppi/extra/tetris/Tetromino.java new file mode 100644 index 00000000..3d54e370 --- /dev/null +++ b/core/src/main/java/it/cavallium/warppi/extra/tetris/Tetromino.java @@ -0,0 +1,126 @@ +package it.cavallium.warppi.extra.tetris; + +public abstract class Tetromino { + private byte x, y, rotation; + private final TetrominoType type; + public final boolean o = false, w = true; + + public Tetromino(byte x, byte y, byte rotation, TetrominoType type) { + super(); + this.x = x; + this.y = y; + this.rotation = rotation; + this.type = type; + } + + public byte getX() { + return x; + } + + public void setX(byte x) { + this.x = x; + } + + public byte getY() { + return y; + } + + public void setY(byte y) { + this.y = y; + } + + public byte getRotation() { + return rotation; + } + + public void setRotation(byte rotation) { + this.rotation = rotation; + } + + public TetrominoType getType() { + return type; + } + + public BlockColor getColor() { + switch(type) { + case I_CYAN: + return BlockColor.CYAN; + case J_BLUE: + return BlockColor.BLUE; + case L_ORANGE: + return BlockColor.ORANGE; + case O_YELLOW: + return BlockColor.YELLOW; + case S_GREEN: + return BlockColor.GREEN; + case T_PURPLE: + return BlockColor.PURPLE; + case Z_RED: + return BlockColor.RED; + default: + return BlockColor.RED; + } + } + + public void draw(BlockColor[] grid, final int WIDTH) { + boolean[] blockGrid = getRenderedBlock(); + final int tetrominoGridSize = getTetrominoGridSize(); + final int centerOffset = (int) Math.floor((double)tetrominoGridSize/2d); + final BlockColor type = getColor(); + for (int bx = 0; bx < tetrominoGridSize; bx++) { + for (int by = 0; by < tetrominoGridSize; by++) { + if (blockGrid[bx+by*tetrominoGridSize] == w) { + final int index = x+bx-centerOffset + (y+by-centerOffset) * WIDTH; + if (index < grid.length && index >= 0) { + grid[index] = type; + } + } + } + } + } + + public void fixInitialPosition() { + this.y -= (byte) (this.getTetrominoGridSize()/2); + } + + public abstract int getTetrominoGridSize(); + protected abstract boolean[] getRenderedBlock(); + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + rotation; + result = prime * result + ((type == null) ? 0 : type.hashCode()); + result = prime * result + x; + result = prime * result + y; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Tetromino other = (Tetromino) obj; + if (rotation != other.rotation) + return false; + if (type != other.type) + return false; + if (x != other.x) + return false; + if (y != other.y) + return false; + return true; + } + + @Override + public String toString() { + return "Tetromino = {\n\t\"x\": \"" + x + "\",\n\ty\": \"" + y + "\",\n\trotation\": \"" + rotation + "\",\n\ttype\": \"" + type + "\"\n}"; + } + + +} diff --git a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoICyan.java b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoICyan.java new file mode 100644 index 00000000..a6eb84a4 --- /dev/null +++ b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoICyan.java @@ -0,0 +1,48 @@ +package it.cavallium.warppi.extra.tetris; + +public class TetrominoICyan extends Tetromino { + public TetrominoICyan(byte x, byte y, byte rotation) { + super(x, y, rotation, TetrominoType.I_CYAN); + } + + @Override + public int getTetrominoGridSize() { + return 4; + } + + @Override + public boolean[] getRenderedBlock() { + switch(getRotation()) { + case 0: + return new boolean[] { + o,o,o,o, + w,w,w,w, + o,o,o,o, + o,o,o,o + }; + case 1: + return new boolean[] { + o,o,w,o, + o,o,w,o, + o,o,w,o, + o,o,w,o + }; + case 2: + return new boolean[] { + o,o,o,o, + o,o,o,o, + w,w,w,w, + o,o,o,o + }; + case 3: + return new boolean[] { + o,w,o,o, + o,w,o,o, + o,w,o,o, + o,w,o,o + }; + default: + throw new NullPointerException(); + } + } +} diff --git a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoJBlue.java b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoJBlue.java new file mode 100644 index 00000000..e1835e22 --- /dev/null +++ b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoJBlue.java @@ -0,0 +1,44 @@ +package it.cavallium.warppi.extra.tetris; + +public class TetrominoJBlue extends Tetromino { + public TetrominoJBlue(byte x, byte y, byte rotation) { + super(x, y, rotation, TetrominoType.J_BLUE); + } + + @Override + public int getTetrominoGridSize() { + return 3; + } + + @Override + public boolean[] getRenderedBlock() { + switch(getRotation()) { + case 0: + return new boolean[] { + w,o,o, + w,w,w, + o,o,o + }; + case 1: + return new boolean[] { + o,w,w, + o,w,o, + o,w,o + }; + case 2: + return new boolean[] { + o,o,o, + w,w,w, + o,o,w + }; + case 3: + return new boolean[] { + o,w,o, + o,w,o, + w,w,o + }; + default: + throw new NullPointerException(); + } + } +} diff --git a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoLOrange.java b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoLOrange.java new file mode 100644 index 00000000..ec7ad80d --- /dev/null +++ b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoLOrange.java @@ -0,0 +1,44 @@ +package it.cavallium.warppi.extra.tetris; + +public class TetrominoLOrange extends Tetromino { + public TetrominoLOrange(byte x, byte y, byte rotation) { + super(x, y, rotation, TetrominoType.L_ORANGE); + } + + @Override + public int getTetrominoGridSize() { + return 3; + } + + @Override + public boolean[] getRenderedBlock() { + switch(getRotation()) { + case 0: + return new boolean[] { + o,o,w, + w,w,w, + o,o,o + }; + case 1: + return new boolean[] { + o,w,o, + o,w,o, + o,w,w + }; + case 2: + return new boolean[] { + o,o,o, + w,w,w, + w,o,o + }; + case 3: + return new boolean[] { + w,w,o, + o,w,o, + o,w,o + }; + default: + throw new NullPointerException(); + } + } +} diff --git a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoOYellow.java b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoOYellow.java new file mode 100644 index 00000000..124c6c16 --- /dev/null +++ b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoOYellow.java @@ -0,0 +1,19 @@ +package it.cavallium.warppi.extra.tetris; + +public class TetrominoOYellow extends Tetromino { + public TetrominoOYellow(byte x, byte y, byte rotation) { + super(x, y, rotation, TetrominoType.O_YELLOW); + } + @Override + public int getTetrominoGridSize() { + return 2; + } + + @Override + public boolean[] getRenderedBlock() { + return new boolean[] { + w,w, + w,w, + }; + } +} diff --git a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoSGreen.java b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoSGreen.java new file mode 100644 index 00000000..1bfb5354 --- /dev/null +++ b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoSGreen.java @@ -0,0 +1,44 @@ +package it.cavallium.warppi.extra.tetris; + +public class TetrominoSGreen extends Tetromino { + public TetrominoSGreen(byte x, byte y, byte rotation) { + super(x, y, rotation, TetrominoType.S_GREEN); + } + + @Override + public int getTetrominoGridSize() { + return 3; + } + + @Override + public boolean[] getRenderedBlock() { + switch(getRotation()) { + case 0: + return new boolean[] { + o,w,w, + w,w,o, + o,o,o + }; + case 1: + return new boolean[] { + o,w,o, + o,w,w, + o,o,w + }; + case 2: + return new boolean[] { + o,o,o, + o,w,w, + w,w,o + }; + case 3: + return new boolean[] { + w,o,o, + w,w,o, + o,w,o + }; + default: + throw new NullPointerException(); + } + } +} diff --git a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoTPurple.java b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoTPurple.java new file mode 100644 index 00000000..9d56e4db --- /dev/null +++ b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoTPurple.java @@ -0,0 +1,44 @@ +package it.cavallium.warppi.extra.tetris; + +public class TetrominoTPurple extends Tetromino { + public TetrominoTPurple(byte x, byte y, byte rotation) { + super(x, y, rotation, TetrominoType.I_CYAN); + } + + @Override + public int getTetrominoGridSize() { + return 3; + } + + @Override + public boolean[] getRenderedBlock() { + switch(getRotation()) { + case 0: + return new boolean[] { + o,w,o, + w,w,w, + o,o,o + }; + case 1: + return new boolean[] { + o,w,o, + o,w,w, + o,w,o + }; + case 2: + return new boolean[] { + o,o,o, + w,w,w, + o,w,o + }; + case 3: + return new boolean[] { + o,w,o, + w,w,o, + o,w,o + }; + default: + throw new NullPointerException(); + } + } +} diff --git a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoType.java b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoType.java new file mode 100644 index 00000000..c6091e39 --- /dev/null +++ b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoType.java @@ -0,0 +1,11 @@ +package it.cavallium.warppi.extra.tetris; + +public enum TetrominoType { + I_CYAN, + O_YELLOW, + T_PURPLE, + S_GREEN, + Z_RED, + J_BLUE, + L_ORANGE +} diff --git a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoZRed.java b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoZRed.java new file mode 100644 index 00000000..22daa3f9 --- /dev/null +++ b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoZRed.java @@ -0,0 +1,44 @@ +package it.cavallium.warppi.extra.tetris; + +public class TetrominoZRed extends Tetromino { + public TetrominoZRed(byte x, byte y, byte rotation) { + super(x, y, rotation, TetrominoType.Z_RED); + } + + @Override + public int getTetrominoGridSize() { + return 3; + } + + @Override + public boolean[] getRenderedBlock() { + switch(getRotation()) { + case 0: + return new boolean[] { + w,w,o, + o,w,w, + o,o,o + }; + case 1: + return new boolean[] { + o,o,w, + o,w,w, + o,w,o + }; + case 2: + return new boolean[] { + o,o,o, + w,w,o, + o,w,w + }; + case 3: + return new boolean[] { + o,w,o, + w,w,o, + w,o,o + }; + default: + throw new NullPointerException(); + } + } +} diff --git a/core/src/main/resources/tetrisskin.png b/core/src/main/resources/tetrisskin.png index 7cc22a6636fc1910f38a1767f435415fb3ef1d9c..f73890af931f6f15c31fc9bf4f50622dac8c6acc 100644 GIT binary patch delta 2232 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 delta 261 zcmX@WbB%j~6Qle_XG0br=Kt)>-2c@XUi@FW^glBL12YJN*#A$T{{MgRVxZKY!Z~va z*%`iosEx;uZ!DQTyF{IV87RbD$h?sm#0DC|{9k|aO%@#wW*`fU8?_nUzzl=YjhUH^ z`Va$|8Ge8aj8037)(05`!qM75`ytx(CpWO_ID?D@iU2K8XZV2ZItcp@$bnGv*ctwS d90oEA=rEw8AbRyDbFwK4i39Z`a6P{U0{|T|S_uFE From 971759a6e7452309c6016d120203622d1eabfe4b Mon Sep 17 00:00:00 2001 From: Andrea Cavalli Date: Sun, 14 Oct 2018 22:32:08 +0200 Subject: [PATCH 09/18] Updated pom.xml --- Flow | 2 +- core/pom.xml | 15 +- .../warppi/extra/tetris/BlockColor.java | 22 +- .../warppi/extra/tetris/Tetromino.java | 252 +++++++++--------- .../warppi/extra/tetris/TetrominoICyan.java | 96 +++---- .../warppi/extra/tetris/TetrominoJBlue.java | 88 +++--- .../warppi/extra/tetris/TetrominoLOrange.java | 88 +++--- .../warppi/extra/tetris/TetrominoOYellow.java | 38 +-- .../warppi/extra/tetris/TetrominoSGreen.java | 88 +++--- .../warppi/extra/tetris/TetrominoTPurple.java | 88 +++--- .../warppi/extra/tetris/TetrominoType.java | 22 +- .../warppi/extra/tetris/TetrominoZRed.java | 88 +++--- desktop/pom.xml | 2 +- engine-jogl/pom.xml | 116 ++++---- hardware/pom.xml | 2 +- pom.xml | 3 +- rules/pom.xml | 2 +- teavm/pom.xml | 2 +- 18 files changed, 502 insertions(+), 512 deletions(-) diff --git a/Flow b/Flow index 4ec16b0e..9acbcc2e 160000 --- a/Flow +++ b/Flow @@ -1 +1 @@ -Subproject commit 4ec16b0e2d49da9771503e713604f5641ef6c414 +Subproject commit 9acbcc2e0af4961688490eb3447d990d157a46a8 diff --git a/core/pom.xml b/core/pom.xml index d163a214..3d996471 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -6,10 +6,9 @@ it.cavallium warppi - ${project.version} + 0.9.0a2 warppi-core - bundle WarpPI Calculator Core WarpPI Calculator core project @@ -46,16 +45,8 @@ - org.apache.felix - maven-bundle-plugin - true - - - * - it.cavallium.warppi.* - warppi-core - - + org.apache.maven.plugins + maven-compiler-plugin org.apache.maven.plugins diff --git a/core/src/main/java/it/cavallium/warppi/extra/tetris/BlockColor.java b/core/src/main/java/it/cavallium/warppi/extra/tetris/BlockColor.java index 903667cc..cc784d60 100644 --- a/core/src/main/java/it/cavallium/warppi/extra/tetris/BlockColor.java +++ b/core/src/main/java/it/cavallium/warppi/extra/tetris/BlockColor.java @@ -1,11 +1,11 @@ -package it.cavallium.warppi.extra.tetris; - -public enum BlockColor { - RED, - GREEN, - BLUE, - YELLOW, - ORANGE, - PURPLE, - CYAN -} +package it.cavallium.warppi.extra.tetris; + +public enum BlockColor { + RED, + GREEN, + BLUE, + YELLOW, + ORANGE, + PURPLE, + CYAN +} diff --git a/core/src/main/java/it/cavallium/warppi/extra/tetris/Tetromino.java b/core/src/main/java/it/cavallium/warppi/extra/tetris/Tetromino.java index 3d54e370..0ca620c4 100644 --- a/core/src/main/java/it/cavallium/warppi/extra/tetris/Tetromino.java +++ b/core/src/main/java/it/cavallium/warppi/extra/tetris/Tetromino.java @@ -1,126 +1,126 @@ -package it.cavallium.warppi.extra.tetris; - -public abstract class Tetromino { - private byte x, y, rotation; - private final TetrominoType type; - public final boolean o = false, w = true; - - public Tetromino(byte x, byte y, byte rotation, TetrominoType type) { - super(); - this.x = x; - this.y = y; - this.rotation = rotation; - this.type = type; - } - - public byte getX() { - return x; - } - - public void setX(byte x) { - this.x = x; - } - - public byte getY() { - return y; - } - - public void setY(byte y) { - this.y = y; - } - - public byte getRotation() { - return rotation; - } - - public void setRotation(byte rotation) { - this.rotation = rotation; - } - - public TetrominoType getType() { - return type; - } - - public BlockColor getColor() { - switch(type) { - case I_CYAN: - return BlockColor.CYAN; - case J_BLUE: - return BlockColor.BLUE; - case L_ORANGE: - return BlockColor.ORANGE; - case O_YELLOW: - return BlockColor.YELLOW; - case S_GREEN: - return BlockColor.GREEN; - case T_PURPLE: - return BlockColor.PURPLE; - case Z_RED: - return BlockColor.RED; - default: - return BlockColor.RED; - } - } - - public void draw(BlockColor[] grid, final int WIDTH) { - boolean[] blockGrid = getRenderedBlock(); - final int tetrominoGridSize = getTetrominoGridSize(); - final int centerOffset = (int) Math.floor((double)tetrominoGridSize/2d); - final BlockColor type = getColor(); - for (int bx = 0; bx < tetrominoGridSize; bx++) { - for (int by = 0; by < tetrominoGridSize; by++) { - if (blockGrid[bx+by*tetrominoGridSize] == w) { - final int index = x+bx-centerOffset + (y+by-centerOffset) * WIDTH; - if (index < grid.length && index >= 0) { - grid[index] = type; - } - } - } - } - } - - public void fixInitialPosition() { - this.y -= (byte) (this.getTetrominoGridSize()/2); - } - - public abstract int getTetrominoGridSize(); - protected abstract boolean[] getRenderedBlock(); - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + rotation; - result = prime * result + ((type == null) ? 0 : type.hashCode()); - result = prime * result + x; - result = prime * result + y; - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - Tetromino other = (Tetromino) obj; - if (rotation != other.rotation) - return false; - if (type != other.type) - return false; - if (x != other.x) - return false; - if (y != other.y) - return false; - return true; - } - - @Override - public String toString() { - return "Tetromino = {\n\t\"x\": \"" + x + "\",\n\ty\": \"" + y + "\",\n\trotation\": \"" + rotation + "\",\n\ttype\": \"" + type + "\"\n}"; - } - - -} +package it.cavallium.warppi.extra.tetris; + +public abstract class Tetromino { + private byte x, y, rotation; + private final TetrominoType type; + public final boolean o = false, w = true; + + public Tetromino(byte x, byte y, byte rotation, TetrominoType type) { + super(); + this.x = x; + this.y = y; + this.rotation = rotation; + this.type = type; + } + + public byte getX() { + return x; + } + + public void setX(byte x) { + this.x = x; + } + + public byte getY() { + return y; + } + + public void setY(byte y) { + this.y = y; + } + + public byte getRotation() { + return rotation; + } + + public void setRotation(byte rotation) { + this.rotation = rotation; + } + + public TetrominoType getType() { + return type; + } + + public BlockColor getColor() { + switch(type) { + case I_CYAN: + return BlockColor.CYAN; + case J_BLUE: + return BlockColor.BLUE; + case L_ORANGE: + return BlockColor.ORANGE; + case O_YELLOW: + return BlockColor.YELLOW; + case S_GREEN: + return BlockColor.GREEN; + case T_PURPLE: + return BlockColor.PURPLE; + case Z_RED: + return BlockColor.RED; + default: + return BlockColor.RED; + } + } + + public void draw(BlockColor[] grid, final int WIDTH) { + boolean[] blockGrid = getRenderedBlock(); + final int tetrominoGridSize = getTetrominoGridSize(); + final int centerOffset = (int) Math.floor((double)tetrominoGridSize/2d); + final BlockColor type = getColor(); + for (int bx = 0; bx < tetrominoGridSize; bx++) { + for (int by = 0; by < tetrominoGridSize; by++) { + if (blockGrid[bx+by*tetrominoGridSize] == w) { + final int index = x+bx-centerOffset + (y+by-centerOffset) * WIDTH; + if (index < grid.length && index >= 0) { + grid[index] = type; + } + } + } + } + } + + public void fixInitialPosition() { + this.y -= (byte) (this.getTetrominoGridSize()/2); + } + + public abstract int getTetrominoGridSize(); + protected abstract boolean[] getRenderedBlock(); + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + rotation; + result = prime * result + ((type == null) ? 0 : type.hashCode()); + result = prime * result + x; + result = prime * result + y; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Tetromino other = (Tetromino) obj; + if (rotation != other.rotation) + return false; + if (type != other.type) + return false; + if (x != other.x) + return false; + if (y != other.y) + return false; + return true; + } + + @Override + public String toString() { + return "Tetromino = {\n\t\"x\": \"" + x + "\",\n\ty\": \"" + y + "\",\n\trotation\": \"" + rotation + "\",\n\ttype\": \"" + type + "\"\n}"; + } + + +} diff --git a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoICyan.java b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoICyan.java index a6eb84a4..4e908b6f 100644 --- a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoICyan.java +++ b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoICyan.java @@ -1,48 +1,48 @@ -package it.cavallium.warppi.extra.tetris; - -public class TetrominoICyan extends Tetromino { - public TetrominoICyan(byte x, byte y, byte rotation) { - super(x, y, rotation, TetrominoType.I_CYAN); - } - - @Override - public int getTetrominoGridSize() { - return 4; - } - - @Override - public boolean[] getRenderedBlock() { - switch(getRotation()) { - case 0: - return new boolean[] { - o,o,o,o, - w,w,w,w, - o,o,o,o, - o,o,o,o - }; - case 1: - return new boolean[] { - o,o,w,o, - o,o,w,o, - o,o,w,o, - o,o,w,o - }; - case 2: - return new boolean[] { - o,o,o,o, - o,o,o,o, - w,w,w,w, - o,o,o,o - }; - case 3: - return new boolean[] { - o,w,o,o, - o,w,o,o, - o,w,o,o, - o,w,o,o - }; - default: - throw new NullPointerException(); - } - } -} +package it.cavallium.warppi.extra.tetris; + +public class TetrominoICyan extends Tetromino { + public TetrominoICyan(byte x, byte y, byte rotation) { + super(x, y, rotation, TetrominoType.I_CYAN); + } + + @Override + public int getTetrominoGridSize() { + return 4; + } + + @Override + public boolean[] getRenderedBlock() { + switch(getRotation()) { + case 0: + return new boolean[] { + o,o,o,o, + w,w,w,w, + o,o,o,o, + o,o,o,o + }; + case 1: + return new boolean[] { + o,o,w,o, + o,o,w,o, + o,o,w,o, + o,o,w,o + }; + case 2: + return new boolean[] { + o,o,o,o, + o,o,o,o, + w,w,w,w, + o,o,o,o + }; + case 3: + return new boolean[] { + o,w,o,o, + o,w,o,o, + o,w,o,o, + o,w,o,o + }; + default: + throw new NullPointerException(); + } + } +} diff --git a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoJBlue.java b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoJBlue.java index e1835e22..be8049e3 100644 --- a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoJBlue.java +++ b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoJBlue.java @@ -1,44 +1,44 @@ -package it.cavallium.warppi.extra.tetris; - -public class TetrominoJBlue extends Tetromino { - public TetrominoJBlue(byte x, byte y, byte rotation) { - super(x, y, rotation, TetrominoType.J_BLUE); - } - - @Override - public int getTetrominoGridSize() { - return 3; - } - - @Override - public boolean[] getRenderedBlock() { - switch(getRotation()) { - case 0: - return new boolean[] { - w,o,o, - w,w,w, - o,o,o - }; - case 1: - return new boolean[] { - o,w,w, - o,w,o, - o,w,o - }; - case 2: - return new boolean[] { - o,o,o, - w,w,w, - o,o,w - }; - case 3: - return new boolean[] { - o,w,o, - o,w,o, - w,w,o - }; - default: - throw new NullPointerException(); - } - } -} +package it.cavallium.warppi.extra.tetris; + +public class TetrominoJBlue extends Tetromino { + public TetrominoJBlue(byte x, byte y, byte rotation) { + super(x, y, rotation, TetrominoType.J_BLUE); + } + + @Override + public int getTetrominoGridSize() { + return 3; + } + + @Override + public boolean[] getRenderedBlock() { + switch(getRotation()) { + case 0: + return new boolean[] { + w,o,o, + w,w,w, + o,o,o + }; + case 1: + return new boolean[] { + o,w,w, + o,w,o, + o,w,o + }; + case 2: + return new boolean[] { + o,o,o, + w,w,w, + o,o,w + }; + case 3: + return new boolean[] { + o,w,o, + o,w,o, + w,w,o + }; + default: + throw new NullPointerException(); + } + } +} diff --git a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoLOrange.java b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoLOrange.java index ec7ad80d..2d8911f0 100644 --- a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoLOrange.java +++ b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoLOrange.java @@ -1,44 +1,44 @@ -package it.cavallium.warppi.extra.tetris; - -public class TetrominoLOrange extends Tetromino { - public TetrominoLOrange(byte x, byte y, byte rotation) { - super(x, y, rotation, TetrominoType.L_ORANGE); - } - - @Override - public int getTetrominoGridSize() { - return 3; - } - - @Override - public boolean[] getRenderedBlock() { - switch(getRotation()) { - case 0: - return new boolean[] { - o,o,w, - w,w,w, - o,o,o - }; - case 1: - return new boolean[] { - o,w,o, - o,w,o, - o,w,w - }; - case 2: - return new boolean[] { - o,o,o, - w,w,w, - w,o,o - }; - case 3: - return new boolean[] { - w,w,o, - o,w,o, - o,w,o - }; - default: - throw new NullPointerException(); - } - } -} +package it.cavallium.warppi.extra.tetris; + +public class TetrominoLOrange extends Tetromino { + public TetrominoLOrange(byte x, byte y, byte rotation) { + super(x, y, rotation, TetrominoType.L_ORANGE); + } + + @Override + public int getTetrominoGridSize() { + return 3; + } + + @Override + public boolean[] getRenderedBlock() { + switch(getRotation()) { + case 0: + return new boolean[] { + o,o,w, + w,w,w, + o,o,o + }; + case 1: + return new boolean[] { + o,w,o, + o,w,o, + o,w,w + }; + case 2: + return new boolean[] { + o,o,o, + w,w,w, + w,o,o + }; + case 3: + return new boolean[] { + w,w,o, + o,w,o, + o,w,o + }; + default: + throw new NullPointerException(); + } + } +} diff --git a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoOYellow.java b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoOYellow.java index 124c6c16..aa2d0602 100644 --- a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoOYellow.java +++ b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoOYellow.java @@ -1,19 +1,19 @@ -package it.cavallium.warppi.extra.tetris; - -public class TetrominoOYellow extends Tetromino { - public TetrominoOYellow(byte x, byte y, byte rotation) { - super(x, y, rotation, TetrominoType.O_YELLOW); - } - @Override - public int getTetrominoGridSize() { - return 2; - } - - @Override - public boolean[] getRenderedBlock() { - return new boolean[] { - w,w, - w,w, - }; - } -} +package it.cavallium.warppi.extra.tetris; + +public class TetrominoOYellow extends Tetromino { + public TetrominoOYellow(byte x, byte y, byte rotation) { + super(x, y, rotation, TetrominoType.O_YELLOW); + } + @Override + public int getTetrominoGridSize() { + return 2; + } + + @Override + public boolean[] getRenderedBlock() { + return new boolean[] { + w,w, + w,w, + }; + } +} diff --git a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoSGreen.java b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoSGreen.java index 1bfb5354..10e59ba4 100644 --- a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoSGreen.java +++ b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoSGreen.java @@ -1,44 +1,44 @@ -package it.cavallium.warppi.extra.tetris; - -public class TetrominoSGreen extends Tetromino { - public TetrominoSGreen(byte x, byte y, byte rotation) { - super(x, y, rotation, TetrominoType.S_GREEN); - } - - @Override - public int getTetrominoGridSize() { - return 3; - } - - @Override - public boolean[] getRenderedBlock() { - switch(getRotation()) { - case 0: - return new boolean[] { - o,w,w, - w,w,o, - o,o,o - }; - case 1: - return new boolean[] { - o,w,o, - o,w,w, - o,o,w - }; - case 2: - return new boolean[] { - o,o,o, - o,w,w, - w,w,o - }; - case 3: - return new boolean[] { - w,o,o, - w,w,o, - o,w,o - }; - default: - throw new NullPointerException(); - } - } -} +package it.cavallium.warppi.extra.tetris; + +public class TetrominoSGreen extends Tetromino { + public TetrominoSGreen(byte x, byte y, byte rotation) { + super(x, y, rotation, TetrominoType.S_GREEN); + } + + @Override + public int getTetrominoGridSize() { + return 3; + } + + @Override + public boolean[] getRenderedBlock() { + switch(getRotation()) { + case 0: + return new boolean[] { + o,w,w, + w,w,o, + o,o,o + }; + case 1: + return new boolean[] { + o,w,o, + o,w,w, + o,o,w + }; + case 2: + return new boolean[] { + o,o,o, + o,w,w, + w,w,o + }; + case 3: + return new boolean[] { + w,o,o, + w,w,o, + o,w,o + }; + default: + throw new NullPointerException(); + } + } +} diff --git a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoTPurple.java b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoTPurple.java index 9d56e4db..bd30e433 100644 --- a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoTPurple.java +++ b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoTPurple.java @@ -1,44 +1,44 @@ -package it.cavallium.warppi.extra.tetris; - -public class TetrominoTPurple extends Tetromino { - public TetrominoTPurple(byte x, byte y, byte rotation) { - super(x, y, rotation, TetrominoType.I_CYAN); - } - - @Override - public int getTetrominoGridSize() { - return 3; - } - - @Override - public boolean[] getRenderedBlock() { - switch(getRotation()) { - case 0: - return new boolean[] { - o,w,o, - w,w,w, - o,o,o - }; - case 1: - return new boolean[] { - o,w,o, - o,w,w, - o,w,o - }; - case 2: - return new boolean[] { - o,o,o, - w,w,w, - o,w,o - }; - case 3: - return new boolean[] { - o,w,o, - w,w,o, - o,w,o - }; - default: - throw new NullPointerException(); - } - } -} +package it.cavallium.warppi.extra.tetris; + +public class TetrominoTPurple extends Tetromino { + public TetrominoTPurple(byte x, byte y, byte rotation) { + super(x, y, rotation, TetrominoType.I_CYAN); + } + + @Override + public int getTetrominoGridSize() { + return 3; + } + + @Override + public boolean[] getRenderedBlock() { + switch(getRotation()) { + case 0: + return new boolean[] { + o,w,o, + w,w,w, + o,o,o + }; + case 1: + return new boolean[] { + o,w,o, + o,w,w, + o,w,o + }; + case 2: + return new boolean[] { + o,o,o, + w,w,w, + o,w,o + }; + case 3: + return new boolean[] { + o,w,o, + w,w,o, + o,w,o + }; + default: + throw new NullPointerException(); + } + } +} diff --git a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoType.java b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoType.java index c6091e39..794fa4b9 100644 --- a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoType.java +++ b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoType.java @@ -1,11 +1,11 @@ -package it.cavallium.warppi.extra.tetris; - -public enum TetrominoType { - I_CYAN, - O_YELLOW, - T_PURPLE, - S_GREEN, - Z_RED, - J_BLUE, - L_ORANGE -} +package it.cavallium.warppi.extra.tetris; + +public enum TetrominoType { + I_CYAN, + O_YELLOW, + T_PURPLE, + S_GREEN, + Z_RED, + J_BLUE, + L_ORANGE +} diff --git a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoZRed.java b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoZRed.java index 22daa3f9..0b7a70d5 100644 --- a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoZRed.java +++ b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoZRed.java @@ -1,44 +1,44 @@ -package it.cavallium.warppi.extra.tetris; - -public class TetrominoZRed extends Tetromino { - public TetrominoZRed(byte x, byte y, byte rotation) { - super(x, y, rotation, TetrominoType.Z_RED); - } - - @Override - public int getTetrominoGridSize() { - return 3; - } - - @Override - public boolean[] getRenderedBlock() { - switch(getRotation()) { - case 0: - return new boolean[] { - w,w,o, - o,w,w, - o,o,o - }; - case 1: - return new boolean[] { - o,o,w, - o,w,w, - o,w,o - }; - case 2: - return new boolean[] { - o,o,o, - w,w,o, - o,w,w - }; - case 3: - return new boolean[] { - o,w,o, - w,w,o, - w,o,o - }; - default: - throw new NullPointerException(); - } - } -} +package it.cavallium.warppi.extra.tetris; + +public class TetrominoZRed extends Tetromino { + public TetrominoZRed(byte x, byte y, byte rotation) { + super(x, y, rotation, TetrominoType.Z_RED); + } + + @Override + public int getTetrominoGridSize() { + return 3; + } + + @Override + public boolean[] getRenderedBlock() { + switch(getRotation()) { + case 0: + return new boolean[] { + w,w,o, + o,w,w, + o,o,o + }; + case 1: + return new boolean[] { + o,o,w, + o,w,w, + o,w,o + }; + case 2: + return new boolean[] { + o,o,o, + w,w,o, + o,w,w + }; + case 3: + return new boolean[] { + o,w,o, + w,w,o, + w,o,o + }; + default: + throw new NullPointerException(); + } + } +} diff --git a/desktop/pom.xml b/desktop/pom.xml index b3c78995..58f9edd3 100644 --- a/desktop/pom.xml +++ b/desktop/pom.xml @@ -6,7 +6,7 @@ it.cavallium warppi - ${project.version} + 0.9.0a2 warppi-desktop diff --git a/engine-jogl/pom.xml b/engine-jogl/pom.xml index 76422426..c828b7a5 100644 --- a/engine-jogl/pom.xml +++ b/engine-jogl/pom.xml @@ -1,58 +1,58 @@ - - 4.0.0 - - - it.cavallium - warppi - ${project.version} - - warppi-engine-jogl - - bundle - - WarpPI Calculator JOGL Engine - WarpPI Calculator engine-jogl project - - - it.cavallium - warppi-core - ${project.version} - - - org.jogamp.jogl - jogl-all-main - 2.3.2 - - - org.jogamp.gluegen - gluegen-rt-main - 2.3.2 - - - ar.com.hjg - pngj - - - - - - org.apache.felix - maven-bundle-plugin - true - - - * - it.cavallium.warppi.* - warppi-engine-jogl - - - - - org.apache.maven.plugins - maven-source-plugin - - - - + + 4.0.0 + + + it.cavallium + warppi + 0.9.0a2 + + warppi-engine-jogl + + bundle + + WarpPI Calculator JOGL Engine + WarpPI Calculator engine-jogl project + + + it.cavallium + warppi-core + ${project.version} + + + org.jogamp.jogl + jogl-all-main + 2.3.2 + + + org.jogamp.gluegen + gluegen-rt-main + 2.3.2 + + + ar.com.hjg + pngj + + + + + + org.apache.felix + maven-bundle-plugin + true + + + * + it.cavallium.warppi.* + warppi-engine-jogl + + + + + org.apache.maven.plugins + maven-source-plugin + + + + diff --git a/hardware/pom.xml b/hardware/pom.xml index 22e5800f..1af4fd9b 100644 --- a/hardware/pom.xml +++ b/hardware/pom.xml @@ -6,7 +6,7 @@ it.cavallium warppi - ${project.version} + 0.9.0a2 warppi-hardware diff --git a/pom.xml b/pom.xml index 50ab7069..5bf95ad3 100755 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ it.cavallium warppi - ${project.version} + 0.9.0a2 pom WarpPI Calculator @@ -27,7 +27,6 @@ - 0.9.0a0 UTF-8 1.8 1.8 diff --git a/rules/pom.xml b/rules/pom.xml index 85fdabb0..0e81bb93 100644 --- a/rules/pom.xml +++ b/rules/pom.xml @@ -6,7 +6,7 @@ it.cavallium warppi - ${project.version} + 0.9.0a2 warppi-rules bundle diff --git a/teavm/pom.xml b/teavm/pom.xml index 148a5f0a..1493dc03 100644 --- a/teavm/pom.xml +++ b/teavm/pom.xml @@ -6,7 +6,7 @@ it.cavallium warppi - ${project.version} + 0.9.0a2 warppi-teavm jar From 76985314d4e12d7e7e0d9cdc98506a733f3336fc Mon Sep 17 00:00:00 2001 From: Cavallium Date: Mon, 15 Oct 2018 19:37:56 +0200 Subject: [PATCH 10/18] Removed useless files --- .gitmodules | 3 + LICENSE | 2 +- MmapByteBuffer.java | 33 - bigdecimal-math | 1 + core/pom.xml | 14 +- .../warppi/extra/tetris/TetrisScreen.java | 2 - .../main/java/org/nevec/rjm/Bernoulli.java | 102 - .../main/java/org/nevec/rjm/BigComplex.java | 220 -- .../java/org/nevec/rjm/BigDecimalMath.java | 3038 ----------------- .../java/org/nevec/rjm/BigIntegerMath.java | 644 ---- .../java/org/nevec/rjm/BigIntegerPoly.java | 764 ----- core/src/main/java/org/nevec/rjm/BigSurd.java | 567 --- .../main/java/org/nevec/rjm/BigSurdVec.java | 650 ---- core/src/main/java/org/nevec/rjm/Euler.java | 73 - .../src/main/java/org/nevec/rjm/EulerPhi.java | 66 - .../main/java/org/nevec/rjm/Factorial.java | 79 - .../src/main/java/org/nevec/rjm/Harmonic.java | 43 - core/src/main/java/org/nevec/rjm/Ifactor.java | 835 ----- .../main/java/org/nevec/rjm/PartitionsP.java | 88 - core/src/main/java/org/nevec/rjm/Prime.java | 321 -- core/src/main/java/org/nevec/rjm/RatPoly.java | 1045 ------ .../src/main/java/org/nevec/rjm/Rational.java | 900 ----- .../java/org/nevec/rjm/SafeMathContext.java | 27 - .../src/main/java/org/nevec/rjm/Wigner3j.java | 628 ---- .../main/java/org/nevec/rjm/Wigner3jGUI.java | 335 -- desktop/pom.xml | 22 +- engine-jogl/pom.xml | 16 +- {libs => hardware/libs}/raspi2fb | Bin hardware/pom.xml | 4 +- pom.xml | 2 + rules/pom.xml | 15 +- teavm/pom.xml | 4 +- util/.classpath | 27 + util/.gitignore | 1 + util/.project | 23 + util/pom.xml | 38 + .../java/it/cavallium/warppi/util/Error.java | 0 .../java/it/cavallium/warppi/util/Errors.java | 0 38 files changed, 124 insertions(+), 10508 deletions(-) delete mode 100644 MmapByteBuffer.java create mode 160000 bigdecimal-math delete mode 100644 core/src/main/java/org/nevec/rjm/Bernoulli.java delete mode 100644 core/src/main/java/org/nevec/rjm/BigComplex.java delete mode 100644 core/src/main/java/org/nevec/rjm/BigDecimalMath.java delete mode 100644 core/src/main/java/org/nevec/rjm/BigIntegerMath.java delete mode 100644 core/src/main/java/org/nevec/rjm/BigIntegerPoly.java delete mode 100644 core/src/main/java/org/nevec/rjm/BigSurd.java delete mode 100644 core/src/main/java/org/nevec/rjm/BigSurdVec.java delete mode 100644 core/src/main/java/org/nevec/rjm/Euler.java delete mode 100644 core/src/main/java/org/nevec/rjm/EulerPhi.java delete mode 100644 core/src/main/java/org/nevec/rjm/Factorial.java delete mode 100644 core/src/main/java/org/nevec/rjm/Harmonic.java delete mode 100644 core/src/main/java/org/nevec/rjm/Ifactor.java delete mode 100644 core/src/main/java/org/nevec/rjm/PartitionsP.java delete mode 100644 core/src/main/java/org/nevec/rjm/Prime.java delete mode 100644 core/src/main/java/org/nevec/rjm/RatPoly.java delete mode 100644 core/src/main/java/org/nevec/rjm/Rational.java delete mode 100644 core/src/main/java/org/nevec/rjm/SafeMathContext.java delete mode 100644 core/src/main/java/org/nevec/rjm/Wigner3j.java delete mode 100644 core/src/main/java/org/nevec/rjm/Wigner3jGUI.java rename {libs => hardware/libs}/raspi2fb (100%) create mode 100644 util/.classpath create mode 100644 util/.gitignore create mode 100644 util/.project create mode 100644 util/pom.xml rename {core => util}/src/main/java/it/cavallium/warppi/util/Error.java (100%) rename {core => util}/src/main/java/it/cavallium/warppi/util/Errors.java (100%) diff --git a/.gitmodules b/.gitmodules index 6b558787..612f980c 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "Flow"] path = Flow url = https://github.com/Cavallium/Flow +[submodule "bigdecimal-math"] + path = bigdecimal-math + url = https://github.com/Cavallium/bigdecimal-math diff --git a/LICENSE b/LICENSE index 753842b6..c59b79ea 100644 --- a/LICENSE +++ b/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright [yyyy] [name of copyright owner] + Copyright 2018 Andrea Cavalli Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/MmapByteBuffer.java b/MmapByteBuffer.java deleted file mode 100644 index 5573e989..00000000 --- a/MmapByteBuffer.java +++ /dev/null @@ -1,33 +0,0 @@ -package it.cavallium.warppi; - -import java.nio.ByteBuffer; - -public class MmapByteBuffer { - private int fd; - private int address; - private int length; - private ByteBuffer buffer; - - public MmapByteBuffer(int fd, int address, int length, ByteBuffer buffer) { - this.fd = fd; - this.address = address; - this.length = length; - this.buffer = buffer; - } - - public int getFd() { - return fd; - } - - public int getAddress() { - return address; - } - - public int getLength() { - return length; - } - - public ByteBuffer getBuffer() { - return buffer; - } -} \ No newline at end of file diff --git a/bigdecimal-math b/bigdecimal-math new file mode 160000 index 00000000..92a67122 --- /dev/null +++ b/bigdecimal-math @@ -0,0 +1 @@ +Subproject commit 92a67122350a38af726beae58d2582fb867a4c61 diff --git a/core/pom.xml b/core/pom.xml index 3d996471..b4e2c2ca 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -6,7 +6,7 @@ it.cavallium warppi - 0.9.0a2 + 0.9.0a2 warppi-core @@ -17,7 +17,17 @@ it.cavallium warppi-flow - ${project.version} + 0.9.0a2 + + + it.cavallium + warppi-util + 0.9.0a2 + + + it.cavallium + bigdecimal-math + 0.9.0a2 it.unimi.dsi diff --git a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrisScreen.java b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrisScreen.java index 2604a6cd..30c5c402 100644 --- a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrisScreen.java +++ b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrisScreen.java @@ -2,8 +2,6 @@ package it.cavallium.warppi.extra.tetris; import java.io.IOException; -import org.nevec.rjm.Wigner3j; - import it.cavallium.warppi.Engine; import it.cavallium.warppi.StaticVars; import it.cavallium.warppi.device.Keyboard; diff --git a/core/src/main/java/org/nevec/rjm/Bernoulli.java b/core/src/main/java/org/nevec/rjm/Bernoulli.java deleted file mode 100644 index d4834db0..00000000 --- a/core/src/main/java/org/nevec/rjm/Bernoulli.java +++ /dev/null @@ -1,102 +0,0 @@ -package org.nevec.rjm; - -import java.math.BigInteger; -import java.util.Vector; - -import it.cavallium.warppi.util.Error; - -/** - * Bernoulli numbers. - * - * @since 2006-06-25 - * @author Richard J. Mathar - */ -public class Bernoulli { - /* - * The list of all Bernoulli numbers as a vector, n=0,2,4,.... - */ - static Vector a = new Vector<>(); - - public Bernoulli() { - if (Bernoulli.a.size() == 0) { - Bernoulli.a.add(Rational.ONE); - Bernoulli.a.add(new Rational(1, 6)); - } - } - - /** - * Set a coefficient in the internal table. - * - * @param n - * the zero-based index of the coefficient. n=0 for the constant - * term. - * @param value - * the new value of the coefficient. - */ - protected void set(final int n, final Rational value) { - final int nindx = n / 2; - if (nindx < Bernoulli.a.size()) { - Bernoulli.a.set(nindx, value); - } else { - while (Bernoulli.a.size() < nindx) { - Bernoulli.a.add(Rational.ZERO); - } - Bernoulli.a.add(value); - } - } - - /** - * The Bernoulli number at the index provided. - * - * @param n - * the index, non-negative. - * @return the B_0=1 for n=0, B_1=-1/2 for n=1, B_2=1/6 for n=2 etc - * @throws Error - */ - public Rational at(final int n) throws Error { - if (n == 1) { - return new Rational(-1, 2); - } else if (n % 2 != 0) { - return Rational.ZERO; - } else { - final int nindx = n / 2; - if (Bernoulli.a.size() <= nindx) { - for (int i = 2 * Bernoulli.a.size(); i <= n; i += 2) { - set(i, doubleSum(i)); - } - } - return Bernoulli.a.elementAt(nindx); - } - } - - /* - * Generate a new B_n by a standard double sum. - * - * @param n The index of the Bernoulli number. - * - * @return The Bernoulli number at n. - */ - private Rational doubleSum(final int n) throws Error { - Rational resul = Rational.ZERO; - for (int k = 0; k <= n; k++) { - Rational jsum = Rational.ZERO; - BigInteger bin = BigInteger.ONE; - for (int j = 0; j <= k; j++) { - final BigInteger jpown = new BigInteger("" + j).pow(n); - if (j % 2 == 0) { - jsum = jsum.add(bin.multiply(jpown)); - } else { - jsum = jsum.subtract(bin.multiply(jpown)); - } - - /* - * update binomial(k,j) recursively - */ - bin = bin.multiply(new BigInteger("" + (k - j))).divide(new BigInteger("" + (j + 1))); - } - resul = resul.add(jsum.divide(new BigInteger("" + (k + 1)))); - } - return resul; - } - -} /* Bernoulli */ diff --git a/core/src/main/java/org/nevec/rjm/BigComplex.java b/core/src/main/java/org/nevec/rjm/BigComplex.java deleted file mode 100644 index 636e53a4..00000000 --- a/core/src/main/java/org/nevec/rjm/BigComplex.java +++ /dev/null @@ -1,220 +0,0 @@ -package org.nevec.rjm; - -import java.math.BigDecimal; -import java.math.MathContext; - -/** - * Complex numbers with BigDecimal real and imaginary components - * - * @since 2008-10-26 - * @author Richard J. Mathar - */ -public class BigComplex { - /** - * real part - */ - BigDecimal re; - - /** - * imaginary part - */ - BigDecimal im; - - /** - * The constant that equals zero - */ - final static BigComplex ZERO = new BigComplex(BigDecimal.ZERO, BigDecimal.ZERO); - - /** - * Default ctor equivalent to zero. - */ - public BigComplex() { - re = BigDecimal.ZERO; - im = BigDecimal.ZERO; - } - - /** - * ctor with real and imaginary parts - * - * @param x - * real part - * @param y - * imaginary part - */ - public BigComplex(final BigDecimal x, final BigDecimal y) { - re = x; - im = y; - } - - /** - * ctor with real part. - * - * @param x - * real part. - * The imaginary part is set to zero. - */ - public BigComplex(final BigDecimal x) { - re = x; - im = BigDecimal.ZERO; - } - - /** - * ctor with real and imaginary parts - * - * @param x - * real part - * @param y - * imaginary part - */ - public BigComplex(final double x, final double y) { - re = new BigDecimal(x); - im = new BigDecimal(y); - } - - /** - * Multiply with another BigComplex - * - * @param oth - * The BigComplex which is a factor in the product - * @param mc - * Defining precision and rounding mode - * @return This multiplied by oth - * @since 2010-07-19 implemented with 3 multiplications and 5 - * additions/subtractions - */ - BigComplex multiply(final BigComplex oth, final MathContext mc) { - final BigDecimal a = re.add(im).multiply(oth.re); - final BigDecimal b = oth.re.add(oth.im).multiply(im); - final BigDecimal c = oth.im.subtract(oth.re).multiply(re); - final BigDecimal x = a.subtract(b, mc); - final BigDecimal y = a.add(c, mc); - return new BigComplex(x, y); - } - - /** - * Add a BigDecimal - * - * @param oth - * the value to be added to the real part. - * @return this added to oth - */ - BigComplex add(final BigDecimal oth) { - final BigDecimal x = re.add(oth); - return new BigComplex(x, im); - } - - /** - * Subtract another BigComplex - * - * @param oth - * the value to be subtracted from this. - * @return this minus oth - */ - BigComplex subtract(final BigComplex oth) { - final BigDecimal x = re.subtract(oth.re); - final BigDecimal y = im.subtract(oth.im); - return new BigComplex(x, y); - } - - /** - * Complex-conjugation - * - * @return the complex conjugate of this. - */ - BigComplex conj() { - return new BigComplex(re, im.negate()); - } - - /** - * The absolute value squared. - * - * @return The sum of the squares of real and imaginary parts. - * This is the square of BigComplex.abs() . - */ - BigDecimal norm() { - return re.multiply(re).add(im.multiply(im)); - } - - /** - * The absolute value. - * - * @return the square root of the sum of the squares of real and imaginary - * parts. - * @since 2008-10-27 - */ - BigDecimal abs(final MathContext mc) { - return BigDecimalMath.sqrt(norm(), mc); - } - - /** - * The square root. - * - * @return the square root of the this. - * The branch is chosen such that the imaginary part of the result - * has the - * same sign as the imaginary part of this. - * @see Tim Ahrendt, Fast - * High-precision computation of complex square roots, - * ISSAC 1996 p142-149. - * @since 2008-10-27 - */ - BigComplex sqrt(final MathContext mc) { - final BigDecimal half = new BigDecimal("2"); - /* - * compute l=sqrt(re^2+im^2), then u=sqrt((l+re)/2) - * and v= +- sqrt((l-re)/2 as the new real and imaginary parts. - */ - final BigDecimal l = abs(mc); - if (l.compareTo(BigDecimal.ZERO) == 0) { - return new BigComplex(BigDecimalMath.scalePrec(BigDecimal.ZERO, mc), BigDecimalMath.scalePrec(BigDecimal.ZERO, mc)); - } - final BigDecimal u = BigDecimalMath.sqrt(l.add(re).divide(half, mc), mc); - final BigDecimal v = BigDecimalMath.sqrt(l.subtract(re).divide(half, mc), mc); - if (im.compareTo(BigDecimal.ZERO) >= 0) { - return new BigComplex(u, v); - } else { - return new BigComplex(u, v.negate()); - } - } - - /** - * The inverse of this. - * - * @return 1/this - */ - BigComplex inverse(final MathContext mc) { - final BigDecimal hyp = norm(); - /* 1/(x+iy)= (x-iy)/(x^2+y^2 */ - return new BigComplex(re.divide(hyp, mc), im.divide(hyp, mc).negate()); - } - - /** - * Divide through another BigComplex number. - * - * @return this/oth - */ - BigComplex divide(final BigComplex oth, final MathContext mc) { - /* lazy implementation: (x+iy)/(a+ib)= (x+iy)* 1/(a+ib) */ - return multiply(oth.inverse(mc), mc); - } - - /** - * Human-readable Fortran-type display - * - * @return real and imaginary part in parenthesis, divided by a comma. - */ - @Override - public String toString() { - return "(" + re.toString() + "," + im.toString() + ")"; - } - - /** - * Human-readable Fortran-type display - * - * @return real and imaginary part in parenthesis, divided by a comma. - */ - public String toString(final MathContext mc) { - return "(" + re.round(mc).toString() + "," + im.round(mc).toString() + ")"; - } - -} /* BigComplex */ diff --git a/core/src/main/java/org/nevec/rjm/BigDecimalMath.java b/core/src/main/java/org/nevec/rjm/BigDecimalMath.java deleted file mode 100644 index 926f1ded..00000000 --- a/core/src/main/java/org/nevec/rjm/BigDecimalMath.java +++ /dev/null @@ -1,3038 +0,0 @@ -package org.nevec.rjm; - -import java.math.BigDecimal; -import java.math.BigInteger; -import java.math.MathContext; -import java.security.ProviderException; - -import it.cavallium.warppi.util.Error; - -/** - * BigDecimal special functions. - * A Java Math.BigDecimal - * Implementation of Core Mathematical Functions - * - * @since 2009-05-22 - * @author Richard J. Mathar - * @see apfloat - * @see dfp - * @see JScience - */ -public class BigDecimalMath { - - /** - * The base of the natural logarithm in a predefined accuracy. - * http://www.cs.arizona.edu/icon/oddsends/e.htm - * The precision of the predefined constant is one less than - * the string's length, taking into account the decimal dot. - * static int E_PRECISION = E.length()-1 ; - */ - static BigDecimal E = new BigDecimal("2.71828182845904523536028747135266249775724709369995957496696762772407663035354" + "759457138217852516642742746639193200305992181741359662904357290033429526059563" + "073813232862794349076323382988075319525101901157383418793070215408914993488416" + "750924476146066808226480016847741185374234544243710753907774499206955170276183" + "860626133138458300075204493382656029760673711320070932870912744374704723069697" + "720931014169283681902551510865746377211125238978442505695369677078544996996794" + "686445490598793163688923009879312773617821542499922957635148220826989519366803" + "318252886939849646510582093923982948879332036250944311730123819706841614039701" + "983767932068328237646480429531180232878250981945581530175671736133206981125099" + "618188159304169035159888851934580727386673858942287922849989208680582574927961" + "048419844436346324496848756023362482704197862320900216099023530436994184914631" + "409343173814364054625315209618369088870701676839642437814059271456354906130310" + "720851038375051011574770417189861068739696552126715468895703503540212340784981" + "933432106817012100562788023519303322474501585390473041995777709350366041699732" + "972508868769664035557071622684471625607988265178713419512466520103059212366771" + "943252786753985589448969709640975459185695638023637016211204774272283648961342" + "251644507818244235294863637214174023889344124796357437026375529444833799801612" + "549227850925778256209262264832627793338656648162772516401910590049164499828931"); - - /** - * Euler's constant Pi. - * http://www.cs.arizona.edu/icon/oddsends/pi.htm - */ - static BigDecimal PI = new BigDecimal("3.14159265358979323846264338327950288419716939937510582097494459230781640628620" + "899862803482534211706798214808651328230664709384460955058223172535940812848111" + "745028410270193852110555964462294895493038196442881097566593344612847564823378" + "678316527120190914564856692346034861045432664821339360726024914127372458700660" + "631558817488152092096282925409171536436789259036001133053054882046652138414695" + "194151160943305727036575959195309218611738193261179310511854807446237996274956" + "735188575272489122793818301194912983367336244065664308602139494639522473719070" + "217986094370277053921717629317675238467481846766940513200056812714526356082778" + "577134275778960917363717872146844090122495343014654958537105079227968925892354" + "201995611212902196086403441815981362977477130996051870721134999999837297804995" + "105973173281609631859502445945534690830264252230825334468503526193118817101000" + "313783875288658753320838142061717766914730359825349042875546873115956286388235" + "378759375195778185778053217122680661300192787661119590921642019893809525720106" + "548586327886593615338182796823030195203530185296899577362259941389124972177528" + "347913151557485724245415069595082953311686172785588907509838175463746493931925" + "506040092770167113900984882401285836160356370766010471018194295559619894676783" + "744944825537977472684710404753464620804668425906949129331367702898915210475216" + "205696602405803815019351125338243003558764024749647326391419927260426992279678" + "235478163600934172164121992458631503028618297455570674983850549458858692699569" + "092721079750930295532116534498720275596023648066549911988183479775356636980742" + "654252786255181841757467289097777279380008164706001614524919217321721477235014"); - - /** - * Euler-Mascheroni constant lower-case gamma. - * http://www.worldwideschool.org/library/books/sci/math/ - * MiscellaneousMathematicalConstants/chap35.html - */ - static BigDecimal GAMMA = new BigDecimal("0.577215664901532860606512090082402431" + "0421593359399235988057672348848677267776646709369470632917467495146314472498070" + "8248096050401448654283622417399764492353625350033374293733773767394279259525824" + "7094916008735203948165670853233151776611528621199501507984793745085705740029921" + "3547861466940296043254215190587755352673313992540129674205137541395491116851028" + "0798423487758720503843109399736137255306088933126760017247953783675927135157722" + "6102734929139407984301034177717780881549570661075010161916633401522789358679654" + "9725203621287922655595366962817638879272680132431010476505963703947394957638906" + "5729679296010090151251959509222435014093498712282479497471956469763185066761290" + "6381105182419744486783638086174945516989279230187739107294578155431600500218284" + "4096053772434203285478367015177394398700302370339518328690001558193988042707411" + "5422278197165230110735658339673487176504919418123000406546931429992977795693031" + "0050308630341856980323108369164002589297089098548682577736428825395492587362959" + "6133298574739302373438847070370284412920166417850248733379080562754998434590761" + "6431671031467107223700218107450444186647591348036690255324586254422253451813879" + "1243457350136129778227828814894590986384600629316947188714958752549236649352047" + "3243641097268276160877595088095126208404544477992299157248292516251278427659657" + "0832146102982146179519579590959227042089896279712553632179488737642106606070659" + "8256199010288075612519913751167821764361905705844078357350158005607745793421314" + "49885007864151716151945"); - - /** - * Natural logarithm of 2. - * http://www.worldwideschool.org/library/books/sci/math/ - * MiscellaneousMathematicalConstants/chap58.html - */ - static BigDecimal LOG2 = new BigDecimal("0.693147180559945309417232121458176568075" + "50013436025525412068000949339362196969471560586332699641868754200148102057068573" + "368552023575813055703267075163507596193072757082837143519030703862389167347112335" + "011536449795523912047517268157493206515552473413952588295045300709532636664265410" + "423915781495204374043038550080194417064167151864471283996817178454695702627163106" + "454615025720740248163777338963855069526066834113727387372292895649354702576265209" + "885969320196505855476470330679365443254763274495125040606943814710468994650622016" + "772042452452961268794654619316517468139267250410380254625965686914419287160829380" + "317271436778265487756648508567407764845146443994046142260319309673540257444607030" + "809608504748663852313818167675143866747664789088143714198549423151997354880375165" + "861275352916610007105355824987941472950929311389715599820565439287170007218085761" + "025236889213244971389320378439353088774825970171559107088236836275898425891853530" + "243634214367061189236789192372314672321720534016492568727477823445353476481149418" + "642386776774406069562657379600867076257199184734022651462837904883062033061144630" + "073719489002743643965002580936519443041191150608094879306786515887090060520346842" + "973619384128965255653968602219412292420757432175748909770675268711581705113700915" + "894266547859596489065305846025866838294002283300538207400567705304678700184162404" + "418833232798386349001563121889560650553151272199398332030751408426091479001265168" + "243443893572472788205486271552741877243002489794540196187233980860831664811490930" + "667519339312890431641370681397776498176974868903887789991296503619270710889264105" + "230924783917373501229842420499568935992206602204654941510613"); - - /** - * Euler's constant. - * - * @param mc - * The required precision of the result. - * @return 3.14159... - * @throws Error - * @since 2009-05-29 - */ - static public BigDecimal pi(final MathContext mc) throws Error { - /* look it up if possible */ - if (mc.getPrecision() < BigDecimalMath.PI.precision()) { - return BigDecimalMath.PI.round(mc); - } else { - /* - * Broadhurst arXiv:math/9803067 - */ - final int[] a = { 1, 0, 0, -1, -1, -1, 0, 0 }; - final BigDecimal S = BigDecimalMath.broadhurstBBP(1, 1, a, mc); - return BigDecimalMath.multiplyRound(S, 8); - } - } /* BigDecimalMath.pi */ - - /** - * Euler-Mascheroni constant. - * - * @param mc - * The required precision of the result. - * @return 0.577... - * @throws Error - * @since 2009-08-13 - */ - static public BigDecimal gamma(final MathContext mc) throws Error { - /* look it up if possible */ - if (mc.getPrecision() < BigDecimalMath.GAMMA.precision()) { - return BigDecimalMath.GAMMA.round(mc); - } else { - final double eps = BigDecimalMath.prec2err(0.577, mc.getPrecision()); - - /* - * Euler-Stieltjes as shown in Dilcher, Aequat Math 48 (1) (1994) - * 55-85 - */ - MathContext mcloc = SafeMathContext.newMathContext(2 + mc.getPrecision()); - BigDecimal resul = BigDecimal.ONE; - resul = resul.add(BigDecimalMath.log(2, mcloc)); - resul = resul.subtract(BigDecimalMath.log(3, mcloc)); - - /* - * how many terms: zeta-1 falls as 1/2^(2n+1), so the - * terms drop faster than 1/2^(4n+2). Set 1/2^(4kmax+2) < eps. - * Leading term zeta(3)/(4^1*3) is 0.017. Leading zeta(3) is 1.2. - * Log(2) is 0.7 - */ - final int kmax = (int) ((Math.log(eps / 0.7) - 2.) / 4.); - mcloc = SafeMathContext.newMathContext(1 + BigDecimalMath.err2prec(1.2, eps / kmax)); - for (int n = 1;; n++) { - /* - * zeta is close to 1. Division of zeta-1 through - * 4^n*(2n+1) means divion through roughly 2^(2n+1) - */ - BigDecimal c = BigDecimalMath.zeta(2 * n + 1, mcloc).subtract(BigDecimal.ONE); - BigInteger fourn = new BigInteger("" + (2 * n + 1)); - fourn = fourn.shiftLeft(2 * n); - c = BigDecimalMath.divideRound(c, fourn); - resul = resul.subtract(c); - if (c.doubleValue() < 0.1 * eps) { - break; - } - } - return resul.round(mc); - } - - } /* BigDecimalMath.gamma */ - - /** - * The square root. - * - * @param x - * the non-negative argument. - * @param mc - * @return the square root of the BigDecimal. - * @since 2008-10-27 - */ - static public BigDecimal sqrt(final BigDecimal x, final MathContext mc) { - if (x.compareTo(BigDecimal.ZERO) < 0) { - throw new ArithmeticException("negative argument " + x.toString() + " of square root"); - } - if (x.abs().subtract(new BigDecimal(Math.pow(10., -mc.getPrecision()))).compareTo(BigDecimal.ZERO) < 0) { - return BigDecimalMath.scalePrec(BigDecimal.ZERO, mc); - } - /* start the computation from a double precision estimate */ - BigDecimal s = new BigDecimal(Math.sqrt(x.doubleValue()), mc); - final BigDecimal half = new BigDecimal("2"); - - /* increase the local accuracy by 2 digits */ - final MathContext locmc = SafeMathContext.newMathContext(mc.getPrecision() + 2, mc.getRoundingMode()); - - /* - * relative accuracy requested is 10^(-precision) - */ - final double eps = Math.pow(10.0, -mc.getPrecision()); - for (;;) { - /* - * s = s -(s/2-x/2s); test correction s-x/s for being - * smaller than the precision requested. The relative correction is - * 1-x/s^2, - * (actually half of this, which we use for a little bit of - * additional protection). - */ - if (Math.abs(BigDecimal.ONE.subtract(x.divide(s.pow(2, locmc), locmc)).doubleValue()) <= eps) { - break; - } - s = s.add(x.divide(s, locmc)).divide(half, locmc); - /* debugging */ - // System.out.println("itr "+x.round(locmc).toString() + " " + - // s.round(locmc).toString()) ; - } - return s; - } /* BigDecimalMath.sqrt */ - - /** - * The square root. - * - * @param x - * the non-negative argument. - * @return the square root of the BigDecimal rounded to the precision - * implied by x. - * @since 2009-06-25 - */ - static public BigDecimal sqrt(final BigDecimal x) { - if (x.compareTo(BigDecimal.ZERO) < 0) { - throw new ArithmeticException("negative argument " + x.toString() + " of square root"); - } - - return BigDecimalMath.root(2, x); - } /* BigDecimalMath.sqrt */ - - /** - * The cube root. - * - * @param x - * The argument. - * @return The cubic root of the BigDecimal rounded to the precision implied - * by x. - * The sign of the result is the sign of the argument. - * @since 2009-08-16 - */ - static public BigDecimal cbrt(final BigDecimal x) { - if (x.compareTo(BigDecimal.ZERO) < 0) { - return BigDecimalMath.root(3, x.negate()).negate(); - } else { - return BigDecimalMath.root(3, x); - } - } /* BigDecimalMath.cbrt */ - - /** - * The integer root. - * - * @param n - * the positive argument. - * @param x - * the non-negative argument. - * @return The n-th root of the BigDecimal rounded to the precision implied - * by x, x^(1/n). - * @since 2009-07-30 - */ - static public BigDecimal root(final int n, final BigDecimal x) { - if (x.compareTo(BigDecimal.ZERO) < 0) { - throw new ArithmeticException("negative argument " + x.toString() + " of root"); - } - if (n <= 0) { - throw new ArithmeticException("negative power " + n + " of root"); - } - - if (n == 1) { - return x; - } - - /* start the computation from a double precision estimate */ - BigDecimal s = new BigDecimal(Math.pow(x.doubleValue(), 1.0 / n)); - - /* - * this creates nth with nominal precision of 1 digit - */ - final BigDecimal nth = new BigDecimal(n); - - /* - * Specify an internal accuracy within the loop which is - * slightly larger than what is demanded by 'eps' below. - */ - final BigDecimal xhighpr = BigDecimalMath.scalePrec(x, 2); - final MathContext mc = SafeMathContext.newMathContext(2 + x.precision()); - - /* - * Relative accuracy of the result is eps. - */ - final double eps = x.ulp().doubleValue() / (2 * n * x.doubleValue()); - for (;;) { - /* - * s = s -(s/n-x/n/s^(n-1)) = s-(s-x/s^(n-1))/n; test correction - * s/n-x/s for being - * smaller than the precision requested. The relative correction is - * (1-x/s^n)/n, - */ - BigDecimal c = xhighpr.divide(s.pow(n - 1), mc); - c = s.subtract(c); - final MathContext locmc = SafeMathContext.newMathContext(c.precision()); - c = c.divide(nth, locmc); - s = s.subtract(c); - if (Math.abs(c.doubleValue() / s.doubleValue()) < eps) { - break; - } - } - return s.round(SafeMathContext.newMathContext(BigDecimalMath.err2prec(eps))); - } /* BigDecimalMath.root */ - - /** - * The hypotenuse. - * - * @param x - * the first argument. - * @param y - * the second argument. - * @return the square root of the sum of the squares of the two arguments, - * sqrt(x^2+y^2). - * @since 2009-06-25 - */ - static public BigDecimal hypot(final BigDecimal x, final BigDecimal y) { - /* - * compute x^2+y^2 - */ - BigDecimal z = x.pow(2).add(y.pow(2)); - - /* - * truncate to the precision set by x and y. Absolute error = - * 2*x*xerr+2*y*yerr, - * where the two errors are 1/2 of the ulp's. Two intermediate protectio - * digits. - */ - final BigDecimal zerr = x.abs().multiply(x.ulp()).add(y.abs().multiply(y.ulp())); - MathContext mc = SafeMathContext.newMathContext(2 + BigDecimalMath.err2prec(z, zerr)); - - /* Pull square root */ - z = BigDecimalMath.sqrt(z.round(mc)); - - /* - * Final rounding. Absolute error in the square root is - * (y*yerr+x*xerr)/z, where zerr holds 2*(x*xerr+y*yerr). - */ - mc = SafeMathContext.newMathContext(BigDecimalMath.err2prec(z.doubleValue(), 0.5 * zerr.doubleValue() / z.doubleValue())); - return z.round(mc); - } /* BigDecimalMath.hypot */ - - /** - * The hypotenuse. - * - * @param n - * the first argument. - * @param x - * the second argument. - * @return the square root of the sum of the squares of the two arguments, - * sqrt(n^2+x^2). - * @since 2009-08-05 - */ - static public BigDecimal hypot(final int n, final BigDecimal x) { - /* - * compute n^2+x^2 in infinite precision - */ - BigDecimal z = new BigDecimal(n).pow(2).add(x.pow(2)); - - /* - * Truncate to the precision set by x. Absolute error = in z (square of - * the result) is |2*x*xerr|, - * where the error is 1/2 of the ulp. Two intermediate protection - * digits. - * zerr is a signed value, but used only in conjunction with err2prec(), - * so this feature does not harm. - */ - final double zerr = x.doubleValue() * x.ulp().doubleValue(); - MathContext mc = SafeMathContext.newMathContext(2 + BigDecimalMath.err2prec(z.doubleValue(), zerr)); - - /* Pull square root */ - z = BigDecimalMath.sqrt(z.round(mc)); - - /* - * Final rounding. Absolute error in the square root is x*xerr/z, where - * zerr holds 2*x*xerr. - */ - mc = SafeMathContext.newMathContext(BigDecimalMath.err2prec(z.doubleValue(), 0.5 * zerr / z.doubleValue())); - return z.round(mc); - } /* BigDecimalMath.hypot */ - - /** - * A suggestion for the maximum numter of terms in the Taylor expansion of - * the exponential. - */ - static private int TAYLOR_NTERM = 8; - - /** - * The exponential function. - * - * @param x - * the argument. - * @return exp(x). - * The precision of the result is implicitly defined by the - * precision in the argument. - * In particular this means that "Invalid Operation" errors are - * thrown if catastrophic - * cancellation of digits causes the result to have no valid digits - * left. - * @since 2009-05-29 - * @author Richard J. Mathar - */ - static public BigDecimal exp(final BigDecimal x) { - /* - * To calculate the value if x is negative, use exp(-x) = 1/exp(x) - */ - if (x.compareTo(BigDecimal.ZERO) < 0) { - final BigDecimal invx = BigDecimalMath.exp(x.negate()); - /* - * Relative error in inverse of invx is the same as the relative - * errror in invx. - * This is used to define the precision of the result. - */ - final MathContext mc = SafeMathContext.newMathContext(invx.precision()); - return BigDecimal.ONE.divide(invx, mc); - } else if (x.compareTo(BigDecimal.ZERO) == 0) { - /* - * recover the valid number of digits from x.ulp(), if x hits the - * zero. The x.precision() is 1 then, and does not provide this - * information. - */ - return BigDecimalMath.scalePrec(BigDecimal.ONE, -(int) Math.log10(x.ulp().doubleValue())); - } else { - /* - * Push the number in the Taylor expansion down to a small - * value where TAYLOR_NTERM terms will do. If x<1, the n-th term is - * of the order - * x^n/n!, and equal to both the absolute and relative error of the - * result - * since the result is close to 1. The x.ulp() sets the relative and - * absolute error - * of the result, as estimated from the first Taylor term. - * We want x^TAYLOR_NTERM/TAYLOR_NTERM! < x.ulp, which is guaranteed - * if - * x^TAYLOR_NTERM < TAYLOR_NTERM*(TAYLOR_NTERM-1)*...*x.ulp. - */ - final double xDbl = x.doubleValue(); - final double xUlpDbl = x.ulp().doubleValue(); - if (Math.pow(xDbl, BigDecimalMath.TAYLOR_NTERM) < BigDecimalMath.TAYLOR_NTERM * (BigDecimalMath.TAYLOR_NTERM - 1.0) * (BigDecimalMath.TAYLOR_NTERM - 2.0) * xUlpDbl) { - /* - * Add TAYLOR_NTERM terms of the Taylor expansion (Euler's sum - * formula) - */ - BigDecimal resul = BigDecimal.ONE; - - /* x^i */ - BigDecimal xpowi = BigDecimal.ONE; - - /* i factorial */ - BigInteger ifac = BigInteger.ONE; - - /* - * TAYLOR_NTERM terms to be added means we move x.ulp() to the - * right - * for each power of 10 in TAYLOR_NTERM, so the addition won't - * add noise beyond - * what's already in x. - */ - final MathContext mcTay = SafeMathContext.newMathContext(BigDecimalMath.err2prec(1., xUlpDbl / BigDecimalMath.TAYLOR_NTERM)); - for (int i = 1; i <= BigDecimalMath.TAYLOR_NTERM; i++) { - ifac = ifac.multiply(new BigInteger("" + i)); - xpowi = xpowi.multiply(x); - final BigDecimal c = xpowi.divide(new BigDecimal(ifac), mcTay); - resul = resul.add(c); - if (Math.abs(xpowi.doubleValue()) < i && Math.abs(c.doubleValue()) < 0.5 * xUlpDbl) { - break; - } - } - /* - * exp(x+deltax) = exp(x)(1+deltax) if deltax is <<1. So the - * relative error - * in the result equals the absolute error in the argument. - */ - final MathContext mc = SafeMathContext.newMathContext(BigDecimalMath.err2prec(xUlpDbl / 2.)); - return resul.round(mc); - } else { - /* - * Compute exp(x) = (exp(0.1*x))^10. Division by 10 does not - * lead - * to loss of accuracy. - */ - int exSc = (int) (1.0 - Math.log10(BigDecimalMath.TAYLOR_NTERM * (BigDecimalMath.TAYLOR_NTERM - 1.0) * (BigDecimalMath.TAYLOR_NTERM - 2.0) * xUlpDbl / Math.pow(xDbl, BigDecimalMath.TAYLOR_NTERM)) / (BigDecimalMath.TAYLOR_NTERM - 1.0)); - final BigDecimal xby10 = x.scaleByPowerOfTen(-exSc); - BigDecimal expxby10 = BigDecimalMath.exp(xby10); - - /* - * Final powering by 10 means that the relative error of the - * result - * is 10 times the relative error of the base (First order - * binomial expansion). - * This looses one digit. - */ - final MathContext mc = SafeMathContext.newMathContext(expxby10.precision() - exSc); - /* - * Rescaling the powers of 10 is done in chunks of a maximum of - * 8 to avoid an invalid operation - * response by the BigDecimal.pow library or integer overflow. - */ - while (exSc > 0) { - int exsub = Math.min(8, exSc); - exSc -= exsub; - final MathContext mctmp = SafeMathContext.newMathContext(expxby10.precision() - exsub + 2); - int pex = 1; - while (exsub-- > 0) { - pex *= 10; - } - expxby10 = expxby10.pow(pex, mctmp); - } - return expxby10.round(mc); - } - } - } /* BigDecimalMath.exp */ - - /** - * The base of the natural logarithm. - * - * @param mc - * the required precision of the result - * @return exp(1) = 2.71828.... - * @since 2009-05-29 - */ - static public BigDecimal exp(final MathContext mc) { - /* look it up if possible */ - if (mc.getPrecision() < BigDecimalMath.E.precision()) { - return BigDecimalMath.E.round(mc); - } else { - /* - * Instantiate a 1.0 with the requested pseudo-accuracy - * and delegate the computation to the public method above. - */ - final BigDecimal uni = BigDecimalMath.scalePrec(BigDecimal.ONE, mc.getPrecision()); - return BigDecimalMath.exp(uni); - } - } /* BigDecimalMath.exp */ - - /** - * The natural logarithm. - * - * @param x - * the argument. - * @return ln(x). - * The precision of the result is implicitly defined by the - * precision in the argument. - * @since 2009-05-29 - * @author Richard J. Mathar - */ - static public BigDecimal log(final BigDecimal x) { - /* - * the value is undefined if x is negative. - */ - if (x.compareTo(BigDecimal.ZERO) < 0) { - throw new ArithmeticException("Cannot take log of negative " + x.toString()); - } else if (x.compareTo(BigDecimal.ONE) == 0) { - /* log 1. = 0. */ - return BigDecimalMath.scalePrec(BigDecimal.ZERO, x.precision() - 1); - } else if (Math.abs(x.doubleValue() - 1.0) <= 0.3) { - /* - * The standard Taylor series around x=1, z=0, z=x-1. - * Abramowitz-Stegun 4.124. - * The absolute error is err(z)/(1+z) = err(x)/x. - */ - final BigDecimal z = BigDecimalMath.scalePrec(x.subtract(BigDecimal.ONE), 2); - BigDecimal zpown = z; - final double eps = 0.5 * x.ulp().doubleValue() / Math.abs(x.doubleValue()); - BigDecimal resul = z; - for (int k = 2;; k++) { - zpown = BigDecimalMath.multiplyRound(zpown, z); - final BigDecimal c = BigDecimalMath.divideRound(zpown, k); - if (k % 2 == 0) { - resul = resul.subtract(c); - } else { - resul = resul.add(c); - } - if (Math.abs(c.doubleValue()) < eps) { - break; - } - } - final MathContext mc = SafeMathContext.newMathContext(BigDecimalMath.err2prec(resul.doubleValue(), eps)); - return resul.round(mc); - } else { - final double xDbl = x.doubleValue(); - final double xUlpDbl = x.ulp().doubleValue(); - - /* - * Map log(x) = log root[r](x)^r = r*log( root[r](x)) with the aim - * to move roor[r](x) near to 1.2 (that is, below the 0.3 appearing - * above), where log(1.2) is roughly 0.2. - */ - int r = (int) (Math.log(xDbl) / 0.2); - - /* - * Since the actual requirement is a function of the value 0.3 - * appearing above, - * we avoid the hypothetical case of endless recurrence by ensuring - * that r >= 2. - */ - r = Math.max(2, r); - - /* - * Compute r-th root with 2 additional digits of precision - */ - final BigDecimal xhighpr = BigDecimalMath.scalePrec(x, 2); - BigDecimal resul = BigDecimalMath.root(r, xhighpr); - resul = BigDecimalMath.log(resul).multiply(new BigDecimal(r)); - - /* - * error propagation: log(x+errx) = log(x)+errx/x, so the absolute - * error - * in the result equals the relative error in the input, - * xUlpDbl/xDbl . - */ - final MathContext mc = SafeMathContext.newMathContext(BigDecimalMath.err2prec(resul.doubleValue(), xUlpDbl / xDbl)); - return resul.round(mc); - } - } /* BigDecimalMath.log */ - - /** - * The natural logarithm. - * - * @param n - * The main argument, a strictly positive integer. - * @param mc - * The requirements on the precision. - * @return ln(n). - * @since 2009-08-08 - * @author Richard J. Mathar - * @throws Error - */ - static public BigDecimal log(final int n, final MathContext mc) throws Error { - /* - * the value is undefined if x is negative. - */ - if (n <= 0) { - throw new ArithmeticException("Cannot take log of negative " + n); - } else if (n == 1) { - return BigDecimal.ZERO; - } else if (n == 2) { - if (mc.getPrecision() < BigDecimalMath.LOG2.precision()) { - return BigDecimalMath.LOG2.round(mc); - } else { - /* - * Broadhurst arXiv:math/9803067 - * Error propagation: the error in log(2) is twice the error in - * S(2,-5,...). - */ - final int[] a = { 2, -5, -2, -7, -2, -5, 2, -3 }; - BigDecimal S = BigDecimalMath.broadhurstBBP(2, 1, a, SafeMathContext.newMathContext(1 + mc.getPrecision())); - S = S.multiply(new BigDecimal(8)); - S = BigDecimalMath.sqrt(BigDecimalMath.divideRound(S, 3)); - return S.round(mc); - } - } else if (n == 3) { - /* - * summation of a series roughly proportional to (7/500)^k. Estimate - * count - * of terms to estimate the precision (drop the favorable additional - * 1/k here): 0.013^k <= 10^(-precision), so k*log10(0.013) <= - * -precision - * so k>= precision/1.87. - */ - final int kmax = (int) (mc.getPrecision() / 1.87); - MathContext mcloc = SafeMathContext.newMathContext(mc.getPrecision() + 1 + (int) Math.log10(kmax * 0.693 / 1.098)); - BigDecimal log3 = BigDecimalMath.multiplyRound(BigDecimalMath.log(2, mcloc), 19); - - /* - * log3 is roughly 1, so absolute and relative error are the same. - * The - * result will be divided by 12, so a conservative error is the one - * already found in mc - */ - final double eps = BigDecimalMath.prec2err(1.098, mc.getPrecision()) / kmax; - final Rational r = new Rational(7153, 524288); - Rational pk = new Rational(7153, 524288); - for (int k = 1;; k++) { - final Rational tmp = pk.divide(k); - if (tmp.doubleValue() < eps) { - break; - } - - /* - * how many digits of tmp do we need in the sum? - */ - mcloc = SafeMathContext.newMathContext(BigDecimalMath.err2prec(tmp.doubleValue(), eps)); - final BigDecimal c = pk.divide(k).BigDecimalValue(mcloc); - if (k % 2 != 0) { - log3 = log3.add(c); - } else { - log3 = log3.subtract(c); - } - pk = pk.multiply(r); - } - log3 = BigDecimalMath.divideRound(log3, 12); - return log3.round(mc); - } else if (n == 5) { - /* - * summation of a series roughly proportional to (7/160)^k. Estimate - * count - * of terms to estimate the precision (drop the favorable additional - * 1/k here): 0.046^k <= 10^(-precision), so k*log10(0.046) <= - * -precision - * so k>= precision/1.33. - */ - final int kmax = (int) (mc.getPrecision() / 1.33); - MathContext mcloc = SafeMathContext.newMathContext(mc.getPrecision() + 1 + (int) Math.log10(kmax * 0.693 / 1.609)); - BigDecimal log5 = BigDecimalMath.multiplyRound(BigDecimalMath.log(2, mcloc), 14); - - /* - * log5 is roughly 1.6, so absolute and relative error are the same. - * The - * result will be divided by 6, so a conservative error is the one - * already found in mc - */ - final double eps = BigDecimalMath.prec2err(1.6, mc.getPrecision()) / kmax; - final Rational r = new Rational(759, 16384); - Rational pk = new Rational(759, 16384); - for (int k = 1;; k++) { - final Rational tmp = pk.divide(k); - if (tmp.doubleValue() < eps) { - break; - } - - /* - * how many digits of tmp do we need in the sum? - */ - mcloc = SafeMathContext.newMathContext(BigDecimalMath.err2prec(tmp.doubleValue(), eps)); - final BigDecimal c = pk.divide(k).BigDecimalValue(mcloc); - log5 = log5.subtract(c); - pk = pk.multiply(r); - } - log5 = BigDecimalMath.divideRound(log5, 6); - return log5.round(mc); - } else if (n == 7) { - /* - * summation of a series roughly proportional to (1/8)^k. Estimate - * count - * of terms to estimate the precision (drop the favorable additional - * 1/k here): 0.125^k <= 10^(-precision), so k*log10(0.125) <= - * -precision - * so k>= precision/0.903. - */ - final int kmax = (int) (mc.getPrecision() / 0.903); - MathContext mcloc = SafeMathContext.newMathContext(mc.getPrecision() + 1 + (int) Math.log10(kmax * 3 * 0.693 / 1.098)); - BigDecimal log7 = BigDecimalMath.multiplyRound(BigDecimalMath.log(2, mcloc), 3); - - /* - * log7 is roughly 1.9, so absolute and relative error are the same. - */ - final double eps = BigDecimalMath.prec2err(1.9, mc.getPrecision()) / kmax; - final Rational r = new Rational(1, 8); - Rational pk = new Rational(1, 8); - for (int k = 1;; k++) { - final Rational tmp = pk.divide(k); - if (tmp.doubleValue() < eps) { - break; - } - - /* - * how many digits of tmp do we need in the sum? - */ - mcloc = SafeMathContext.newMathContext(BigDecimalMath.err2prec(tmp.doubleValue(), eps)); - final BigDecimal c = pk.divide(k).BigDecimalValue(mcloc); - log7 = log7.subtract(c); - pk = pk.multiply(r); - } - return log7.round(mc); - - } - - else { - /* - * At this point one could either forward to the log(BigDecimal) - * signature (implemented) - * or decompose n into Ifactors and use an implemenation of all the - * prime bases. - * Estimate of the result; convert the mc argument to an absolute - * error eps - * log(n+errn) = log(n)+errn/n = log(n)+eps - */ - final double res = Math.log(n); - double eps = BigDecimalMath.prec2err(res, mc.getPrecision()); - /* - * errn = eps*n, convert absolute error in result to requirement on - * absolute error in input - */ - eps *= n; - /* - * Convert this absolute requirement of error in n to a relative - * error in n - */ - final MathContext mcloc = SafeMathContext.newMathContext(1 + BigDecimalMath.err2prec(n, eps)); - /* - * Padd n with a number of zeros to trigger the required accuracy in - * the standard signature method - */ - final BigDecimal nb = BigDecimalMath.scalePrec(new BigDecimal(n), mcloc); - return BigDecimalMath.log(nb); - } - } /* log */ - - /** - * The natural logarithm. - * - * @param r - * The main argument, a strictly positive value. - * @param mc - * The requirements on the precision. - * @return ln(r). - * @since 2009-08-09 - * @author Richard J. Mathar - */ - static public BigDecimal log(final Rational r, final MathContext mc) { - /* - * the value is undefined if x is negative. - */ - if (r.compareTo(Rational.ZERO) <= 0) { - throw new ArithmeticException("Cannot take log of negative " + r.toString()); - } else if (r.compareTo(Rational.ONE) == 0) { - return BigDecimal.ZERO; - } else { - - /* - * log(r+epsr) = log(r)+epsr/r. Convert the precision to an absolute - * error in the result. - * eps contains the required absolute error of the result, epsr/r. - */ - final double eps = BigDecimalMath.prec2err(Math.log(r.doubleValue()), mc.getPrecision()); - - /* - * Convert this further into a requirement of the relative precision - * in r, given that - * epsr/r is also the relative precision of r. Add one safety digit. - */ - final MathContext mcloc = SafeMathContext.newMathContext(1 + BigDecimalMath.err2prec(eps)); - - final BigDecimal resul = BigDecimalMath.log(r.BigDecimalValue(mcloc)); - - return resul.round(mc); - } - } /* log */ - - /** - * Power function. - * - * @param x - * Base of the power. - * @param y - * Exponent of the power. - * @return x^y. - * The estimation of the relative error in the result is - * |log(x)*err(y)|+|y*err(x)/x| - * @since 2009-06-01 - */ - static public BigDecimal pow(final BigDecimal x, final BigDecimal y) { - if (x.compareTo(BigDecimal.ZERO) < 0) { - throw new ArithmeticException("Cannot power negative " + x.toString()); - } else if (x.compareTo(BigDecimal.ZERO) == 0) { - return BigDecimal.ZERO; - } else { - /* - * return x^y = exp(y*log(x)) ; - */ - final BigDecimal logx = BigDecimalMath.log(x); - final BigDecimal ylogx = y.multiply(logx); - final BigDecimal resul = BigDecimalMath.exp(ylogx); - - /* - * The estimation of the relative error in the result is - * |log(x)*err(y)|+|y*err(x)/x| - */ - final double errR = Math.abs(logx.doubleValue() * y.ulp().doubleValue() / 2.) + Math.abs(y.doubleValue() * x.ulp().doubleValue() / 2. / x.doubleValue()); - final MathContext mcR = SafeMathContext.newMathContext(BigDecimalMath.err2prec(1.0, errR)); - return resul.round(mcR); - } - } /* BigDecimalMath.pow */ - - /** - * Raise to an integer power and round. - * - * @param x - * The base. - * @param n - * The exponent. - * @return x^n. - * @since 2009-08-13 - * @since 2010-05-26 handle also n<0 cases. - */ - static public BigDecimal powRound(final BigDecimal x, final int n) { - /** - * Special cases: x^1=x and x^0 = 1 - */ - if (n == 1) { - return x; - } else if (n == 0) { - return BigDecimal.ONE; - } else { - /* - * The relative error in the result is n times the relative error in - * the input. - * The estimation is slightly optimistic due to the integer rounding - * of the logarithm. - * Since the standard BigDecimal.pow can only handle positive n, we - * split the algorithm. - */ - final MathContext mc = SafeMathContext.newMathContext(x.precision() - (int) Math.log10(Math.abs(n))); - if (n > 0) { - return x.pow(n, mc); - } else { - return BigDecimal.ONE.divide(x.pow(-n), mc); - } - } - } /* BigDecimalMath.powRound */ - - /** - * Raise to an integer power and round. - * - * @param x - * The base. - * @param n - * The exponent. - * The current implementation allows n only in the interval of - * the standard int values. - * @return x^n. - * @since 2010-05-26 - */ - static public BigDecimal powRound(final BigDecimal x, final BigInteger n) { - /** - * For now, the implementation forwards to the cases where n - * is in the range of the standard integers. This might, however, be - * implemented to decompose larger powers into cascaded calls to smaller - * ones. - */ - if (n.compareTo(Rational.MAX_INT) > 0 || n.compareTo(Rational.MIN_INT) < 0) { - throw new ProviderException("Not implemented: big power " + n.toString()); - } else { - return BigDecimalMath.powRound(x, n.intValue()); - } - } /* BigDecimalMath.powRound */ - - /** - * Raise to a fractional power and round. - * - * @param x - * The base. - * Generally enforced to be positive, with the exception of - * integer exponents where - * the sign is carried over according to the parity of the - * exponent. - * @param q - * The exponent. - * @return x^q. - * @since 2010-05-26 - */ - static public BigDecimal powRound(final BigDecimal x, final Rational q) { - /** - * Special cases: x^1=x and x^0 = 1 - */ - if (q.compareTo(BigInteger.ONE) == 0) { - return x; - } else if (q.signum() == 0) { - return BigDecimal.ONE; - } else if (q.isInteger()) { - /* - * We are sure that the denominator is positive here, because - * normalize() has been - * called during constrution etc. - */ - return BigDecimalMath.powRound(x, q.a); - } else if (x.compareTo(BigDecimal.ZERO) < 0) { - throw new ArithmeticException("Cannot power negative " + x.toString()); - } else if (q.isIntegerFrac()) { - /* - * Newton method with first estimate in double precision. - * The disadvantage of this first line here is that the result - * must fit in the - * standard range of double precision numbers exponents. - */ - final double estim = Math.pow(x.doubleValue(), q.doubleValue()); - BigDecimal res = new BigDecimal(estim); - - /* - * The error in x^q is q*x^(q-1)*Delta(x). - * The relative error is q*Delta(x)/x, q times the relative - * error of x. - */ - final BigDecimal reserr = new BigDecimal(0.5 * q.abs().doubleValue() * x.ulp().divide(x.abs(), MathContext.DECIMAL64).doubleValue()); - - /* - * The main point in branching the cases above is that this - * conversion - * will succeed for numerator and denominator of q. - */ - final int qa = q.a.intValue(); - final int qb = q.b.intValue(); - - /* Newton iterations. */ - final BigDecimal xpowa = BigDecimalMath.powRound(x, qa); - for (;;) { - /* - * numerator and denominator of the Newton term. The major - * disadvantage of this implementation is that the updates - * of the powers - * of the new estimate are done in full precision calling - * BigDecimal.pow(), - * which becomes slow if the denominator of q is large. - */ - final BigDecimal nu = res.pow(qb).subtract(xpowa); - final BigDecimal de = BigDecimalMath.multiplyRound(res.pow(qb - 1), q.b); - - /* estimated correction */ - BigDecimal eps = nu.divide(de, MathContext.DECIMAL64); - - final BigDecimal err = res.multiply(reserr, MathContext.DECIMAL64); - final int precDiv = 2 + BigDecimalMath.err2prec(eps, err); - if (precDiv <= 0) { - /* - * The case when the precision is already reached and - * any precision - * will do. - */ - eps = nu.divide(de, MathContext.DECIMAL32); - } else { - final MathContext mc = SafeMathContext.newMathContext(precDiv); - eps = nu.divide(de, mc); - } - - res = BigDecimalMath.subtractRound(res, eps); - /* - * reached final precision if the relative error fell below - * reserr, - * |eps/res| < reserr - */ - if (eps.divide(res, MathContext.DECIMAL64).abs().compareTo(reserr) < 0) { - /* - * delete the bits of extra precision kept in this - * working copy. - */ - final MathContext mc = SafeMathContext.newMathContext(BigDecimalMath.err2prec(reserr.doubleValue())); - return res.round(mc); - } - } - } else { - /* - * The error in x^q is q*x^(q-1)*Delta(x) + Delta(q)*x^q*log(x). - * The relative error is q/x*Delta(x) + Delta(q)*log(x). Convert - * q to a floating point - * number such that its relative error becomes negligible: - * Delta(q)/q << Delta(x)/x/log(x) . - */ - final int precq = 3 + BigDecimalMath.err2prec(x.ulp().divide(x, MathContext.DECIMAL64).doubleValue() / Math.log(x.doubleValue())); - final MathContext mc = SafeMathContext.newMathContext(precq); - - /* - * Perform the actual calculation as exponentiation of two - * floating point numbers. - */ - return BigDecimalMath.pow(x, q.BigDecimalValue(mc)); - } - } /* BigDecimalMath.powRound */ - - /** - * Trigonometric sine. - * - * @param x - * The argument in radians. - * @return sin(x) in the range -1 to 1. - * @throws Error - * @since 2009-06-01 - */ - static public BigDecimal sin(final BigDecimal x) throws Error { - if (x.compareTo(BigDecimal.ZERO) < 0) { - return BigDecimalMath.sin(x.negate()).negate(); - } else if (x.compareTo(BigDecimal.ZERO) == 0) { - return BigDecimal.ZERO; - } else { - /* - * reduce modulo 2pi - */ - final BigDecimal res = BigDecimalMath.mod2pi(x); - final double errpi = 0.5 * Math.abs(x.ulp().doubleValue()); - MathContext mc = SafeMathContext.newMathContext(2 + BigDecimalMath.err2prec(3.14159, errpi)); - final BigDecimal p = BigDecimalMath.pi(mc); - mc = SafeMathContext.newMathContext(x.precision()); - if (res.compareTo(p) > 0) { - /* - * pi 0) { - /* - * pi/2 0) { - /* - * x>pi/4: sin(x) = cos(pi/2-x) - */ - return BigDecimalMath.cos(BigDecimalMath.subtractRound(p.divide(new BigDecimal("2")), res)); - } else { - /* - * Simple Taylor expansion, sum_{i=1..infinity} - * (-1)^(..)res^(2i+1)/(2i+1)! - */ - BigDecimal resul = res; - - /* x^i */ - BigDecimal xpowi = res; - - /* 2i+1 factorial */ - BigInteger ifac = BigInteger.ONE; - - /* - * The error in the result is set by the error in x itself. - */ - final double xUlpDbl = res.ulp().doubleValue(); - - /* - * The error in the result is set by the error in x itself. - * We need at most k terms to squeeze x^(2k+1)/(2k+1)! below - * this value. - * x^(2k+1) < x.ulp; (2k+1)*log10(x) < -x.precision; - * 2k*log10(x)< -x.precision; - * 2k*(-log10(x)) > x.precision; 2k*log10(1/x) > x.precision - */ - final int k = (int) (res.precision() / Math.log10(1.0 / res.doubleValue())) / 2; - final MathContext mcTay = SafeMathContext.newMathContext(BigDecimalMath.err2prec(res.doubleValue(), xUlpDbl / k)); - for (int i = 1;; i++) { - /* - * TBD: at which precision will 2*i or 2*i+1 overflow? - */ - ifac = ifac.multiply(new BigInteger("" + 2 * i)); - ifac = ifac.multiply(new BigInteger("" + (2 * i + 1))); - xpowi = xpowi.multiply(res).multiply(res).negate(); - final BigDecimal corr = xpowi.divide(new BigDecimal(ifac), mcTay); - resul = resul.add(corr); - if (corr.abs().doubleValue() < 0.5 * xUlpDbl) { - break; - } - } - /* - * The error in the result is set by the error in x itself. - */ - mc = SafeMathContext.newMathContext(res.precision()); - return resul.round(mc); - } - } - } /* sin */ - - /** - * Trigonometric cosine. - * - * @param x - * The argument in radians. - * @return cos(x) in the range -1 to 1. - * @throws Error - * @since 2009-06-01 - */ - static public BigDecimal cos(final BigDecimal x) throws Error { - if (x.compareTo(BigDecimal.ZERO) < 0) { - return BigDecimalMath.cos(x.negate()); - } else if (x.compareTo(BigDecimal.ZERO) == 0) { - return BigDecimal.ONE; - } else { - /* - * reduce modulo 2pi - */ - final BigDecimal res = BigDecimalMath.mod2pi(x); - final double errpi = 0.5 * Math.abs(x.ulp().doubleValue()); - MathContext mc = SafeMathContext.newMathContext(2 + BigDecimalMath.err2prec(3.14159, errpi)); - final BigDecimal p = BigDecimalMath.pi(mc); - mc = SafeMathContext.newMathContext(x.precision()); - if (res.compareTo(p) > 0) { - /* - * pi 0) { - /* - * pi/2 0) { - /* - * x>pi/4: cos(x) = sin(pi/2-x) - */ - return BigDecimalMath.sin(BigDecimalMath.subtractRound(p.divide(new BigDecimal("2")), res)); - } else { - /* - * Simple Taylor expansion, sum_{i=0..infinity} - * (-1)^(..)res^(2i)/(2i)! - */ - BigDecimal resul = BigDecimal.ONE; - - /* x^i */ - BigDecimal xpowi = BigDecimal.ONE; - - /* 2i factorial */ - BigInteger ifac = BigInteger.ONE; - - /* - * The absolute error in the result is the error in x^2/2 - * which is x times the error in x. - */ - final double xUlpDbl = 0.5 * res.ulp().doubleValue() * res.doubleValue(); - - /* - * The error in the result is set by the error in x^2/2 - * itself, xUlpDbl. - * We need at most k terms to push x^(2k+1)/(2k+1)! below - * this value. - * x^(2k) < xUlpDbl; (2k)*log(x) < log(xUlpDbl); - */ - final int k = (int) (Math.log(xUlpDbl) / Math.log(res.doubleValue())) / 2; - final MathContext mcTay = SafeMathContext.newMathContext(BigDecimalMath.err2prec(1., xUlpDbl / k)); - for (int i = 1;; i++) { - /* - * TBD: at which precision will 2*i-1 or 2*i overflow? - */ - ifac = ifac.multiply(new BigInteger("" + (2 * i - 1))); - ifac = ifac.multiply(new BigInteger("" + 2 * i)); - xpowi = xpowi.multiply(res).multiply(res).negate(); - final BigDecimal corr = xpowi.divide(new BigDecimal(ifac), mcTay); - resul = resul.add(corr); - if (corr.abs().doubleValue() < 0.5 * xUlpDbl) { - break; - } - } - /* - * The error in the result is governed by the error in x - * itself. - */ - mc = SafeMathContext.newMathContext(BigDecimalMath.err2prec(resul.doubleValue(), xUlpDbl)); - return resul.round(mc); - } - } - } /* BigDecimalMath.cos */ - - /** - * The trigonometric tangent. - * - * @param x - * the argument in radians. - * @return the tan(x) - * @throws Error - */ - static public BigDecimal tan(final BigDecimal x) throws Error { - if (x.compareTo(BigDecimal.ZERO) == 0) { - return BigDecimal.ZERO; - } else if (x.compareTo(BigDecimal.ZERO) < 0) { - return BigDecimalMath.tan(x.negate()).negate(); - } else { - /* - * reduce modulo pi - */ - final BigDecimal res = BigDecimalMath.modpi(x); - - /* - * absolute error in the result is err(x)/cos^2(x) to lowest order - */ - final double xDbl = res.doubleValue(); - final double xUlpDbl = x.ulp().doubleValue() / 2.; - final double eps = xUlpDbl / 2. / Math.pow(Math.cos(xDbl), 2.); - - if (xDbl > 0.8) { - /* tan(x) = 1/cot(x) */ - final BigDecimal co = BigDecimalMath.cot(x); - final MathContext mc = SafeMathContext.newMathContext(BigDecimalMath.err2prec(1. / co.doubleValue(), eps)); - return BigDecimal.ONE.divide(co, mc); - } else { - final BigDecimal xhighpr = BigDecimalMath.scalePrec(res, 2); - final BigDecimal xhighprSq = BigDecimalMath.multiplyRound(xhighpr, xhighpr); - - BigDecimal resul = xhighpr.plus(); - - /* x^(2i+1) */ - BigDecimal xpowi = xhighpr; - - final Bernoulli b = new Bernoulli(); - - /* 2^(2i) */ - BigInteger fourn = new BigInteger("4"); - /* (2i)! */ - BigInteger fac = new BigInteger("2"); - - for (int i = 2;; i++) { - Rational f = b.at(2 * i).abs(); - fourn = fourn.shiftLeft(2); - fac = fac.multiply(new BigInteger("" + 2 * i)).multiply(new BigInteger("" + (2 * i - 1))); - f = f.multiply(fourn).multiply(fourn.subtract(BigInteger.ONE)).divide(fac); - xpowi = BigDecimalMath.multiplyRound(xpowi, xhighprSq); - final BigDecimal c = BigDecimalMath.multiplyRound(xpowi, f); - resul = resul.add(c); - if (Math.abs(c.doubleValue()) < 0.1 * eps) { - break; - } - } - final MathContext mc = SafeMathContext.newMathContext(BigDecimalMath.err2prec(resul.doubleValue(), eps)); - return resul.round(mc); - } - } - } /* BigDecimalMath.tan */ - - /** - * The trigonometric co-tangent. - * - * @param x - * the argument in radians. - * @return the cot(x) - * @throws Error - * @since 2009-07-31 - */ - static public BigDecimal cot(final BigDecimal x) throws Error { - if (x.compareTo(BigDecimal.ZERO) == 0) { - throw new ArithmeticException("Cannot take cot of zero " + x.toString()); - } else if (x.compareTo(BigDecimal.ZERO) < 0) { - return BigDecimalMath.cot(x.negate()).negate(); - } else { - /* - * reduce modulo pi - */ - final BigDecimal res = BigDecimalMath.modpi(x); - - /* - * absolute error in the result is err(x)/sin^2(x) to lowest order - */ - final double xDbl = res.doubleValue(); - final double xUlpDbl = x.ulp().doubleValue() / 2.; - final double eps = xUlpDbl / 2. / Math.pow(Math.sin(xDbl), 2.); - - final BigDecimal xhighpr = BigDecimalMath.scalePrec(res, 2); - final BigDecimal xhighprSq = BigDecimalMath.multiplyRound(xhighpr, xhighpr); - - MathContext mc = SafeMathContext.newMathContext(BigDecimalMath.err2prec(xhighpr.doubleValue(), eps)); - BigDecimal resul = BigDecimal.ONE.divide(xhighpr, mc); - - /* x^(2i-1) */ - BigDecimal xpowi = xhighpr; - - final Bernoulli b = new Bernoulli(); - - /* 2^(2i) */ - BigInteger fourn = new BigInteger("4"); - /* (2i)! */ - BigInteger fac = BigInteger.ONE; - - for (int i = 1;; i++) { - Rational f = b.at(2 * i); - fac = fac.multiply(new BigInteger("" + 2 * i)).multiply(new BigInteger("" + (2 * i - 1))); - f = f.multiply(fourn).divide(fac); - final BigDecimal c = BigDecimalMath.multiplyRound(xpowi, f); - if (i % 2 == 0) { - resul = resul.add(c); - } else { - resul = resul.subtract(c); - } - if (Math.abs(c.doubleValue()) < 0.1 * eps) { - break; - } - - fourn = fourn.shiftLeft(2); - xpowi = BigDecimalMath.multiplyRound(xpowi, xhighprSq); - } - mc = SafeMathContext.newMathContext(BigDecimalMath.err2prec(resul.doubleValue(), eps)); - return resul.round(mc); - } - } /* BigDecimalMath.cot */ - - /** - * The inverse trigonometric sine. - * - * @param x - * the argument. - * @return the arcsin(x) in radians. - * @throws Error - */ - static public BigDecimal asin(final BigDecimal x) throws Error { - if (x.compareTo(BigDecimal.ONE) > 0 || x.compareTo(BigDecimal.ONE.negate()) < 0) { - throw new ArithmeticException("Out of range argument " + x.toString() + " of asin"); - } else if (x.compareTo(BigDecimal.ZERO) == 0) { - return BigDecimal.ZERO; - } else if (x.compareTo(BigDecimal.ONE) == 0) { - /* - * arcsin(1) = pi/2 - */ - final double errpi = Math.sqrt(x.ulp().doubleValue()); - final MathContext mc = SafeMathContext.newMathContext(BigDecimalMath.err2prec(3.14159, errpi)); - return BigDecimalMath.pi(mc).divide(new BigDecimal(2)); - } else if (x.compareTo(BigDecimal.ZERO) < 0) { - return BigDecimalMath.asin(x.negate()).negate(); - } else if (x.doubleValue() > 0.7) { - final BigDecimal xCompl = BigDecimal.ONE.subtract(x); - final double xDbl = x.doubleValue(); - final double xUlpDbl = x.ulp().doubleValue() / 2.; - final double eps = xUlpDbl / 2. / Math.sqrt(1. - Math.pow(xDbl, 2.)); - - final BigDecimal xhighpr = BigDecimalMath.scalePrec(xCompl, 3); - final BigDecimal xhighprV = BigDecimalMath.divideRound(xhighpr, 4); - - BigDecimal resul = BigDecimal.ONE; - - /* x^(2i+1) */ - BigDecimal xpowi = BigDecimal.ONE; - - /* i factorial */ - BigInteger ifacN = BigInteger.ONE; - BigInteger ifacD = BigInteger.ONE; - - for (int i = 1;; i++) { - ifacN = ifacN.multiply(new BigInteger("" + (2 * i - 1))); - ifacD = ifacD.multiply(new BigInteger("" + i)); - if (i == 1) { - xpowi = xhighprV; - } else { - xpowi = BigDecimalMath.multiplyRound(xpowi, xhighprV); - } - final BigDecimal c = BigDecimalMath.divideRound(BigDecimalMath.multiplyRound(xpowi, ifacN), ifacD.multiply(new BigInteger("" + (2 * i + 1)))); - resul = resul.add(c); - /* - * series started 1+x/12+... which yields an estimate of the - * sum's error - */ - if (Math.abs(c.doubleValue()) < xUlpDbl / 120.) { - break; - } - } - /* - * sqrt(2*z)*(1+...) - */ - xpowi = BigDecimalMath.sqrt(xhighpr.multiply(new BigDecimal(2))); - resul = BigDecimalMath.multiplyRound(xpowi, resul); - - MathContext mc = SafeMathContext.newMathContext(resul.precision()); - final BigDecimal pihalf = BigDecimalMath.pi(mc).divide(new BigDecimal(2)); - - mc = SafeMathContext.newMathContext(BigDecimalMath.err2prec(resul.doubleValue(), eps)); - return pihalf.subtract(resul, mc); - } else { - /* - * absolute error in the result is err(x)/sqrt(1-x^2) to lowest - * order - */ - final double xDbl = x.doubleValue(); - final double xUlpDbl = x.ulp().doubleValue() / 2.; - final double eps = xUlpDbl / 2. / Math.sqrt(1. - Math.pow(xDbl, 2.)); - - final BigDecimal xhighpr = BigDecimalMath.scalePrec(x, 2); - final BigDecimal xhighprSq = BigDecimalMath.multiplyRound(xhighpr, xhighpr); - - BigDecimal resul = xhighpr.plus(); - - /* x^(2i+1) */ - BigDecimal xpowi = xhighpr; - - /* i factorial */ - BigInteger ifacN = BigInteger.ONE; - BigInteger ifacD = BigInteger.ONE; - - for (int i = 1;; i++) { - ifacN = ifacN.multiply(new BigInteger("" + (2 * i - 1))); - ifacD = ifacD.multiply(new BigInteger("" + 2 * i)); - xpowi = BigDecimalMath.multiplyRound(xpowi, xhighprSq); - final BigDecimal c = BigDecimalMath.divideRound(BigDecimalMath.multiplyRound(xpowi, ifacN), ifacD.multiply(new BigInteger("" + (2 * i + 1)))); - resul = resul.add(c); - if (Math.abs(c.doubleValue()) < 0.1 * eps) { - break; - } - } - final MathContext mc = SafeMathContext.newMathContext(BigDecimalMath.err2prec(resul.doubleValue(), eps)); - return resul.round(mc); - } - } /* BigDecimalMath.asin */ - - /** - * The inverse trigonometric cosine. - * - * @param x - * the argument. - * @return the arccos(x) in radians. - * @throws Error - * @since 2009-09-29 - */ - static public BigDecimal acos(final BigDecimal x) throws Error { - /* - * Essentially forwarded to pi/2 - asin(x) - */ - final BigDecimal xhighpr = BigDecimalMath.scalePrec(x, 2); - BigDecimal resul = BigDecimalMath.asin(xhighpr); - double eps = resul.ulp().doubleValue() / 2.; - - MathContext mc = SafeMathContext.newMathContext(BigDecimalMath.err2prec(3.14159, eps)); - final BigDecimal pihalf = BigDecimalMath.pi(mc).divide(new BigDecimal(2)); - resul = pihalf.subtract(resul); - - /* - * absolute error in the result is err(x)/sqrt(1-x^2) to lowest order - */ - final double xDbl = x.doubleValue(); - final double xUlpDbl = x.ulp().doubleValue() / 2.; - eps = xUlpDbl / 2. / Math.sqrt(1. - Math.pow(xDbl, 2.)); - - mc = SafeMathContext.newMathContext(BigDecimalMath.err2prec(resul.doubleValue(), eps)); - return resul.round(mc); - - } /* BigDecimalMath.acos */ - - /** - * The inverse trigonometric tangent. - * - * @param x - * the argument. - * @return the principal value of arctan(x) in radians in the range -pi/2 to - * +pi/2. - * @throws Error - * @since 2009-08-03 - */ - static public BigDecimal atan(final BigDecimal x) throws Error { - if (x.compareTo(BigDecimal.ZERO) < 0) { - return BigDecimalMath.atan(x.negate()).negate(); - } else if (x.compareTo(BigDecimal.ZERO) == 0) { - return BigDecimal.ZERO; - } else if (x.doubleValue() > 0.7 && x.doubleValue() < 3.0) { - /* - * Abramowitz-Stegun 4.4.34 convergence acceleration - * 2*arctan(x) = arctan(2x/(1-x^2)) = arctan(y). x=(sqrt(1+y^2)-1)/y - * This maps 0<=y<=3 to 0<=x<=0.73 roughly. Temporarily with 2 - * protectionist digits. - */ - final BigDecimal y = BigDecimalMath.scalePrec(x, 2); - final BigDecimal newx = BigDecimalMath.divideRound(BigDecimalMath.hypot(1, y).subtract(BigDecimal.ONE), y); - - /* intermediate result with too optimistic error estimate */ - final BigDecimal resul = BigDecimalMath.multiplyRound(BigDecimalMath.atan(newx), 2); - - /* - * absolute error in the result is errx/(1+x^2), where errx = half - * of the ulp. - */ - final double eps = x.ulp().doubleValue() / (2.0 * Math.hypot(1.0, x.doubleValue())); - final MathContext mc = SafeMathContext.newMathContext(BigDecimalMath.err2prec(resul.doubleValue(), eps)); - return resul.round(mc); - } else if (x.doubleValue() < 0.71) { - /* Taylor expansion around x=0; Abramowitz-Stegun 4.4.42 */ - - final BigDecimal xhighpr = BigDecimalMath.scalePrec(x, 2); - final BigDecimal xhighprSq = BigDecimalMath.multiplyRound(xhighpr, xhighpr).negate(); - - BigDecimal resul = xhighpr.plus(); - - /* signed x^(2i+1) */ - BigDecimal xpowi = xhighpr; - - /* - * absolute error in the result is errx/(1+x^2), where errx = half - * of the ulp. - */ - final double eps = x.ulp().doubleValue() / (2.0 * Math.hypot(1.0, x.doubleValue())); - - for (int i = 1;; i++) { - xpowi = BigDecimalMath.multiplyRound(xpowi, xhighprSq); - final BigDecimal c = BigDecimalMath.divideRound(xpowi, 2 * i + 1); - - resul = resul.add(c); - if (Math.abs(c.doubleValue()) < 0.1 * eps) { - break; - } - } - final MathContext mc = SafeMathContext.newMathContext(BigDecimalMath.err2prec(resul.doubleValue(), eps)); - return resul.round(mc); - } else { - /* Taylor expansion around x=infinity; Abramowitz-Stegun 4.4.42 */ - - /* - * absolute error in the result is errx/(1+x^2), where errx = half - * of the ulp. - */ - final double eps = x.ulp().doubleValue() / (2.0 * Math.hypot(1.0, x.doubleValue())); - - /* - * start with the term pi/2; gather its precision relative to the - * expected result - */ - MathContext mc = SafeMathContext.newMathContext(2 + BigDecimalMath.err2prec(3.1416, eps)); - final BigDecimal onepi = BigDecimalMath.pi(mc); - BigDecimal resul = onepi.divide(new BigDecimal(2)); - - final BigDecimal xhighpr = BigDecimalMath.divideRound(-1, BigDecimalMath.scalePrec(x, 2)); - final BigDecimal xhighprSq = BigDecimalMath.multiplyRound(xhighpr, xhighpr).negate(); - - /* signed x^(2i+1) */ - BigDecimal xpowi = xhighpr; - - for (int i = 0;; i++) { - final BigDecimal c = BigDecimalMath.divideRound(xpowi, 2 * i + 1); - - resul = resul.add(c); - if (Math.abs(c.doubleValue()) < 0.1 * eps) { - break; - } - xpowi = BigDecimalMath.multiplyRound(xpowi, xhighprSq); - } - mc = SafeMathContext.newMathContext(BigDecimalMath.err2prec(resul.doubleValue(), eps)); - return resul.round(mc); - } - } /* BigDecimalMath.atan */ - - /** - * The hyperbolic cosine. - * - * @param x - * The argument. - * @return The cosh(x) = (exp(x)+exp(-x))/2 . - * @author Richard J. Mathar - * @throws Error - * @since 2009-08-19 - */ - static public BigDecimal cosh(final BigDecimal x) throws Error { - if (x.compareTo(BigDecimal.ZERO) < 0) { - return BigDecimalMath.cos(x.negate()); - } else if (x.compareTo(BigDecimal.ZERO) == 0) { - return BigDecimal.ONE; - } else if (x.doubleValue() > 1.5) { - /* - * cosh^2(x) = 1+ sinh^2(x). - */ - return BigDecimalMath.hypot(1, BigDecimalMath.sinh(x)); - } else { - final BigDecimal xhighpr = BigDecimalMath.scalePrec(x, 2); - /* Simple Taylor expansion, sum_{0=1..infinity} x^(2i)/(2i)! */ - BigDecimal resul = BigDecimal.ONE; - - /* x^i */ - BigDecimal xpowi = BigDecimal.ONE; - - /* 2i factorial */ - BigInteger ifac = BigInteger.ONE; - - /* - * The absolute error in the result is the error in x^2/2 which - * is x times the error in x. - */ - final double xUlpDbl = 0.5 * x.ulp().doubleValue() * x.doubleValue(); - - /* - * The error in the result is set by the error in x^2/2 itself, - * xUlpDbl. - * We need at most k terms to push x^(2k)/(2k)! below this - * value. - * x^(2k) < xUlpDbl; (2k)*log(x) < log(xUlpDbl); - */ - final int k = (int) (Math.log(xUlpDbl) / Math.log(x.doubleValue())) / 2; - - /* - * The individual terms are all smaller than 1, so an estimate - * of 1.0 for - * the absolute value will give a safe relative error estimate - * for the indivdual terms - */ - final MathContext mcTay = SafeMathContext.newMathContext(BigDecimalMath.err2prec(1., xUlpDbl / k)); - for (int i = 1;; i++) { - /* - * TBD: at which precision will 2*i-1 or 2*i overflow? - */ - ifac = ifac.multiply(new BigInteger("" + (2 * i - 1))); - ifac = ifac.multiply(new BigInteger("" + 2 * i)); - xpowi = xpowi.multiply(xhighpr).multiply(xhighpr); - final BigDecimal corr = xpowi.divide(new BigDecimal(ifac), mcTay); - resul = resul.add(corr); - if (corr.abs().doubleValue() < 0.5 * xUlpDbl) { - break; - } - } - /* - * The error in the result is governed by the error in x itself. - */ - final MathContext mc = SafeMathContext.newMathContext(BigDecimalMath.err2prec(resul.doubleValue(), xUlpDbl)); - return resul.round(mc); - } - } /* BigDecimalMath.cosh */ - - /** - * The hyperbolic sine. - * - * @param x - * the argument. - * @return the sinh(x) = (exp(x)-exp(-x))/2 . - * @author Richard J. Mathar - * @throws Error - * @since 2009-08-19 - */ - static public BigDecimal sinh(final BigDecimal x) throws Error { - if (x.compareTo(BigDecimal.ZERO) < 0) { - return BigDecimalMath.sinh(x.negate()).negate(); - } else if (x.compareTo(BigDecimal.ZERO) == 0) { - return BigDecimal.ZERO; - } else if (x.doubleValue() > 2.4) { - /* - * Move closer to zero with sinh(2x)= 2*sinh(x)*cosh(x). - */ - final BigDecimal two = new BigDecimal(2); - final BigDecimal xhalf = x.divide(two); - final BigDecimal resul = BigDecimalMath.sinh(xhalf).multiply(BigDecimalMath.cosh(xhalf)).multiply(two); - /* - * The error in the result is set by the error in x itself. - * The first derivative of sinh(x) is cosh(x), so the absolute - * error - * in the result is cosh(x)*errx, and the relative error is - * coth(x)*errx = errx/tanh(x) - */ - final double eps = Math.tanh(x.doubleValue()); - final MathContext mc = SafeMathContext.newMathContext(BigDecimalMath.err2prec(0.5 * x.ulp().doubleValue() / eps)); - return resul.round(mc); - } else { - final BigDecimal xhighpr = BigDecimalMath.scalePrec(x, 2); - /* - * Simple Taylor expansion, sum_{i=0..infinity} x^(2i+1)/(2i+1)! - */ - BigDecimal resul = xhighpr; - - /* x^i */ - BigDecimal xpowi = xhighpr; - - /* 2i+1 factorial */ - BigInteger ifac = BigInteger.ONE; - - /* - * The error in the result is set by the error in x itself. - */ - final double xUlpDbl = x.ulp().doubleValue(); - - /* - * The error in the result is set by the error in x itself. - * We need at most k terms to squeeze x^(2k+1)/(2k+1)! below - * this value. - * x^(2k+1) < x.ulp; (2k+1)*log10(x) < -x.precision; - * 2k*log10(x)< -x.precision; - * 2k*(-log10(x)) > x.precision; 2k*log10(1/x) > x.precision - */ - final int k = (int) (x.precision() / Math.log10(1.0 / xhighpr.doubleValue())) / 2; - final MathContext mcTay = SafeMathContext.newMathContext(BigDecimalMath.err2prec(x.doubleValue(), xUlpDbl / k)); - for (int i = 1;; i++) { - /* - * TBD: at which precision will 2*i or 2*i+1 overflow? - */ - ifac = ifac.multiply(new BigInteger("" + 2 * i)); - ifac = ifac.multiply(new BigInteger("" + (2 * i + 1))); - xpowi = xpowi.multiply(xhighpr).multiply(xhighpr); - final BigDecimal corr = xpowi.divide(new BigDecimal(ifac), mcTay); - resul = resul.add(corr); - if (corr.abs().doubleValue() < 0.5 * xUlpDbl) { - break; - } - } - /* - * The error in the result is set by the error in x itself. - */ - final MathContext mc = SafeMathContext.newMathContext(x.precision()); - return resul.round(mc); - } - } /* BigDecimalMath.sinh */ - - /** - * The hyperbolic tangent. - * - * @param x - * The argument. - * @return The tanh(x) = sinh(x)/cosh(x). - * @author Richard J. Mathar - * @since 2009-08-20 - */ - static public BigDecimal tanh(final BigDecimal x) { - if (x.compareTo(BigDecimal.ZERO) < 0) { - return BigDecimalMath.tanh(x.negate()).negate(); - } else if (x.compareTo(BigDecimal.ZERO) == 0) { - return BigDecimal.ZERO; - } else { - final BigDecimal xhighpr = BigDecimalMath.scalePrec(x, 2); - - /* - * tanh(x) = (1-e^(-2x))/(1+e^(-2x)) . - */ - final BigDecimal exp2x = BigDecimalMath.exp(xhighpr.multiply(new BigDecimal(-2))); - - /* - * The error in tanh x is err(x)/cosh^2(x). - */ - final double eps = 0.5 * x.ulp().doubleValue() / Math.pow(Math.cosh(x.doubleValue()), 2.0); - final MathContext mc = SafeMathContext.newMathContext(BigDecimalMath.err2prec(Math.tanh(x.doubleValue()), eps)); - return BigDecimal.ONE.subtract(exp2x).divide(BigDecimal.ONE.add(exp2x), mc); - } - } /* BigDecimalMath.tanh */ - - /** - * The inverse hyperbolic sine. - * - * @param x - * The argument. - * @return The arcsinh(x) . - * @author Richard J. Mathar - * @since 2009-08-20 - */ - static public BigDecimal asinh(final BigDecimal x) { - if (x.compareTo(BigDecimal.ZERO) == 0) { - return BigDecimal.ZERO; - } else { - final BigDecimal xhighpr = BigDecimalMath.scalePrec(x, 2); - - /* - * arcsinh(x) = log(x+hypot(1,x)) - */ - final BigDecimal logx = BigDecimalMath.log(BigDecimalMath.hypot(1, xhighpr).add(xhighpr)); - - /* - * The absolute error in arcsinh x is err(x)/sqrt(1+x^2) - */ - final double xDbl = x.doubleValue(); - final double eps = 0.5 * x.ulp().doubleValue() / Math.hypot(1., xDbl); - final MathContext mc = SafeMathContext.newMathContext(BigDecimalMath.err2prec(logx.doubleValue(), eps)); - return logx.round(mc); - } - } /* BigDecimalMath.asinh */ - - /** - * The inverse hyperbolic cosine. - * - * @param x - * The argument. - * @return The arccosh(x) . - * @author Richard J. Mathar - * @since 2009-08-20 - */ - static public BigDecimal acosh(final BigDecimal x) { - if (x.compareTo(BigDecimal.ONE) < 0) { - throw new ArithmeticException("Out of range argument cosh " + x.toString()); - } else if (x.compareTo(BigDecimal.ONE) == 0) { - return BigDecimal.ZERO; - } else { - final BigDecimal xhighpr = BigDecimalMath.scalePrec(x, 2); - - /* - * arccosh(x) = log(x+sqrt(x^2-1)) - */ - final BigDecimal logx = BigDecimalMath.log(BigDecimalMath.sqrt(xhighpr.pow(2).subtract(BigDecimal.ONE)).add(xhighpr)); - - /* - * The absolute error in arcsinh x is err(x)/sqrt(x^2-1) - */ - final double xDbl = x.doubleValue(); - final double eps = 0.5 * x.ulp().doubleValue() / Math.sqrt(xDbl * xDbl - 1.); - final MathContext mc = SafeMathContext.newMathContext(BigDecimalMath.err2prec(logx.doubleValue(), eps)); - return logx.round(mc); - } - } /* BigDecimalMath.acosh */ - - /** - * The Gamma function. - * - * @param x - * The argument. - * @return Gamma(x). - * @throws Error - * @since 2009-08-06 - */ - static public BigDecimal Gamma(final BigDecimal x) throws Error { - /* - * reduce to interval near 1.0 with the functional relation, - * Abramowitz-Stegun 6.1.33 - */ - if (x.compareTo(BigDecimal.ZERO) < 0) { - return BigDecimalMath.divideRound(BigDecimalMath.Gamma(x.add(BigDecimal.ONE)), x); - } else if (x.doubleValue() > 1.5) { - /* - * Gamma(x) = Gamma(xmin+n) = Gamma(xmin)*Pochhammer(xmin,n). - */ - final int n = (int) (x.doubleValue() - 0.5); - final BigDecimal xmin1 = x.subtract(new BigDecimal(n)); - return BigDecimalMath.multiplyRound(BigDecimalMath.Gamma(xmin1), BigDecimalMath.pochhammer(xmin1, n)); - } else { - /* - * apply Abramowitz-Stegun 6.1.33 - */ - BigDecimal z = x.subtract(BigDecimal.ONE); - - /* - * add intermediately 2 digits to the partial sum accumulation - */ - z = BigDecimalMath.scalePrec(z, 2); - MathContext mcloc = SafeMathContext.newMathContext(z.precision()); - - /* - * measure of the absolute error is the relative error in the first, - * logarithmic term - */ - double eps = x.ulp().doubleValue() / x.doubleValue(); - - BigDecimal resul = BigDecimalMath.log(BigDecimalMath.scalePrec(x, 2)).negate(); - - if (x.compareTo(BigDecimal.ONE) != 0) { - - final BigDecimal gammCompl = BigDecimal.ONE.subtract(BigDecimalMath.gamma(mcloc)); - resul = resul.add(BigDecimalMath.multiplyRound(z, gammCompl)); - for (int n = 2;; n++) { - /* - * multiplying z^n/n by zeta(n-1) means that the two - * relative errors add. - * so the requirement in the relative error of zeta(n)-1 is - * that this is somewhat - * smaller than the relative error in z^n/n (the absolute - * error of thelatter is the - * absolute error in z) - */ - BigDecimal c = BigDecimalMath.divideRound(z.pow(n, mcloc), n); - MathContext m = SafeMathContext.newMathContext(BigDecimalMath.err2prec(n * z.ulp().doubleValue() / 2. / z.doubleValue())); - c = c.round(m); - - /* - * At larger n, zeta(n)-1 is roughly 1/2^n. The product is - * c/2^n. - * The relative error in c is c.ulp/2/c . The error in the - * product should be small versus eps/10. - * Error from 1/2^n is c*err(sigma-1). - * We need a relative error of zeta-1 of the order of - * c.ulp/50/c. This is an absolute - * error in zeta-1 of c.ulp/50/c/2^n, and also the absolute - * error in zeta, because zeta is - * of the order of 1. - */ - if (eps / 100. / c.doubleValue() < 0.01) { - m = SafeMathContext.newMathContext(BigDecimalMath.err2prec(eps / 100. / c.doubleValue())); - } else { - m = SafeMathContext.newMathContext(2); - } - /* zeta(n) -1 */ - final BigDecimal zetm1 = BigDecimalMath.zeta(n, m).subtract(BigDecimal.ONE); - c = BigDecimalMath.multiplyRound(c, zetm1); - - if (n % 2 == 0) { - resul = resul.add(c); - } else { - resul = resul.subtract(c); - } - - /* - * alternating sum, so truncating as eps is reached suffices - */ - if (Math.abs(c.doubleValue()) < eps) { - break; - } - } - } - - /* - * The relative error in the result is the absolute error in the - * input variable times the digamma (psi) value at that point. - */ - final double zdbl = z.doubleValue(); - eps = BigDecimalMath.psi(zdbl) * x.ulp().doubleValue() / 2.; - mcloc = SafeMathContext.newMathContext(BigDecimalMath.err2prec(eps)); - return BigDecimalMath.exp(resul).round(mcloc); - } - } /* BigDecimalMath.gamma */ - - /** - * The Gamma function. - * - * @param q - * The argument. - * @param mc - * The required accuracy in the result. - * @return Gamma(x). - * @throws Error - * @since 2010-05-26 - */ - static public BigDecimal Gamma(final Rational q, final MathContext mc) throws Error { - if (q.isBigInteger()) { - if (q.compareTo(Rational.ZERO) <= 0) { - throw new ArithmeticException("Gamma at " + q.toString()); - } else { - /* Gamma(n) = (n-1)! */ - final Factorial f = new Factorial(); - final BigInteger g = f.at(q.trunc().intValue() - 1); - return BigDecimalMath.scalePrec(new BigDecimal(g), mc); - } - } else if (q.b.intValue() == 2) { - /* - * half integer cases which are related to sqrt(pi) - */ - final BigDecimal p = BigDecimalMath.sqrt(BigDecimalMath.pi(mc)); - if (q.compareTo(Rational.ZERO) >= 0) { - Rational pro = Rational.ONE; - Rational f = q.subtract(1); - while (f.compareTo(Rational.ZERO) > 0) { - pro = pro.multiply(f); - f = f.subtract(1); - } - return BigDecimalMath.multiplyRound(p, pro); - } else { - Rational pro = Rational.ONE; - Rational f = q; - while (f.compareTo(Rational.ZERO) < 0) { - pro = pro.divide(f); - f = f.add(1); - } - return BigDecimalMath.multiplyRound(p, pro); - } - } else { - /* - * The relative error of the result is psi(x)*Delta(x). Tune - * Delta(x) such - * that this is equivalent to mc: Delta(x) = precision/psi(x). - */ - final double qdbl = q.doubleValue(); - final double deltx = 5. * Math.pow(10., -mc.getPrecision()) / BigDecimalMath.psi(qdbl); - - final MathContext mcx = SafeMathContext.newMathContext(BigDecimalMath.err2prec(qdbl, deltx)); - final BigDecimal x = q.BigDecimalValue(mcx); - - /* forward calculation to the general floating point case */ - return BigDecimalMath.Gamma(x); - } - } /* BigDecimalMath.Gamma */ - - /** - * Pochhammer's function. - * - * @param x - * The main argument. - * @param n - * The non-negative index. - * @return (x)_n = x(x+1)(x+2)*...*(x+n-1). - * @since 2009-08-19 - */ - static public BigDecimal pochhammer(final BigDecimal x, final int n) { - /* - * reduce to interval near 1.0 with the functional relation, - * Abramowitz-Stegun 6.1.33 - */ - if (n < 0) { - throw new ProviderException("Not implemented: pochhammer with negative index " + n); - } else if (n == 0) { - return BigDecimal.ONE; - } else { - /* - * internally two safety digits - */ - final BigDecimal xhighpr = BigDecimalMath.scalePrec(x, 2); - BigDecimal resul = xhighpr; - - final double xUlpDbl = x.ulp().doubleValue(); - final double xDbl = x.doubleValue(); - /* - * relative error of the result is the sum of the relative errors of - * the factors - */ - double eps = 0.5 * xUlpDbl / Math.abs(xDbl); - for (int i = 1; i < n; i++) { - eps += 0.5 * xUlpDbl / Math.abs(xDbl + i); - resul = resul.multiply(xhighpr.add(new BigDecimal(i))); - final MathContext mcloc = SafeMathContext.newMathContext(4 + BigDecimalMath.err2prec(eps)); - resul = resul.round(mcloc); - } - return resul.round(SafeMathContext.newMathContext(BigDecimalMath.err2prec(eps))); - } - } /* BigDecimalMath.pochhammer */ - - /** - * Reduce value to the interval [0,2*Pi]. - * - * @param x - * the original value - * @return the value modulo 2*pi in the interval from 0 to 2*pi. - * @throws Error - * @since 2009-06-01 - */ - static public BigDecimal mod2pi(final BigDecimal x) throws Error { - /* - * write x= 2*pi*k+r with the precision in r defined by the precision of - * x and not - * compromised by the precision of 2*pi, so the ulp of 2*pi*k should - * match the ulp of x. - * First get a guess of k to figure out how many digits of 2*pi are - * needed. - */ - final int k = (int) (0.5 * x.doubleValue() / Math.PI); - - /* - * want to have err(2*pi*k)< err(x)=0.5*x.ulp, so err(pi) = err(x)/(4k) - * with two safety digits - */ - double err2pi; - if (k != 0) { - err2pi = 0.25 * Math.abs(x.ulp().doubleValue() / k); - } else { - err2pi = 0.5 * Math.abs(x.ulp().doubleValue()); - } - MathContext mc = SafeMathContext.newMathContext(2 + BigDecimalMath.err2prec(6.283, err2pi)); - final BigDecimal twopi = BigDecimalMath.pi(mc).multiply(new BigDecimal(2)); - - /* - * Delegate the actual operation to the BigDecimal class, which may - * return - * a negative value of x was negative . - */ - BigDecimal res = x.remainder(twopi); - if (res.compareTo(BigDecimal.ZERO) < 0) { - res = res.add(twopi); - } - - /* - * The actual precision is set by the input value, its absolute value of - * x.ulp()/2. - */ - mc = SafeMathContext.newMathContext(BigDecimalMath.err2prec(res.doubleValue(), x.ulp().doubleValue() / 2.)); - return res.round(mc); - } /* mod2pi */ - - /** - * Reduce value to the interval [-Pi/2,Pi/2]. - * - * @param x - * The original value - * @return The value modulo pi, shifted to the interval from -Pi/2 to Pi/2. - * @throws Error - * @since 2009-07-31 - */ - static public BigDecimal modpi(final BigDecimal x) throws Error { - /* - * write x= pi*k+r with the precision in r defined by the precision of x - * and not - * compromised by the precision of pi, so the ulp of pi*k should match - * the ulp of x. - * First get a guess of k to figure out how many digits of pi are - * needed. - */ - final int k = (int) (x.doubleValue() / Math.PI); - - /* - * want to have err(pi*k)< err(x)=x.ulp/2, so err(pi) = err(x)/(2k) with - * two safety digits - */ - double errpi; - if (k != 0) { - errpi = 0.5 * Math.abs(x.ulp().doubleValue() / k); - } else { - errpi = 0.5 * Math.abs(x.ulp().doubleValue()); - } - MathContext mc = SafeMathContext.newMathContext(2 + BigDecimalMath.err2prec(3.1416, errpi)); - final BigDecimal onepi = BigDecimalMath.pi(mc); - final BigDecimal pihalf = onepi.divide(new BigDecimal(2)); - - /* - * Delegate the actual operation to the BigDecimal class, which may - * return - * a negative value of x was negative . - */ - BigDecimal res = x.remainder(onepi); - if (res.compareTo(pihalf) > 0) { - res = res.subtract(onepi); - } else if (res.compareTo(pihalf.negate()) < 0) { - res = res.add(onepi); - } - - /* - * The actual precision is set by the input value, its absolute value of - * x.ulp()/2. - */ - mc = SafeMathContext.newMathContext(BigDecimalMath.err2prec(res.doubleValue(), x.ulp().doubleValue() / 2.)); - return res.round(mc); - } /* modpi */ - - /** - * Riemann zeta function. - * - * @param n - * The positive integer argument. - * @param mc - * Specification of the accuracy of the result. - * @return zeta(n). - * @throws Error - * @since 2009-08-05 - */ - static public BigDecimal zeta(final int n, final MathContext mc) throws Error { - if (n <= 0) { - throw new ProviderException("Not implemented: zeta at negative argument " + n); - } - if (n == 1) { - throw new ArithmeticException("Pole at zeta(1) "); - } - - if (n % 2 == 0) { - /* - * Even indices. Abramowitz-Stegun 23.2.16. Start with - * 2^(n-1)*B(n)/n! - */ - Rational b = new Bernoulli().at(n).abs(); - b = b.divide(new Factorial().at(n)); - b = b.multiply(BigInteger.ONE.shiftLeft(n - 1)); - - /* - * to be multiplied by pi^n. Absolute error in the result of pi^n is - * n times - * error in pi times pi^(n-1). Relative error is n*error(pi)/pi, - * requested by mc. - * Need one more digit in pi if n=10, two digits if n=100 etc, and - * add one extra digit. - */ - final MathContext mcpi = SafeMathContext.newMathContext(mc.getPrecision() + (int) Math.log10(10.0 * n)); - final BigDecimal piton = BigDecimalMath.pi(mcpi).pow(n, mc); - return BigDecimalMath.multiplyRound(piton, b); - } else if (n == 3) { - /* - * Broadhurst BBP arXiv:math/9803067 - * Error propagation: S31 is roughly 0.087, S33 roughly 0.131 - */ - final int[] a31 = { 1, -7, -1, 10, -1, -7, 1, 0 }; - final int[] a33 = { 1, 1, -1, -2, -1, 1, 1, 0 }; - BigDecimal S31 = BigDecimalMath.broadhurstBBP(3, 1, a31, mc); - BigDecimal S33 = BigDecimalMath.broadhurstBBP(3, 3, a33, mc); - S31 = S31.multiply(new BigDecimal(48)); - S33 = S33.multiply(new BigDecimal(32)); - return S31.add(S33).divide(new BigDecimal(7), mc); - } else if (n == 5) { - /* - * Broadhurst BBP arXiv:math/9803067 - * Error propagation: S51 is roughly -11.15, S53 roughly 22.165, S55 - * is roughly 0.031 - * 9*2048*S51/6265 = -3.28. 7*2038*S53/61651= 5.07. - * 738*2048*S55/61651= 0.747. - * The result is of the order 1.03, so we add 2 digits to S51 and - * S52 and one digit to S55. - */ - final int[] a51 = { 31, -1614, -31, -6212, -31, -1614, 31, 74552 }; - final int[] a53 = { 173, 284, -173, -457, -173, 284, 173, -111 }; - final int[] a55 = { 1, 0, -1, -1, -1, 0, 1, 1 }; - BigDecimal S51 = BigDecimalMath.broadhurstBBP(5, 1, a51, SafeMathContext.newMathContext(2 + mc.getPrecision())); - BigDecimal S53 = BigDecimalMath.broadhurstBBP(5, 3, a53, SafeMathContext.newMathContext(2 + mc.getPrecision())); - BigDecimal S55 = BigDecimalMath.broadhurstBBP(5, 5, a55, SafeMathContext.newMathContext(1 + mc.getPrecision())); - S51 = S51.multiply(new BigDecimal(18432)); - S53 = S53.multiply(new BigDecimal(14336)); - S55 = S55.multiply(new BigDecimal(1511424)); - return S51.add(S53).subtract(S55).divide(new BigDecimal(62651), mc); - } else { - /* - * Cohen et al Exp Math 1 (1) (1992) 25 - */ - Rational betsum = new Rational(); - final Bernoulli bern = new Bernoulli(); - final Factorial fact = new Factorial(); - for (int npr = 0; npr <= (n + 1) / 2; npr++) { - Rational b = bern.at(2 * npr).multiply(bern.at(n + 1 - 2 * npr)); - b = b.divide(fact.at(2 * npr)).divide(fact.at(n + 1 - 2 * npr)); - b = b.multiply(1 - 2 * npr); - if (npr % 2 == 0) { - betsum = betsum.add(b); - } else { - betsum = betsum.subtract(b); - } - } - betsum = betsum.divide(n - 1); - /* - * The first term, including the facor (2pi)^n, is essentially most - * of the result, near one. The second term below is roughly in the - * range 0.003 to 0.009. - * So the precision here is matching the precisionn requested by mc, - * and the precision - * requested for 2*pi is in absolute terms adjusted. - */ - final MathContext mcloc = SafeMathContext.newMathContext(2 + mc.getPrecision() + (int) Math.log10(n)); - BigDecimal ftrm = BigDecimalMath.pi(mcloc).multiply(new BigDecimal(2)); - ftrm = ftrm.pow(n); - ftrm = BigDecimalMath.multiplyRound(ftrm, betsum.BigDecimalValue(mcloc)); - BigDecimal exps = new BigDecimal(0); - - /* - * the basic accuracy of the accumulated terms before multiplication - * with 2 - */ - double eps = Math.pow(10., -mc.getPrecision()); - - if (n % 4 == 3) { - /* - * since the argument n is at least 7 here, the drop - * of the terms is at rather constant pace at least 10^-3, for - * example - * 0.0018, 0.2e-7, 0.29e-11, 0.74e-15 etc for npr=1,2,3.... We - * want 2 times these terms - * fall below eps/10. - */ - final int kmax = mc.getPrecision() / 3; - eps /= kmax; - /* - * need an error of eps for 2/(exp(2pi)-1) = 0.0037 - * The absolute error is - * 4*exp(2pi)*err(pi)/(exp(2pi)-1)^2=0.0075*err(pi) - */ - BigDecimal exp2p = BigDecimalMath.pi(SafeMathContext.newMathContext(3 + BigDecimalMath.err2prec(3.14, eps / 0.0075))); - exp2p = BigDecimalMath.exp(exp2p.multiply(new BigDecimal(2))); - BigDecimal c = exp2p.subtract(BigDecimal.ONE); - exps = BigDecimalMath.divideRound(1, c); - for (int npr = 2; npr <= kmax; npr++) { - /* - * the error estimate above for npr=1 is the worst case of - * the absolute error created by an error in 2pi. So we can - * safely re-use the exp2p value computed above without - * reassessment of its error. - */ - c = BigDecimalMath.powRound(exp2p, npr).subtract(BigDecimal.ONE); - c = BigDecimalMath.multiplyRound(c, new BigInteger("" + npr).pow(n)); - c = BigDecimalMath.divideRound(1, c); - exps = exps.add(c); - } - } else { - /* - * since the argument n is at least 9 here, the drop - * of the terms is at rather constant pace at least 10^-3, for - * example - * 0.0096, 0.5e-7, 0.3e-11, 0.6e-15 etc. We want these terms - * fall below eps/10. - */ - final int kmax = (1 + mc.getPrecision()) / 3; - eps /= kmax; - /* - * need an error of eps for - * 2/(exp(2pi)-1)*(1+4*Pi/8/(1-exp(-2pi)) = 0.0096 - * at k=7 or = 0.00766 at k=13 for example. - * The absolute error is 0.017*err(pi) at k=9, 0.013*err(pi) at - * k=13, 0.012 at k=17 - */ - BigDecimal twop = BigDecimalMath.pi(SafeMathContext.newMathContext(3 + BigDecimalMath.err2prec(3.14, eps / 0.017))); - twop = twop.multiply(new BigDecimal(2)); - final BigDecimal exp2p = BigDecimalMath.exp(twop); - BigDecimal c = exp2p.subtract(BigDecimal.ONE); - exps = BigDecimalMath.divideRound(1, c); - c = BigDecimal.ONE.subtract(BigDecimalMath.divideRound(1, exp2p)); - c = BigDecimalMath.divideRound(twop, c).multiply(new BigDecimal(2)); - c = BigDecimalMath.divideRound(c, n - 1).add(BigDecimal.ONE); - exps = BigDecimalMath.multiplyRound(exps, c); - for (int npr = 2; npr <= kmax; npr++) { - c = BigDecimalMath.powRound(exp2p, npr).subtract(BigDecimal.ONE); - c = BigDecimalMath.multiplyRound(c, new BigInteger("" + npr).pow(n)); - - BigDecimal d = BigDecimalMath.divideRound(1, exp2p.pow(npr)); - d = BigDecimal.ONE.subtract(d); - d = BigDecimalMath.divideRound(twop, d).multiply(new BigDecimal(2 * npr)); - d = BigDecimalMath.divideRound(d, n - 1).add(BigDecimal.ONE); - - d = BigDecimalMath.divideRound(d, c); - - exps = exps.add(d); - } - } - exps = exps.multiply(new BigDecimal(2)); - return ftrm.subtract(exps, mc); - } - } /* zeta */ - - /** - * Riemann zeta function. - * - * @param n - * The positive integer argument. - * @return zeta(n)-1. - * @throws Error - * @since 2009-08-20 - */ - static public double zeta1(final int n) throws Error { - /* - * precomputed static table in double precision - */ - final double[] zmin1 = { 0., 0., 6.449340668482264364724151666e-01, 2.020569031595942853997381615e-01, 8.232323371113819151600369654e-02, 3.692775514336992633136548646e-02, 1.734306198444913971451792979e-02, 8.349277381922826839797549850e-03, 4.077356197944339378685238509e-03, 2.008392826082214417852769232e-03, 9.945751278180853371459589003e-04, 4.941886041194645587022825265e-04, 2.460865533080482986379980477e-04, 1.227133475784891467518365264e-04, 6.124813505870482925854510514e-05, 3.058823630702049355172851064e-05, 1.528225940865187173257148764e-05, 7.637197637899762273600293563e-06, 3.817293264999839856461644622e-06, 1.908212716553938925656957795e-06, 9.539620338727961131520386834e-07, 4.769329867878064631167196044e-07, 2.384505027277329900036481868e-07, 1.192199259653110730677887189e-07, 5.960818905125947961244020794e-08, 2.980350351465228018606370507e-08, 1.490155482836504123465850663e-08, 7.450711789835429491981004171e-09, 3.725334024788457054819204018e-09, 1.862659723513049006403909945e-09, 9.313274324196681828717647350e-10, 4.656629065033784072989233251e-10, 2.328311833676505492001455976e-10, 1.164155017270051977592973835e-10, 5.820772087902700889243685989e-11, 2.910385044497099686929425228e-11, 1.455192189104198423592963225e-11, 7.275959835057481014520869012e-12, 3.637979547378651190237236356e-12, 1.818989650307065947584832101e-12, 9.094947840263889282533118387e-13, 4.547473783042154026799112029e-13, 2.273736845824652515226821578e-13, 1.136868407680227849349104838e-13, 5.684341987627585609277182968e-14, 2.842170976889301855455073705e-14, 1.421085482803160676983430714e-14, 7.105427395210852712877354480e-15, 3.552713691337113673298469534e-15, 1.776356843579120327473349014e-15, 8.881784210930815903096091386e-16, 4.440892103143813364197770940e-16, 2.220446050798041983999320094e-16, 1.110223025141066133720544570e-16, 5.551115124845481243723736590e-17, 2.775557562136124172581632454e-17, 1.387778780972523276283909491e-17, 6.938893904544153697446085326e-18, 3.469446952165922624744271496e-18, 1.734723476047576572048972970e-18, 8.673617380119933728342055067e-19, 4.336808690020650487497023566e-19, 2.168404344997219785013910168e-19, 1.084202172494241406301271117e-19, 5.421010862456645410918700404e-20, 2.710505431223468831954621312e-20, 1.355252715610116458148523400e-20, 6.776263578045189097995298742e-21, 3.388131789020796818085703100e-21, 1.694065894509799165406492747e-21, 8.470329472546998348246992609e-22, 4.235164736272833347862270483e-22, 2.117582368136194731844209440e-22, 1.058791184068023385226500154e-22, 5.293955920339870323813912303e-23, 2.646977960169852961134116684e-23, 1.323488980084899080309451025e-23, 6.617444900424404067355245332e-24, 3.308722450212171588946956384e-24, 1.654361225106075646229923677e-24, 8.271806125530344403671105617e-25, 4.135903062765160926009382456e-25, 2.067951531382576704395967919e-25, 1.033975765691287099328409559e-25, 5.169878828456431320410133217e-26, 2.584939414228214268127761771e-26, 1.292469707114106670038112612e-26, 6.462348535570531803438002161e-27, 3.231174267785265386134814118e-27, 1.615587133892632521206011406e-27, 8.077935669463162033158738186e-28, 4.038967834731580825622262813e-28, 2.019483917365790349158762647e-28, 1.009741958682895153361925070e-28, 5.048709793414475696084771173e-29, 2.524354896707237824467434194e-29, 1.262177448353618904375399966e-29, 6.310887241768094495682609390e-30, 3.155443620884047239109841220e-30, 1.577721810442023616644432780e-30, 7.888609052210118073520537800e-31 }; - if (n <= 0) { - throw new ProviderException("Not implemented: zeta at negative argument " + n); - } - if (n == 1) { - throw new ArithmeticException("Pole at zeta(1) "); - } - - if (n < zmin1.length) { - /* look it up if available */ - return zmin1[n]; - } else { - /* - * Result is roughly 2^(-n), desired accuracy 18 digits. If zeta(n) - * is computed, the equivalent accuracy - * in relative units is higher, because zeta is around 1. - */ - final double eps = 1.e-18 * Math.pow(2., -n); - final MathContext mc = SafeMathContext.newMathContext(BigDecimalMath.err2prec(eps)); - return BigDecimalMath.zeta(n, mc).subtract(BigDecimal.ONE).doubleValue(); - } - } /* zeta */ - - /** - * trigonometric cot. - * - * @param x - * The argument. - * @return cot(x) = 1/tan(x). - */ - static public double cot(final double x) { - return 1. / Math.tan(x); - } - - /** - * Digamma function. - * - * @param x - * The main argument. - * @return psi(x). - * The error is sometimes up to 10 ulp, where AS 6.3.15 suffers from - * cancellation of digits and psi=0 - * @throws Error - * @since 2009-08-26 - */ - static public double psi(final double x) throws Error { - /* - * the single positive zero of psi(x) - */ - final double psi0 = 1.46163214496836234126265954232572132846819; - if (x > 2.0) { - /* - * Reduce to a value near x=1 with the standard recurrence formula. - * Abramowitz-Stegun 6.3.5 - */ - final int m = (int) (x - 0.5); - final double xmin1 = x - m; - double resul = 0.; - for (int i = 1; i <= m; i++) { - resul += 1. / (x - i); - } - return resul + BigDecimalMath.psi(xmin1); - } else if (Math.abs(x - psi0) < 0.55) { - /* - * Taylor approximation around the local zero - */ - final double[] psiT0 = { 9.67672245447621170427e-01, -4.42763168983592106093e-01, 2.58499760955651010624e-01, -1.63942705442406527504e-01, 1.07824050691262365757e-01, -7.21995612564547109261e-02, 4.88042881641431072251e-02, -3.31611264748473592923e-02, 2.25976482322181046596e-02, -1.54247659049489591388e-02, 1.05387916166121753881e-02, -7.20453438635686824097e-03, 4.92678139572985344635e-03, -3.36980165543932808279e-03, 2.30512632673492783694e-03, -1.57693677143019725927e-03, 1.07882520191629658069e-03, -7.38070938996005129566e-04, 5.04953265834602035177e-04, -3.45468025106307699556e-04, 2.36356015640270527924e-04, -1.61706220919748034494e-04, 1.10633727687474109041e-04, -7.56917958219506591924e-05, 5.17857579522208086899e-05, -3.54300709476596063157e-05, 2.42400661186013176527e-05, -1.65842422718541333752e-05, 1.13463845846638498067e-05, -7.76281766846209442527e-06, 5.31106092088986338732e-06, -3.63365078980104566837e-06, 2.48602273312953794890e-06, -1.70085388543326065825e-06, 1.16366753635488427029e-06, -7.96142543124197040035e-07, 5.44694193066944527850e-07, -3.72661612834382295890e-07, 2.54962655202155425666e-07, -1.74436951177277452181e-07, 1.19343948298302427790e-07, -8.16511518948840884084e-08, 5.58629968353217144428e-08, -3.82196006191749421243e-08, 2.61485769519618662795e-08, -1.78899848649114926515e-08, 1.22397314032336619391e-08, -8.37401629767179054290e-09, 5.72922285984999377160e-09 }; - final double xdiff = x - psi0; - double resul = 0.; - for (int i = psiT0.length - 1; i >= 0; i--) { - resul = resul * xdiff + psiT0[i]; - } - return resul * xdiff; - } else if (x < 0.) { - /* Reflection formula */ - final double xmin = 1. - x; - return BigDecimalMath.psi(xmin) + Math.PI / Math.tan(Math.PI * xmin); - } else { - final double xmin1 = x - 1; - double resul = 0.; - for (int k = 26; k >= 1; k--) { - resul -= BigDecimalMath.zeta1(2 * k + 1); - resul *= xmin1 * xmin1; - } - /* 0.422... = 1 -gamma */ - return resul + 0.422784335098467139393487909917597568 + 0.5 / xmin1 - 1. / (1 - xmin1 * xmin1) - Math.PI / (2. * Math.tan(Math.PI * xmin1)); - } - } /* psi */ - - /** - * Broadhurst ladder sequence. - * - * @param a - * The vector of 8 integer arguments - * @param mc - * Specification of the accuracy of the result - * @return S_(n,p)(a) - * @throws Error - * @since 2009-08-09 - * @see arXiv:math/9803067 - */ - static protected BigDecimal broadhurstBBP(final int n, final int p, final int a[], final MathContext mc) - throws Error { - /* - * Explore the actual magnitude of the result first with a quick - * estimate. - */ - double x = 0.0; - for (int k = 1; k < 10; k++) { - x += a[(k - 1) % 8] / Math.pow(2., p * (k + 1) / 2) / Math.pow(k, n); - } - - /* - * Convert the relative precision and estimate of the result into an - * absolute precision. - */ - double eps = BigDecimalMath.prec2err(x, mc.getPrecision()); - - /* - * Divide this through the number of terms in the sum to account for - * error accumulation - * The divisor 2^(p(k+1)/2) means that on the average each 8th term in k - * has shrunk by - * relative to the 8th predecessor by 1/2^(4p). 1/2^(4pc) = - * 10^(-precision) with c the 8term - * cycles yields c=log_2( 10^precision)/4p = 3.3*precision/4p with k=8c - */ - final int kmax = (int) (6.6 * mc.getPrecision() / p); - - /* Now eps is the absolute error in each term */ - eps /= kmax; - BigDecimal res = BigDecimal.ZERO; - for (int c = 0;; c++) { - Rational r = new Rational(); - for (int k = 0; k < 8; k++) { - Rational tmp = new Rational(new BigInteger("" + a[k]), new BigInteger("" + (1 + 8 * c + k)).pow(n)); - /* - * floor( (pk+p)/2) - */ - final int pk1h = p * (2 + 8 * c + k) / 2; - tmp = tmp.divide(BigInteger.ONE.shiftLeft(pk1h)); - r = r.add(tmp); - } - - if (Math.abs(r.doubleValue()) < eps) { - break; - } - final MathContext mcloc = SafeMathContext.newMathContext(1 + BigDecimalMath.err2prec(r.doubleValue(), eps)); - res = res.add(r.BigDecimalValue(mcloc)); - } - return res.round(mc); - } /* broadhurstBBP */ - - /** - * Add a BigDecimal and a BigInteger. - * - * @param x - * The left summand - * @param y - * The right summand - * @return The sum x+y. - * @since 2012-03-02 - */ - static public BigDecimal add(final BigDecimal x, final BigInteger y) { - return x.add(new BigDecimal(y)); - } /* add */ - - /** - * Add and round according to the larger of the two ulp's. - * - * @param x - * The left summand - * @param y - * The right summand - * @return The sum x+y. - * @since 2009-07-30 - */ - static public BigDecimal addRound(final BigDecimal x, final BigDecimal y) { - final BigDecimal resul = x.add(y); - /* - * The estimation of the absolute error in the result is - * |err(y)|+|err(x)| - */ - final double errR = Math.abs(y.ulp().doubleValue() / 2.) + Math.abs(x.ulp().doubleValue() / 2.); - int err2prec = BigDecimalMath.err2prec(resul.doubleValue(), errR); - if (err2prec < 0) { - err2prec = 0; - } - final MathContext mc = SafeMathContext.newMathContext(err2prec); - return resul.round(mc); - } /* addRound */ - - /** - * Add and round according to the larger of the two ulp's. - * - * @param x - * The left summand - * @param y - * The right summand - * @return The sum x+y. - * @since 2010-07-19 - */ - static public BigComplex addRound(final BigComplex x, final BigDecimal y) { - final BigDecimal R = BigDecimalMath.addRound(x.re, y); - return new BigComplex(R, x.im); - } /* addRound */ - - /** - * Add and round according to the larger of the two ulp's. - * - * @param x - * The left summand - * @param y - * The right summand - * @return The sum x+y. - * @since 2010-07-19 - */ - static public BigComplex addRound(final BigComplex x, final BigComplex y) { - final BigDecimal R = BigDecimalMath.addRound(x.re, y.re); - final BigDecimal I = BigDecimalMath.addRound(x.im, y.im); - return new BigComplex(R, I); - } /* addRound */ - - /** - * Subtract and round according to the larger of the two ulp's. - * - * @param x - * The left term. - * @param y - * The right term. - * @return The difference x-y. - * @since 2009-07-30 - */ - static public BigDecimal subtractRound(final BigDecimal x, final BigDecimal y) { - final BigDecimal resul = x.subtract(y); - /* - * The estimation of the absolute error in the result is - * |err(y)|+|err(x)| - */ - final double errR = Math.abs(y.ulp().doubleValue() / 2.) + Math.abs(x.ulp().doubleValue() / 2.); - final MathContext mc = SafeMathContext.newMathContext(BigDecimalMath.err2prec(resul.doubleValue(), errR)); - return resul.round(mc); - } /* subtractRound */ - - /** - * Subtract and round according to the larger of the two ulp's. - * - * @param x - * The left summand - * @param y - * The right summand - * @return The difference x-y. - * @since 2010-07-19 - */ - static public BigComplex subtractRound(final BigComplex x, final BigComplex y) { - final BigDecimal R = BigDecimalMath.subtractRound(x.re, y.re); - final BigDecimal I = BigDecimalMath.subtractRound(x.im, y.im); - return new BigComplex(R, I); - } /* subtractRound */ - - /** - * Multiply and round. - * - * @param x - * The left factor. - * @param y - * The right factor. - * @return The product x*y. - * @since 2009-07-30 - */ - static public BigDecimal multiplyRound(final BigDecimal x, final BigDecimal y) { - final BigDecimal resul = x.multiply(y); - /* - * The estimation of the relative error in the result is the sum of the - * relative - * errors |err(y)/y|+|err(x)/x| - */ - final MathContext mc = SafeMathContext.newMathContext(Math.min(x.precision(), y.precision())); - return resul.round(mc); - } /* multiplyRound */ - - /** - * Multiply and round. - * - * @param x - * The left factor. - * @param y - * The right factor. - * @return The product x*y. - * @since 2010-07-19 - */ - static public BigComplex multiplyRound(final BigComplex x, final BigDecimal y) { - final BigDecimal R = BigDecimalMath.multiplyRound(x.re, y); - final BigDecimal I = BigDecimalMath.multiplyRound(x.im, y); - return new BigComplex(R, I); - } /* multiplyRound */ - - /** - * Multiply and round. - * - * @param x - * The left factor. - * @param y - * The right factor. - * @return The product x*y. - * @since 2010-07-19 - */ - static public BigComplex multiplyRound(final BigComplex x, final BigComplex y) { - final BigDecimal R = BigDecimalMath.subtractRound(BigDecimalMath.multiplyRound(x.re, y.re), BigDecimalMath.multiplyRound(x.im, y.im)); - final BigDecimal I = BigDecimalMath.addRound(BigDecimalMath.multiplyRound(x.re, y.im), BigDecimalMath.multiplyRound(x.im, y.re)); - return new BigComplex(R, I); - } /* multiplyRound */ - - /** - * Multiply and round. - * - * @param x - * The left factor. - * @param f - * The right factor. - * @return The product x*f. - * @since 2009-07-30 - */ - static public BigDecimal multiplyRound(final BigDecimal x, final Rational f) { - if (f.compareTo(BigInteger.ZERO) == 0) { - return BigDecimal.ZERO; - } else { - /* - * Convert the rational value with two digits of extra precision - */ - final MathContext mc = SafeMathContext.newMathContext(2 + x.precision()); - final BigDecimal fbd = f.BigDecimalValue(mc); - - /* - * and the precision of the product is then dominated by the - * precision in x - */ - return BigDecimalMath.multiplyRound(x, fbd); - } - } - - /** - * Multiply and round. - * - * @param x - * The left factor. - * @param n - * The right factor. - * @return The product x*n. - * @since 2009-07-30 - */ - static public BigDecimal multiplyRound(final BigDecimal x, final int n) { - final BigDecimal resul = x.multiply(new BigDecimal(n)); - /* - * The estimation of the absolute error in the result is |n*err(x)| - */ - final MathContext mc = SafeMathContext.newMathContext(n != 0 ? x.precision() : 0); - return resul.round(mc); - } - - /** - * Multiply and round. - * - * @param x - * The left factor. - * @param n - * The right factor. - * @return the product x*n - * @since 2009-07-30 - */ - static public BigDecimal multiplyRound(final BigDecimal x, final BigInteger n) { - final BigDecimal resul = x.multiply(new BigDecimal(n)); - /* - * The estimation of the absolute error in the result is |n*err(x)| - */ - final MathContext mc = SafeMathContext.newMathContext(n.compareTo(BigInteger.ZERO) != 0 ? x.precision() : 0); - return resul.round(mc); - } - - /** - * Divide and round. - * - * @param x - * The numerator - * @param y - * The denominator - * @return the divided x/y - * @since 2009-07-30 - */ - static public BigDecimal divideRound(final BigDecimal x, final BigDecimal y) { - /* - * The estimation of the relative error in the result is - * |err(y)/y|+|err(x)/x| - */ - final MathContext mc = SafeMathContext.newMathContext(Math.min(x.precision(), y.precision())); - final BigDecimal resul = x.divide(y, mc); - /* - * If x and y are precise integer values that may have common factors, - * the method above will truncate trailing zeros, which may result in - * a smaller apparent accuracy than starte... add missing trailing zeros - * now. - */ - return BigDecimalMath.scalePrec(resul, mc); - } - - /** - * Build the inverse and maintain the approximate accuracy. - * - * @param z - * The denominator - * @return The divided 1/z = [Re(z)-i*Im(z)]/ [Re^2 z + Im^2 z] - * @since 2010-07-19 - */ - static public BigComplex invertRound(final BigComplex z) { - if (z.im.compareTo(BigDecimal.ZERO) == 0) { - /* - * In this case with vanishing Im(x), the result is simply 1/Re z. - */ - final MathContext mc = SafeMathContext.newMathContext(z.re.precision()); - return new BigComplex(BigDecimal.ONE.divide(z.re, mc)); - } else if (z.re.compareTo(BigDecimal.ZERO) == 0) { - /* - * In this case with vanishing Re(z), the result is simply -i/Im z - */ - final MathContext mc = SafeMathContext.newMathContext(z.im.precision()); - return new BigComplex(BigDecimal.ZERO, BigDecimal.ONE.divide(z.im, mc).negate()); - } else { - /* - * 1/(x.re+I*x.im) = 1/(x.re+x.im^2/x.re) - I /(x.im +x.re^2/x.im) - */ - BigDecimal R = BigDecimalMath.addRound(z.re, BigDecimalMath.divideRound(BigDecimalMath.multiplyRound(z.im, z.im), z.re)); - BigDecimal I = BigDecimalMath.addRound(z.im, BigDecimalMath.divideRound(BigDecimalMath.multiplyRound(z.re, z.re), z.im)); - MathContext mc = SafeMathContext.newMathContext(1 + R.precision()); - R = BigDecimal.ONE.divide(R, mc); - mc = SafeMathContext.newMathContext(1 + I.precision()); - I = BigDecimal.ONE.divide(I, mc); - return new BigComplex(R, I.negate()); - } - } - - /** - * Divide and round. - * - * @param x - * The numerator - * @param y - * The denominator - * @return the divided x/y - * @since 2010-07-19 - */ - static public BigComplex divideRound(final BigComplex x, final BigComplex y) { - return BigDecimalMath.multiplyRound(x, BigDecimalMath.invertRound(y)); - } - - /** - * Divide and round. - * - * @param x - * The numerator - * @param n - * The denominator - * @return the divided x/n - * @since 2009-07-30 - */ - static public BigDecimal divideRound(final BigDecimal x, final int n) { - /* - * The estimation of the relative error in the result is |err(x)/x| - */ - final MathContext mc = SafeMathContext.newMathContext(x.precision()); - return x.divide(new BigDecimal(n), mc); - } - - /** - * Divide and round. - * - * @param x - * The numerator - * @param n - * The denominator - * @return the divided x/n - * @since 2009-07-30 - */ - static public BigDecimal divideRound(final BigDecimal x, final BigInteger n) { - /* - * The estimation of the relative error in the result is |err(x)/x| - */ - final MathContext mc = SafeMathContext.newMathContext(x.precision()); - return x.divide(new BigDecimal(n), mc); - } /* divideRound */ - - /** - * Divide and round. - * - * @param n - * The numerator - * @param x - * The denominator - * @return the divided n/x - * @since 2009-08-05 - */ - static public BigDecimal divideRound(final BigInteger n, final BigDecimal x) { - /* - * The estimation of the relative error in the result is |err(x)/x| - */ - final MathContext mc = SafeMathContext.newMathContext(x.precision()); - return new BigDecimal(n).divide(x, mc); - } /* divideRound */ - - /** - * Divide and round. - * - * @param n - * The numerator - * @param x - * The denominator - * @return the divided n/x - * @since 2012-03-01 - */ - static public BigComplex divideRound(final BigInteger n, final BigComplex x) { - /* - * catch case of real-valued denominator first - */ - if (x.im.compareTo(BigDecimal.ZERO) == 0) { - return new BigComplex(BigDecimalMath.divideRound(n, x.re), BigDecimal.ZERO); - } else if (x.re.compareTo(BigDecimal.ZERO) == 0) { - return new BigComplex(BigDecimal.ZERO, BigDecimalMath.divideRound(n, x.im).negate()); - } - - final BigComplex z = BigDecimalMath.invertRound(x); - /* - * n/(x+iy) = nx/(x^2+y^2) -nyi/(x^2+y^2) - */ - final BigDecimal repart = BigDecimalMath.multiplyRound(z.re, n); - final BigDecimal impart = BigDecimalMath.multiplyRound(z.im, n); - return new BigComplex(repart, impart); - } /* divideRound */ - - /** - * Divide and round. - * - * @param n - * The numerator. - * @param x - * The denominator. - * @return the divided n/x. - * @since 2009-08-05 - */ - static public BigDecimal divideRound(final int n, final BigDecimal x) { - /* - * The estimation of the relative error in the result is |err(x)/x| - */ - final MathContext mc = SafeMathContext.newMathContext(x.precision()); - return new BigDecimal(n).divide(x, mc); - } - - /** - * Append decimal zeros to the value. This returns a value which appears to - * have - * a higher precision than the input. - * - * @param x - * The input value - * @param d - * The (positive) value of zeros to be added as least significant - * digits. - * @return The same value as the input but with increased (pseudo) - * precision. - */ - static public BigDecimal scalePrec(final BigDecimal x, final int d) { - return x.setScale(d + x.scale()); - } - - /** - * Append decimal zeros to the value. This returns a value which appears to - * have - * a higher precision than the input. - * - * @param x - * The input value - * @param d - * The (positive) value of zeros to be added as least significant - * digits. - * @return The same value as the input but with increased (pseudo) - * precision. - */ - static public BigComplex scalePrec(final BigComplex x, final int d) { - return new BigComplex(BigDecimalMath.scalePrec(x.re, d), BigDecimalMath.scalePrec(x.im, d)); - } - - /** - * Boost the precision by appending decimal zeros to the value. This returns - * a value which appears to have - * a higher precision than the input. - * - * @param x - * The input value - * @param mc - * The requirement on the minimum precision on return. - * @return The same value as the input but with increased (pseudo) - * precision. - */ - static public BigDecimal scalePrec(final BigDecimal x, final MathContext mc) { - final int diffPr = mc.getPrecision() - x.precision(); - if (diffPr > 0) { - return BigDecimalMath.scalePrec(x, diffPr); - } else { - return x; - } - } /* BigDecimalMath.scalePrec */ - - /** - * Convert an absolute error to a precision. - * - * @param x - * The value of the variable - * @param xerr - * The absolute error in the variable - * @return The number of valid digits in x. - * The value is rounded down, and on the pessimistic side for that - * reason. - * @since 2009-06-25 - */ - static public int err2prec(final BigDecimal x, final BigDecimal xerr) { - return BigDecimalMath.err2prec(xerr.divide(x, MathContext.DECIMAL64).doubleValue()); - } - - /** - * Convert an absolute error to a precision. - * - * @param x - * The value of the variable - * The value returned depends only on the absolute value, not on - * the sign. - * @param xerr - * The absolute error in the variable - * The value returned depends only on the absolute value, not on - * the sign. - * @return The number of valid digits in x. - * Derived from the representation x+- xerr, as if the error was - * represented - * in a "half width" (half of the error bar) form. - * The value is rounded down, and on the pessimistic side for that - * reason. - * @since 2009-05-30 - */ - static public int err2prec(final double x, final double xerr) { - /* - * Example: an error of xerr=+-0.5 at x=100 represents 100+-0.5 with - * a precision = 3 (digits). - */ - return 1 + (int) Math.log10(Math.abs(0.5 * x / xerr)); - } - - /** - * Convert a relative error to a precision. - * - * @param xerr - * The relative error in the variable. - * The value returned depends only on the absolute value, not on - * the sign. - * @return The number of valid digits in x. - * The value is rounded down, and on the pessimistic side for that - * reason. - * @since 2009-08-05 - */ - static public int err2prec(final double xerr) { - /* - * Example: an error of xerr=+-0.5 a precision of 1 (digit), an error of - * +-0.05 a precision of 2 (digits) - */ - return 1 + (int) Math.log10(Math.abs(0.5 / xerr)); - } - - /** - * Convert a precision (relative error) to an absolute error. - * The is the inverse functionality of err2prec(). - * - * @param x - * The value of the variable - * The value returned depends only on the absolute value, not on - * the sign. - * @param prec - * The number of valid digits of the variable. - * @return the absolute error in x. - * Derived from the an accuracy of one half of the ulp. - * @since 2009-08-09 - */ - static public double prec2err(final double x, final int prec) { - return 5. * Math.abs(x) * Math.pow(10., -prec); - } - -} /* BigDecimalMath */ diff --git a/core/src/main/java/org/nevec/rjm/BigIntegerMath.java b/core/src/main/java/org/nevec/rjm/BigIntegerMath.java deleted file mode 100644 index 7c2893f2..00000000 --- a/core/src/main/java/org/nevec/rjm/BigIntegerMath.java +++ /dev/null @@ -1,644 +0,0 @@ -package org.nevec.rjm; - -import java.math.BigInteger; -import java.util.Vector; - -import it.cavallium.warppi.util.Error; - -/** - * BigInteger special functions and Number theory. - * - * @since 2009-08-06 - * @author Richard J. Mathar - */ -public class BigIntegerMath { - - /** - * Evaluate binomial(n,k). - * - * @param n - * The upper index - * @param k - * The lower index - * @return The binomial coefficient - */ - static public BigInteger binomial(final int n, final int k) { - if (k == 0) { - return BigInteger.ONE; - } - BigInteger bin = new BigInteger("" + n); - final BigInteger n2 = bin; - for (BigInteger i = new BigInteger("" + (k - 1)); i.compareTo(BigInteger.ONE) >= 0; i = i.subtract(BigInteger.ONE)) { - bin = bin.multiply(n2.subtract(i)); - } - for (BigInteger i = new BigInteger("" + k); i.compareTo(BigInteger.ONE) == 1; i = i.subtract(BigInteger.ONE)) { - bin = bin.divide(i); - } - return bin; - } /* binomial */ - - /** - * Evaluate binomial(n,k). - * - * @param n - * The upper index - * @param k - * The lower index - * @return The binomial coefficient - * @since 2008-10-15 - */ - static public BigInteger binomial(final BigInteger n, final BigInteger k) { - /* - * binomial(n,0) =1 - */ - if (k.compareTo(BigInteger.ZERO) == 0) { - return BigInteger.ONE; - } - - BigInteger bin = new BigInteger("" + n); - - /* - * the following version first calculates n(n-1)(n-2)..(n-k+1) - * in the first loop, and divides this product through k(k-1)(k-2)....2 - * in the second loop. This is rather slow and replaced by a faster - * version - * below - * BigInteger n2 = bin ; - * BigInteger i= k.subtract(BigInteger.ONE) ; - * for( ; i.compareTo(BigInteger.ONE) >= 0 ; i = - * i.subtract(BigInteger.ONE) ) - * bin = bin.multiply(n2.subtract(i)) ; - * i= new BigInteger(""+k) ; - * for( ; i.compareTo(BigInteger.ONE) == 1 ; i = - * i.subtract(BigInteger.ONE) ) - * bin = bin.divide(i) ; - */ - - /* - * calculate n then n(n-1)/2 then n(n-1)(n-2)(2*3) etc up to - * n(n-1)..(n-k+1)/(2*3*..k) - * This is roughly the best way to keep the individual intermediate - * products small - * and in the integer domain. First replace C(n,k) by C(n,n-k) if n-k - * Deprecated: This function is extremely slow and inefficient! - * - * @param n - * The integer of which the divisors are to be found. - * @return The sorted list of positive divisors. - * @since 2010-08-27 - * @author Richard J. Mathar - */ - @Deprecated - static public Vector divisors(final BigInteger n) { - return new Ifactor(n.abs()).divisors(); - } - - /** - * Evaluate sigma(n). - * - * @param n - * the argument for which divisors will be searched. - * @return the sigma function. Sum of the divisors of the argument. - * @since 2006-08-14 - * @author Richard J. Mathar - */ - static public BigInteger sigma(final BigInteger n) { - return new Ifactor(n.abs()).sigma().n; - } - - /** - * Evaluate floor(sqrt(n)). - * - * @param n - * The non-negative argument. - * @return The integer square root. The square root rounded down. - * @since 2010-08-27 - * @author Richard J. Mathar - */ - static public int isqrt(final int n) { - if (n < 0) { - throw new ArithmeticException("Negative argument " + n); - } - final double resul = Math.sqrt(n); - return (int) Math.round(resul); - } - - /** - * Evaluate floor(sqrt(n)). - * - * @param n - * The non-negative argument. - * Arguments less than zero throw an ArithmeticException. - * @return The integer square root, the square root rounded down. - * @since 2010-08-27 - * @author Richard J. Mathar - */ - static public long isqrt(final long n) { - if (n < 0) { - throw new ArithmeticException("Negative argument " + n); - } - final double resul = Math.sqrt(n); - return Math.round(resul); - } - - /** - * Evaluate floor(sqrt(n)). - * - * @param n - * The non-negative argument. - * Arguments less than zero throw an ArithmeticException. - * @return The integer square root, the square root rounded down. - * @since 2011-02-12 - * @author Richard J. Mathar - */ - static public BigInteger isqrt(final BigInteger n) { - if (n.compareTo(BigInteger.ZERO) < 0) { - throw new ArithmeticException("Negative argument " + n.toString()); - } - /* - * Start with an estimate from a floating point reduction. - */ - BigInteger x; - final int bl = n.bitLength(); - if (bl > 120) { - x = n.shiftRight(bl / 2 - 1); - } else { - final double resul = Math.sqrt(n.doubleValue()); - x = new BigInteger("" + Math.round(resul)); - } - - final BigInteger two = new BigInteger("2"); - while (true) { - /* - * check whether the result is accurate, x^2 =n - */ - final BigInteger x2 = x.pow(2); - BigInteger xplus2 = x.add(BigInteger.ONE).pow(2); - if (x2.compareTo(n) <= 0 && xplus2.compareTo(n) > 0) { - return x; - } - xplus2 = xplus2.subtract(x.shiftLeft(2)); - if (xplus2.compareTo(n) <= 0 && x2.compareTo(n) > 0) { - return x.subtract(BigInteger.ONE); - } - /* - * Newton algorithm. This correction is on the - * low side caused by the integer divisions. So the value required - * may end up by one unit too large by the bare algorithm, and this - * is caught above by comparing x^2, (x+-1)^2 with n. - */ - xplus2 = x2.subtract(n).divide(x).divide(two); - x = x.subtract(xplus2); - } - } - - /** - * Evaluate core(n). - * Returns the smallest positive integer m such that n/m is a perfect - * square. - * - * @param n - * The non-negative argument. - * @return The square-free part of n. - * @since 2011-02-12 - * @author Richard J. Mathar - */ - static public BigInteger core(final BigInteger n) { - if (n.compareTo(BigInteger.ZERO) < 0) { - throw new ArithmeticException("Negative argument " + n); - } - final Ifactor i = new Ifactor(n); - return i.core(); - } - - /** - * Minor of an integer matrix. - * - * @param A - * The matrix. - * @param r - * The row index of the row to be removed (0-based). - * An exception is thrown if this is outside the range 0 to the - * upper row index of A. - * @param c - * The column index of the column to be removed (0-based). - * An exception is thrown if this is outside the range 0 to the - * upper column index of A. - * @return The depleted matrix. This is not a deep copy but contains - * references to the original. - * @since 2010-08-27 - * @author Richard J. Mathar - */ - static public BigInteger[][] minor(final BigInteger[][] A, final int r, final int c) throws ArithmeticException { - /* original row count */ - final int rL = A.length; - if (rL == 0) { - throw new ArithmeticException("zero row count in matrix"); - } - if (r < 0 || r >= rL) { - throw new ArithmeticException("row number " + r + " out of range 0.." + (rL - 1)); - } - /* original column count */ - final int cL = A[0].length; - if (cL == 0) { - throw new ArithmeticException("zero column count in matrix"); - } - if (c < 0 || c >= cL) { - throw new ArithmeticException("column number " + c + " out of range 0.." + (cL - 1)); - } - final BigInteger M[][] = new BigInteger[rL - 1][cL - 1]; - int imrow = 0; - for (int row = 0; row < rL; row++) { - if (row != r) { - int imcol = 0; - for (int col = 0; col < cL; col++) { - if (col != c) { - M[imrow][imcol] = A[row][col]; - imcol++; - } - } - imrow++; - } - } - return M; - } - - /** - * Replace column of a matrix with a column vector. - * - * @param A - * The matrix. - * @param c - * The column index of the column to be substituted (0-based). - * @param v - * The column vector to be inserted. - * With the current implementation, it must be at least as long - * as the row count, and - * its elements that exceed that count are ignored. - * @return The modified matrix. This is not a deep copy but contains - * references to the original. - * @since 2010-08-27 - * @author Richard J. Mathar - */ - @SuppressWarnings("unused") - static private BigInteger[][] colSubs(final BigInteger[][] A, final int c, final BigInteger[] v) - throws ArithmeticException { - /* original row count */ - final int rL = A.length; - if (rL == 0) { - throw new ArithmeticException("zero row count in matrix"); - } - /* original column count */ - final int cL = A[0].length; - if (cL == 0) { - throw new ArithmeticException("zero column count in matrix"); - } - if (c < 0 || c >= cL) { - throw new ArithmeticException("column number " + c + " out of range 0.." + (cL - 1)); - } - final BigInteger M[][] = new BigInteger[rL][cL]; - for (int row = 0; row < rL; row++) { - for (int col = 0; col < cL; col++) { - /* - * currently, v may just be longer than the row count, and - * surplus - * elements will be ignored. Shorter v lead to an exception. - */ - if (col != c) { - M[row][col] = A[row][col]; - } else { - M[row][col] = v[row]; - } - } - } - return M; - } - - /** - * Determinant of an integer square matrix. - * - * @param A - * The square matrix. - * If column and row dimensions are unequal, an - * ArithmeticException is thrown. - * @return The determinant. - * @since 2010-08-27 - * @author Richard J. Mathar - */ - static public BigInteger det(final BigInteger[][] A) throws ArithmeticException { - BigInteger d = BigInteger.ZERO; - /* row size */ - final int rL = A.length; - if (rL == 0) { - throw new ArithmeticException("zero row count in matrix"); - } - /* column size */ - final int cL = A[0].length; - if (cL != rL) { - throw new ArithmeticException("Non-square matrix dim " + rL + " by " + cL); - } - - /* - * Compute the low-order cases directly. - */ - if (rL == 1) { - return A[0][0]; - } else if (rL == 2) { - d = A[0][0].multiply(A[1][1]); - return d.subtract(A[0][1].multiply(A[1][0])); - } else { - /* Work arbitrarily along the first column of the matrix */ - for (int r = 0; r < rL; r++) { - /* - * Do not consider minors that do no contribute anyway - */ - if (A[r][0].compareTo(BigInteger.ZERO) != 0) { - final BigInteger M[][] = BigIntegerMath.minor(A, r, 0); - final BigInteger m = A[r][0].multiply(BigIntegerMath.det(M)); - /* recursive call */ - if (r % 2 == 0) { - d = d.add(m); - } else { - d = d.subtract(m); - } - } - } - } - return d; - } - - /** - * Solve a linear system of equations. - * - * @param A - * The square matrix. - * If it is not of full rank, an ArithmeticException is thrown. - * @param rhs - * The right hand side. The length of this vector must match the - * matrix size; - * else an ArithmeticException is thrown. - * @return The vector of x in A*x=rhs. - * @since 2010-08-28 - * @author Richard J. Mathar - * @throws Error - */ - static public Rational[] solve(final BigInteger[][] A, final BigInteger[] rhs) throws ArithmeticException, Error { - - final int rL = A.length; - if (rL == 0) { - throw new ArithmeticException("zero row count in matrix"); - } - - /* column size */ - final int cL = A[0].length; - if (cL != rL) { - throw new ArithmeticException("Non-square matrix dim " + rL + " by " + cL); - } - if (rhs.length != rL) { - throw new ArithmeticException("Right hand side dim " + rhs.length + " unequal matrix dim " + rL); - } - - /* - * Gauss elimination - */ - final Rational x[] = new Rational[rL]; - - /* - * copy of r.h.s ito a mutable Rationalright hand side - */ - for (int c = 0; c < cL; c++) { - x[c] = new Rational(rhs[c]); - } - - /* - * Create zeros downwards column c by linear combination of row c and - * row r. - */ - for (int c = 0; c < cL - 1; c++) { - /* - * zero on the diagonal? swap with a non-zero row, searched with - * index r - */ - if (A[c][c].compareTo(BigInteger.ZERO) == 0) { - boolean swpd = false; - for (int r = c + 1; r < rL; r++) { - if (A[r][c].compareTo(BigInteger.ZERO) != 0) { - for (int cpr = c; cpr < cL; cpr++) { - final BigInteger tmp = A[c][cpr]; - A[c][cpr] = A[r][cpr]; - A[r][cpr] = tmp; - } - final Rational tmp = x[c]; - x[c] = x[r]; - x[r] = tmp; - swpd = true; - break; - } - } - /* - * not swapped with a non-zero row: determinant zero and no - * solution - */ - if (!swpd) { - throw new ArithmeticException("Zero determinant of main matrix"); - } - } - /* create zero at A[c+1..cL-1][c] */ - for (int r = c + 1; r < rL; r++) { - /* - * skip the cpr=c which actually sets the zero: this element is - * not visited again - */ - for (int cpr = c + 1; cpr < cL; cpr++) { - final BigInteger tmp = A[c][c].multiply(A[r][cpr]).subtract(A[c][cpr].multiply(A[r][c])); - A[r][cpr] = tmp; - } - final Rational tmp = x[r].multiply(A[c][c]).subtract(x[c].multiply(A[r][c])); - x[r] = tmp; - } - } - if (A[cL - 1][cL - 1].compareTo(BigInteger.ZERO) == 0) { - throw new ArithmeticException("Zero determinant of main matrix"); - } - /* backward elimination */ - for (int r = cL - 1; r >= 0; r--) { - x[r] = x[r].divide(A[r][r]); - for (int rpr = r - 1; rpr >= 0; rpr--) { - x[rpr] = x[rpr].subtract(x[r].multiply(A[rpr][r])); - } - } - - return x; - } - - /** - * The lowest common multiple - * - * @param a - * The first argument - * @param b - * The second argument - * @return lcm(|a|,|b|) - * @since 2010-08-27 - * @author Richard J. Mathar - */ - static public BigInteger lcm(final BigInteger a, final BigInteger b) { - final BigInteger g = a.gcd(b); - return a.multiply(b).abs().divide(g); - } - - /** - * Evaluate the value of an integer polynomial at some integer argument. - * - * @param c - * Represents the coefficients c[0]+c[1]*x+c[2]*x^2+.. of the - * polynomial - * @param x - * The abscissa point of the evaluation - * @return The polynomial value. - * @since 2010-08-27 - * @author Richard J. Mathar - */ - static public BigInteger valueOf(final Vector c, final BigInteger x) { - if (c.size() == 0) { - return BigInteger.ZERO; - } - BigInteger res = c.lastElement(); - for (int i = c.size() - 2; i >= 0; i--) { - res = res.multiply(x).add(c.elementAt(i)); - } - return res; - } - - /** - * The central factorial number t(n,k) number at the indices provided. - * - * @param n - * the first parameter, non-negative. - * @param k - * the second index, non-negative. - * @return t(n,k) - * @since 2009-08-06 - * @author Richard J. Mathar - * @throws Error - * @see P. L. Butzer - * et al, Num. Funct. Anal. Opt. 10 (5)( 1989) 419-488 - */ - static public Rational centrlFactNumt(final int n, final int k) throws Error { - if (k > n || k < 0 || k % 2 != n % 2) { - return Rational.ZERO; - } else if (k == n) { - return Rational.ONE; - } else { - /* Proposition 6.2.6 */ - final Factorial f = new Factorial(); - Rational jsum = new Rational(0, 1); - final int kprime = n - k; - for (int j = 0; j <= kprime; j++) { - Rational nusum = new Rational(0, 1); - for (int nu = 0; nu <= j; nu++) { - Rational t = new Rational(j - 2 * nu, 2); - t = t.pow(kprime + j); - t = t.multiply(BigIntegerMath.binomial(j, nu)); - if (nu % 2 != 0) { - nusum = nusum.subtract(t); - } else { - nusum = nusum.add(t); - } - } - nusum = nusum.divide(f.at(j)).divide(n + j); - nusum = nusum.multiply(BigIntegerMath.binomial(2 * kprime, kprime - j)); - if (j % 2 != 0) { - jsum = jsum.subtract(nusum); - } else { - jsum = jsum.add(nusum); - } - } - return jsum.multiply(k).multiply(BigIntegerMath.binomial(n + kprime, k)); - } - } /* CentralFactNumt */ - - /** - * The central factorial number T(n,k) number at the indices provided. - * - * @param n - * the first parameter, non-negative. - * @param k - * the second index, non-negative. - * @return T(n,k) - * @since 2009-08-06 - * @author Richard J. Mathar - * @see P. L. Butzer - * et al, Num. Funct. Anal. Opt. 10 (5)( 1989) 419-488 - */ - static public Rational centrlFactNumT(final int n, final int k) { - if (k > n || k < 0 || k % 2 != n % 2) { - return Rational.ZERO; - } else if (k == n) { - return Rational.ONE; - } else { - /* Proposition 2.1 */ - return BigIntegerMath.centrlFactNumT(n - 2, k - 2).add(BigIntegerMath.centrlFactNumT(n - 2, k).multiply(new Rational(k * k, 4))); - } - } /* CentralFactNumT */ - -} /* BigIntegerMath */ diff --git a/core/src/main/java/org/nevec/rjm/BigIntegerPoly.java b/core/src/main/java/org/nevec/rjm/BigIntegerPoly.java deleted file mode 100644 index 0818c02e..00000000 --- a/core/src/main/java/org/nevec/rjm/BigIntegerPoly.java +++ /dev/null @@ -1,764 +0,0 @@ -package org.nevec.rjm; - -import java.math.BigDecimal; -import java.math.BigInteger; -import java.util.Scanner; -import java.util.Vector; - -import it.cavallium.warppi.util.Error; - -/** - * Polynomial with integer coefficients. - * Alternatively to be interpreted as a sequence which has the polynomial as an - * (approximate) - * generating function. - * - * @since 2010-08-27 - * @author Richard J. Mathar - */ -public class BigIntegerPoly implements Cloneable { - /** - * The list of all coefficients, starting with a0, then a1, as in - * poly=a0+a1*x+a2*x^2+a3*x^3+... - */ - Vector a; - - /** - * Default ctor. - * Creates the polynomial p(x)=0. - */ - public BigIntegerPoly() { - a = new Vector<>(); - } - - /** - * Ctor with a comma-separated list as the list of coefficients. - * - * @param L - * the string of the form a0,a1,a2,a3 with the coefficients - */ - public BigIntegerPoly(final String L) throws NumberFormatException { - a = new Vector<>(); - final Scanner sc = new Scanner(L); - sc.useDelimiter(","); - while (sc.hasNextBigInteger()) { - a.add(sc.nextBigInteger()); - } - simplify(); - sc.close(); - } /* ctor */ - - /** - * Ctor with a list of coefficients. - * - * @param c - * The coefficients a0, a1, a2 etc in a0+a1*x+a2*x^2+... - */ - @SuppressWarnings("unchecked") - public BigIntegerPoly(final Vector c) { - a = (Vector) c.clone(); - simplify(); - } /* ctor */ - - /** - * Ctor with a list of coefficients. - * - * @param c - * The coefficients a0, a1, a2 etc in a0+a1*x+a2*x^2+... - */ - public BigIntegerPoly(final BigInteger[] c) { - for (final BigInteger element : c) { - a.add(element.add(BigInteger.ZERO)); - } - simplify(); - } /* ctor */ - - /** - * Create a copy of this. - * - * @since 2010-08-27 - */ - @Override - public BigIntegerPoly clone() { - return new BigIntegerPoly(a); - } /* clone */ - - /** - * Translate into a RatPoly copy. - * - * @since 2012-03-02 - */ - public RatPoly toRatPoly() { - final RatPoly bd = new RatPoly(); - for (int i = 0; i < a.size(); i++) { - bd.set(i, a.elementAt(i)); - } - return bd; - } /* toRatPoly */ - - /** - * Retrieve a polynomial coefficient. - * - * @param n - * the zero-based index of the coefficient. n=0 for the constant - * term. - * @return the polynomial coefficient in front of x^n. - */ - public BigInteger at(final int n) { - if (n < a.size()) { - return a.elementAt(n); - } else { - return BigInteger.ZERO; - } - } /* at */ - - /** - * Evaluate at some integer argument. - * - * @param x - * The abscissa point of the evaluation - * @return The polynomial value. - * @since 2010-08-27 - * @author Richard J. Mathar - */ - public BigInteger valueOf(final BigInteger x) { - if (a.size() == 0) { - return BigInteger.ZERO; - } - BigInteger res = a.lastElement(); - /* - * Heron casted form - */ - for (int i = a.size() - 2; i >= 0; i--) { - res = res.multiply(x).add(a.elementAt(i)); - } - return res; - } /* valueOf */ - - /** - * Horner scheme to find the function value at the argument x - * - * @param x - * The argument x. - * @return Value of the polynomial at x. - * @since 2008-11-13 - */ - public BigInteger valueOf(final int x) { - return valueOf(new BigInteger("" + x)); - } /* valueOf */ - - /** - * Set a polynomial coefficient. - * - * @param n - * the zero-based index of the coefficient. n=0 for the constant - * term. - * If the polynomial has not yet the degree to need this - * coefficient, - * the intermediate coefficients are set to zero. - * @param value - * the new value of the coefficient. - */ - public void set(final int n, final BigInteger value) { - if (n < a.size()) { - a.set(n, value); - } else { - /* - * fill intermediate powers with coefficients of zero - */ - while (a.size() < n) { - a.add(BigInteger.ZERO); - } - a.add(value); - } - } /* set */ - - /** - * Set a polynomial coefficient. - * - * @param n - * the zero-based index of the coefficient. n=0 for the constant - * term. - * If the polynomial has not yet the degree to need this - * coefficient, - * the intermediate coefficients are implicitly set to zero. - * @param value - * the new value of the coefficient. - */ - public void set(final int n, final int value) { - final BigInteger val2 = new BigInteger("" + value); - set(n, val2); - } /* set */ - - /** - * Count of coefficients. - * - * @return the number of polynomial coefficients. - * Differs from the polynomial degree by one. - */ - public int size() { - return a.size(); - } /* size */ - - /** - * Polynomial degree. - * - * @return the polynomial degree. - */ - public int degree() { - return a.size() - 1; - } /* degree */ - - /** - * Polynomial lower degree. - * - * @return power of the smallest non-zero coefficient. - * If the polynomial is identical to 0, 0 is returned. - */ - public int ldegree() { - for (int n = 0; n < a.size(); n++) { - if (a.elementAt(n).compareTo(BigInteger.ZERO) != 0) { - return n; - } - } - return 0; - } /* ldegree */ - - /** - * Multiply by a constant factor. - * - * @param val - * the factor - * @return the product of this with the factor. - * All coefficients of this have been multiplied individually by the - * factor. - * @since 2010-08-27 - */ - public BigIntegerPoly multiply(final BigInteger val) { - final BigIntegerPoly resul = new BigIntegerPoly(); - if (val.compareTo(BigInteger.ZERO) != 0) { - for (int n = 0; n < a.size(); n++) { - resul.set(n, a.elementAt(n).multiply(val)); - } - } - return resul; - } /* multiply */ - - /** - * Multiply by another polynomial - * - * @param val - * the other polynomial - * @return the product of this with the other polynomial - */ - public BigIntegerPoly multiply(final BigIntegerPoly val) { - final BigIntegerPoly resul = new BigIntegerPoly(); - /* - * the degree of the result is the sum of the two degrees. - */ - final int nmax = degree() + val.degree(); - for (int n = 0; n <= nmax; n++) { - BigInteger coef = BigInteger.ZERO; - for (int nleft = 0; nleft <= n; nleft++) { - coef = coef.add(at(nleft).multiply(val.at(n - nleft))); - } - resul.set(n, coef); - } - resul.simplify(); - return resul; - } /* multiply */ - - /** - * Raise to a positive power. - * - * @param n - * the exponent of the power - * @return the n-th power of this. - */ - public BigIntegerPoly pow(final int n) throws ArithmeticException { - BigIntegerPoly resul = new BigIntegerPoly("1"); - if (n < 0) { - throw new ArithmeticException("negative polynomial power " + n); - } else { - for (int i = 1; i <= n; i++) { - resul = resul.multiply(this); - } - resul.simplify(); - return resul; - } - } /* pow */ - - /** - * Add another polynomial - * - * @param val - * the other polynomial - * @return the sum of this with the other polynomial - * @since 2010-08-27 - */ - public BigIntegerPoly add(final BigIntegerPoly val) { - final BigIntegerPoly resul = new BigIntegerPoly(); - /* - * the degree of the result is the larger of the two degrees (before - * simplify() at least). - */ - final int nmax = degree() > val.degree() ? degree() : val.degree(); - for (int n = 0; n <= nmax; n++) { - final BigInteger coef = at(n).add(val.at(n)); - resul.set(n, coef); - } - resul.simplify(); - return resul; - } /* add */ - - /** - * Subtract another polynomial - * - * @param val - * the other polynomial - * @return the difference between this and the other polynomial - * @since 2008-10-25 - */ - public BigIntegerPoly subtract(final BigIntegerPoly val) { - final BigIntegerPoly resul = new BigIntegerPoly(); - /* - * the degree of the result is the larger of the two degrees (before - * simplify() at least). - */ - final int nmax = degree() > val.degree() ? degree() : val.degree(); - for (int n = 0; n <= nmax; n++) { - final BigInteger coef = at(n).subtract(val.at(n)); - resul.set(n, coef); - } - resul.simplify(); - return resul; - } /* subtract */ - - /** - * Divide by another polynomial. - * - * @param val - * the other polynomial - * @return A vector with [0] containg the polynomial of degree which is the - * difference of the degree of this and the degree of val. [1] the - * remainder polynomial. - * This = returnvalue[0] + returnvalue[1]/val . - * @since 2012-03-01 - */ - public BigIntegerPoly[] divideAndRemainder(final BigIntegerPoly val) { - final BigIntegerPoly[] ret = new BigIntegerPoly[2]; - /* - * remove any high-order zeros. note that the clone() operation calls - * simplify(). - */ - final BigIntegerPoly valSimpl = val.clone(); - final BigIntegerPoly thisSimpl = clone(); - - /* - * catch the case with val equal to zero - */ - if (valSimpl.degree() == 0 && valSimpl.a.firstElement().compareTo(BigInteger.ZERO) == 0) { - throw new ArithmeticException("Division through zero polynomial"); - } - /* - * degree of this smaller than degree of val: remainder is this - */ - if (thisSimpl.degree() < valSimpl.degree()) { - /* - * leading polynomial equals zero - */ - ret[0] = new BigIntegerPoly(); - ret[1] = thisSimpl; - } else { - /* - * long division. Highest degree by dividing the highest degree - * of this thru val. At this point an exception is thrown if the - * polynomial division cannot be done with integer coefficients. - */ - ret[0] = new BigIntegerPoly(); - final BigInteger[] newc = thisSimpl.a.lastElement().divideAndRemainder(valSimpl.a.lastElement()); - if (newc[1].compareTo(BigInteger.ZERO) != 0) { - throw new ArithmeticException("Incompatible leading term in " + this + " / " + val); - } - ret[0].set(thisSimpl.degree() - valSimpl.degree(), newc[0]); - - /* - * recurrences: build this - val*(1-termresult) and feed this - * into another round of division. Have intermediate - * ret[0]+ret[1]/val. - */ - ret[1] = thisSimpl.subtract(ret[0].multiply(valSimpl)); - - /* - * any remainder left ? - */ - if (ret[1].degree() < valSimpl.degree()) { - ; - } else { - final BigIntegerPoly rem[] = ret[1].divideAndRemainder(val); - ret[0] = ret[0].add(rem[0]); - ret[1] = rem[1]; - } - } - return ret; - } /* divideAndRemainder */ - - /** - * Print as a comma-separated list of coefficients. - * - * @return the representation a0,a1,a2,a3,... - * @since 2010-08-27 - */ - @Override - public String toString() { - String str = new String(); - for (int n = 0; n < a.size(); n++) { - if (n == 0) { - str += a.elementAt(n).toString(); - } else { - str += "," + a.elementAt(n).toString(); - } - } - if (str.length() == 0) { - str = "0"; - } - return str; - } /* toString */ - - /** - * Print as a polyomial in x. - * - * @return The representation a0+a1*x+a2*x^2+... - * The terms with zero coefficients are not mentioned. - * @since 2008-10-26 - */ - public String toPString() { - String str = new String(); - for (int n = 0; n < a.size(); n++) { - final BigInteger num = a.elementAt(n); - if (num.compareTo(BigInteger.ZERO) != 0) { - str += " "; - if (num.compareTo(BigInteger.ZERO) > 0 && n > 0) { - str += "+"; - } - str += a.elementAt(n).toString(); - if (n > 0) { - str += "*x"; - if (n > 1) { - str += "^" + n; - } - } - } - } - if (str.length() == 0) { - str = "0"; - } - return str; - } /* toPString */ - - /** - * Simplify the representation. - * Trailing values with zero coefficients (at high powers) are deleted. - */ - protected void simplify() { - int n = a.size() - 1; - if (n >= 0) { - while (a.elementAt(n).compareTo(BigInteger.ZERO) == 0) { - a.removeElementAt(n); - if (--n < 0) { - break; - } - } - } - } /* simplify */ - - /** - * First derivative. - * - * @return The first derivative with respect to the indeterminate variable. - * @since 2008-10-26 - */ - public BigIntegerPoly derive() { - if (a.size() <= 1) { - /* - * derivative of the constant is just zero - */ - return new BigIntegerPoly(); - } else { - final BigIntegerPoly d = new BigIntegerPoly(); - for (int i = 1; i <= degree(); i++) { - final BigInteger c = a.elementAt(i).multiply(new BigInteger("" + i)); - d.set(i - 1, c); - } - return d; - } - } /* derive */ - - /** - * Truncate polynomial degree. - * - * @return The polynomial with all coefficients beyond deg set to zero. - * @since 2010-08-27 - */ - public BigIntegerPoly trunc(final int newdeg) { - final BigIntegerPoly t = new BigIntegerPoly(); - for (int i = 0; i <= newdeg; i++) { - t.set(i, at(i)); - } - t.simplify(); - return t; - } /* trunc */ - - /** - * Inverse Binomial transform. - * - * @param maxdeg - * the maximum polynomial degree of the result - * @return the sequence of coefficients is the inverse binomial transform of - * the original sequence. - * @since 2010-08-29 - */ - public BigIntegerPoly binomialTInv(final int maxdeg) { - final BigIntegerPoly r = new BigIntegerPoly(); - for (int i = 0; i <= maxdeg; i++) { - BigInteger c = BigInteger.ZERO; - for (int j = 0; j <= i && j < a.size(); j++) { - if ((j + i) % 2 != 0) { - c = c.subtract(a.elementAt(j).multiply(BigIntegerMath.binomial(i, j))); - } else { - c = c.add(a.elementAt(j).multiply(BigIntegerMath.binomial(i, j))); - } - } - r.set(i, c); - } - r.simplify(); - return r; - } /* binomialTInv */ - - /** - * Compute the order of the root r. - * - * @return 1 for simple roots, 2 for order 2 etc., 0 if not a root - * @since 2010-08-27 - */ - public int rootDeg(final BigInteger r) { - int o = 0; - BigIntegerPoly d = clone(); - BigInteger roo = d.valueOf(r); - while (roo.compareTo(BigInteger.ZERO) == 0) { - o++; - d = d.derive(); - roo = d.valueOf(r); - } - return o; - } /* rootDeg */ - - /** - * Generate the integer roots of the polynomial. - * - * @return The vector of integer roots, without their multiplicity. - * @since 2010-08-27 - */ - public Vector iroots() { - /* The vector of the roots */ - final Vector res = new Vector<>(); - - /* - * collect the zero - */ - if (a.firstElement().compareTo(BigInteger.ZERO) == 0) { - res.add(BigInteger.ZERO); - } - - /* - * collect the divisors of the constant element (or the reduced - * polynomial) - */ - final int l = ldegree(); - if (a.elementAt(l).compareTo(BigInteger.ZERO) != 0) { - @SuppressWarnings("deprecation") - final Vector cand = BigIntegerMath.divisors(a.elementAt(l).abs()); - - /* check the divisors (both signs) */ - for (int i = 0; i < cand.size(); i++) { - BigInteger roo = valueOf(cand.elementAt(i)); - if (roo.compareTo(BigInteger.ZERO) == 0) { - /* found a root cand[i] */ - res.add(cand.elementAt(i)); - } - roo = valueOf(cand.elementAt(i).negate()); - if (roo.compareTo(BigInteger.ZERO) == 0) { - res.add(cand.elementAt(i).negate()); - } - } - } - return res; - } /* iroots */ - - /** - * Generate the factors which are 2nd degree polynomials. - * - * @return A (potentially empty) vector of factors, without multiplicity. - * Only factors with non-zero absolute coefficient are generated. - * This means the factors are of the form x^2+a*x+b=0 with nonzero - * b. - * @throws Error - * @since 2012-03-01 - */ - protected Vector i2roots() throws Error { - /* - * The vector of the factors to be returned - */ - final Vector res = new Vector<>(); - - if (degree() < 2) { - return res; - } - - final BigInteger bsco = a.firstElement().abs(); - @SuppressWarnings("deprecation") - final Vector b = BigIntegerMath.divisors(bsco); - final BigInteger csco = a.lastElement().abs(); - @SuppressWarnings("deprecation") - final Vector c = BigIntegerMath.divisors(csco); - - /* - * Generate the floating point values of roots. To have some reasonable - * accuracy in the results, add zeros to the integer coefficients, - * scaled - * by the expected division with values of b (which are all <= - * a.firstele). - * Number of decimal digits in bsco by using a log2->log10 rough - * estimate - * and adding 6 safety digits - */ - final RatPoly thisDec = toRatPoly(); - final Vector roo = thisDec.roots(6 + (int) (0.3 * bsco.bitCount())); - - final BigDecimal half = new BigDecimal("0.5"); - - /* - * for each of the roots z try to see whether c*z^2+a*z+b=0 with integer - * a, b and c - * where b is restricted to a signed divisor of the constant - * coefficient. - * Solve z*(c*z+a)=-b or c*z+a = -b/z or -b/z-c*z = some integer a. - */ - for (final BigComplex z : roo) { - for (final BigInteger bco : b) { - for (final BigInteger cco : c) { - /* - * the major reason to avoid the case b=0 is that this would - * require precaution of double counting below. Note that - * this - * case is already covered by using iroots(). - */ - if (bco.signum() != 0) { - for (int sig = -1; sig <= 1; sig += 2) { - final BigInteger bcosig = sig > 0 ? bco : bco.negate(); - /* - * -a = b/z+c*z has real part b*Re(z)/|z|^2+c*Re(z) - * = Re z *( b/|z|^2+c) - */ - BigDecimal negA = BigDecimalMath.add(BigDecimalMath.divideRound(bcosig, z.norm()), cco); - negA = negA.multiply(z.re); - /* - * convert to a with round-to-nearest - */ - final BigInteger a = negA.negate().add(half).toBigInteger(); - - /* - * test the polynomial remainder. if zero, add the - * term - * to the results. - */ - final BigIntegerPoly dtst = new BigIntegerPoly("" + bcosig + "," + a + "," + cco); - try { - final BigIntegerPoly[] rm = divideAndRemainder(dtst); - if (rm[1].isZero()) { - res.add(dtst); - } - } catch (final ArithmeticException ex) {} - } - } - } - } - } - - return res; - } /* i2roots */ - - /** - * Test whether this polynomial value is zero. - * - * @return If this is a polynomial p(x)=0 for all x. - */ - public boolean isZero() { - simplify(); - return a.size() == 0; - } - - /** - * Factorization into integer polynomials. - * The current factorization detects only factors which are polynomials of - * order up to 2. - * - * @return The vector of factors. Factors with higher multiplicity are - * represented by repetition. - * @throws Error - * @since 2012-03-01 - */ - public Vector ifactor() throws Error { - /* - * this ought be entirely rewritten in terms of the LLL algorithm - */ - final Vector fac = new Vector<>(); - - /* collect integer roots (polynomial factors of degree 1) */ - final Vector r = iroots(); - BigIntegerPoly[] res = new BigIntegerPoly[2]; - res[0] = this; - for (final BigInteger i : r) { - final int deg = rootDeg(i); - /* construct the factor x-i */ - final BigIntegerPoly f = new BigIntegerPoly("" + i.negate() + ",1"); - for (int mu = 0; mu < deg; mu++) { - fac.add(f); - res = res[0].divideAndRemainder(f); - } - } - - /* - * collect factors which are polynomials of degree 2 - */ - final Vector pol2 = i2roots(); - for (final BigIntegerPoly i : pol2) { - /* - * the internal loop catches cases with higher - * powers of individual polynomials (of actual degree 2 or 4...) - */ - while (res[0].degree() >= 2) { - try { - final BigIntegerPoly[] dtst = res[0].divideAndRemainder(i); - if (dtst[1].isZero()) { - fac.add(i); - res = dtst; - } else { - break; - } - } catch (final ArithmeticException ex) { - break; - } - } - } - - /* - * add remaining factor, if not equal to 1 - */ - if (res[0].degree() > 0 || res[0].a.firstElement().compareTo(BigInteger.ONE) != 0) { - fac.add(res[0]); - } - return fac; - } /* ifactor */ - -} /* BigIntegerPoly */ diff --git a/core/src/main/java/org/nevec/rjm/BigSurd.java b/core/src/main/java/org/nevec/rjm/BigSurd.java deleted file mode 100644 index 7e60fa57..00000000 --- a/core/src/main/java/org/nevec/rjm/BigSurd.java +++ /dev/null @@ -1,567 +0,0 @@ -package org.nevec.rjm; - -import java.math.BigDecimal; -import java.math.BigInteger; -import java.math.MathContext; -import java.security.ProviderException; - -import it.cavallium.warppi.util.Error; -import it.cavallium.warppi.util.Utils; - -/** - * Square roots on the real line. - * These represent numbers which are a product of a (signed) fraction by - * a square root of a non-negative fraction. - * This might be extended to values on the imaginary axis by allowing negative - * values underneath the square root, but this is not yet implemented. - * - * @since 2011-02-12 - * @author Richard J. Mathar - */ -public class BigSurd implements Cloneable, Comparable { - /** - * The value of zero. - */ - static public BigSurd ZERO = new BigSurd(); - - /** - * The value of one. - */ - static public BigSurd ONE = new BigSurd(Rational.ONE, Rational.ONE); - /** - * Prefactor - */ - Rational pref; - - /** - * The number underneath the square root, always non-negative. - * The mathematical object has the value pref*sqrt(disc). - */ - Rational disc; - - /** - * Default ctor, which represents the zero. - * - * @since 2011-02-12 - */ - public BigSurd() { - pref = Rational.ZERO; - disc = Rational.ZERO; - } - - /** - * ctor given the prefactor and the basis of the root. - * This creates an object of value a*sqrt(b). - * - * @param a - * the prefactor. - * @param b - * the discriminant. - * @since 2011-02-12 - */ - public BigSurd(final Rational a, final Rational b) { - pref = a; - /* - * reject attempts to use a negative b - */ - if (b.signum() < 0) { - throw new ProviderException("Not implemented: imaginary surds"); - } - disc = b; - try { - normalize(); - normalizeG(); - } catch (final Error e) { - e.printStackTrace(); - } - } - - /** - * ctor given the numerator and denominator of the root. - * This creates an object of value sqrt(a/b). - * - * @param a - * the numerator - * @param b - * the denominator. - * @since 2011-02-12 - */ - public BigSurd(final int a, final int b) { - this(Rational.ONE, new Rational(a, b)); - } - - /** - * ctor given the value under the root. - * This creates an object of value sqrt(a). - * - * @param a - * the discriminant. - * @since 2011-02-12 - */ - public BigSurd(final BigInteger a) { - this(Rational.ONE, new Rational(a, BigInteger.ONE)); - } - - public BigSurd(final Rational a) { - this(Rational.ONE, a); - } - - /** - * Create a deep copy. - * - * @since 2011-02-12 - */ - @Override - public BigSurd clone() { - final Rational fclon = pref.clone(); - final Rational dclon = disc.clone(); - /* - * the main intent here is to bypass any attempt to reduce the - * discriminant - * by figuring out the square-free part in normalize(), which has - * already done - * in the current copy of the number. - */ - final BigSurd cl = new BigSurd(); - cl.pref = fclon; - cl.disc = dclon; - return cl; - } /* BigSurd.clone */ - - /** - * Add two surds of compatible discriminant. - * - * @param val - * The value to be added to this. - */ - - public BigSurdVec add(final BigSurd val) { - // zero plus somethings yields something - if (signum() == 0) { - return new BigSurdVec(val); - } else if (val.signum() == 0) { - return new BigSurdVec(this); - } else { - // let the ctor of BigSurdVec to the work - return new BigSurdVec(this, val); - } - } /* BigSurd.add */ - - /** - * Multiply by another square root. - * - * @param val - * a second number of this type. - * @return the product of this with the val. - * @since 2011-02-12 - */ - public BigSurd multiply(final BigSurd val) { - return new BigSurd(pref.multiply(val.pref), disc.multiply(val.disc)); - } /* BigSurd.multiply */ - - /** - * Multiply by a rational number. - * - * @param val - * the factor. - * @return the product of this with the val. - * @since 2011-02-15 - */ - public BigSurd multiply(final Rational val) { - return new BigSurd(pref.multiply(val), disc); - } /* BigSurd.multiply */ - - /** - * Multiply by a BigInteger. - * - * @param val - * a second number. - * @return the product of this with the value. - * @since 2011-02-12 - */ - public BigSurd multiply(final BigInteger val) { - return new BigSurd(pref.multiply(val), disc); - } /* BigSurd.multiply */ - - /** - * Multiply by an integer. - * - * @param val - * a second number. - * @return the product of this with the value. - * @since 2011-02-12 - */ - public BigSurd multiply(final int val) { - final BigInteger tmp = new BigInteger("" + val); - return multiply(tmp); - } /* BigSurd.multiply */ - - /** - * Compute the square. - * - * @return this value squared. - * @since 2011-02-12 - */ - public Rational sqr() { - Rational res = pref.pow(2); - res = res.multiply(disc); - return res; - } /* BigSurd.sqr */ - - /** - * Divide by another square root. - * - * @param val - * A second number of this type. - * @return The value of this/val - * @throws Error - * @since 2011-02-12 - */ - public BigSurd divide(final BigSurd val) throws Error { - if (val.signum() == 0) { - throw new ArithmeticException("Dividing " + toFancyString() + " through zero."); - } - return new BigSurd(pref.divide(val.pref), disc.divide(val.disc)); - } /* BigSurd.divide */ - - private String toFancyString() { - final BigSurd bs = this; - final BigInteger denominator = pref.b; - String s = ""; - if (denominator.compareTo(BigInteger.ONE) != 0) { - s += "("; - } - if (bs.isBigInteger()) { - s += bs.BigDecimalValue(new MathContext(Utils.scale, Utils.scaleMode2)).toBigInteger().toString(); - } else if (bs.isRational()) { - s += bs.toRational().toString(); - } else { - final BigInteger numerator = bs.pref.a; - if (numerator.compareTo(BigInteger.ONE) != 0) { - s += numerator.toString(); - s += "*"; - s += "("; - } - s += "2√"; - if (bs.disc.isInteger()) { - s += bs.disc.toString(); - } else { - s += "(" + bs.disc.toString() + ")"; - } - if (numerator.compareTo(BigInteger.ONE) != 0) { - s += ")"; - } - } - return s; - } - - /** - * Divide by an integer. - * - * @param val - * a second number. - * @return the value of this/val - * @throws Error - * @since 2011-02-12 - */ - public BigSurd divide(final BigInteger val) throws Error { - if (val.signum() == 0) { - throw new ArithmeticException("Dividing " + toFancyString() + " through zero."); - } - return new BigSurd(pref.divide(val), disc); - } /* BigSurd.divide */ - - /** - * Divide by an integer. - * - * @param val - * A second number. - * @return The value of this/val - * @throws Error - * @since 2011-02-12 - */ - public BigSurd divide(final int val) throws Error { - if (val == 0) { - throw new ArithmeticException("Dividing " + toFancyString() + " through zero."); - } - return new BigSurd(pref.divide(val), disc); - } /* BigSurd.divide */ - - /** - * Compute the negative. - * - * @return -this. - * @since 2011-02-12 - */ - public BigSurd negate() { - /* - * This is trying to be quick, avoiding normalize(), by toggling - * the sign in a clone() - */ - final BigSurd n = clone(); - n.pref = n.pref.negate(); - return n; - } /* BigSurd.negate */ - - /** - * Absolute value. - * - * @return The absolute (non-negative) value of this. - * @since 2011-02-12 - */ - public BigSurd abs() { - return new BigSurd(pref.abs(), disc); - } - - /** - * Compares the value of this with another constant. - * - * @param val - * the other constant to compare with - * @return -1, 0 or 1 if this number is numerically less than, equal to, - * or greater than val. - * @since 2011-02-12 - */ - @Override - public int compareTo(final BigSurd val) { - /* - * Since we keep the discriminant positive, the rough estimate - * comes from comparing the signs of the prefactors. - */ - final int sig = signum(); - final int sigv = val.signum(); - if (sig < 0 && sigv >= 0) { - return -1; - } - if (sig > 0 && sigv <= 0) { - return 1; - } - if (sig == 0 && sigv == 0) { - return 0; - } - if (sig == 0 && sigv > 0) { - return -1; - } - if (sig == 0 && sigv < 0) { - return 1; - } - - /* - * Work out the cases of equal sign. Compare absolute values by - * comparison - * of the squares which is forwarded to the comparison of the Rational - * class. - */ - final Rational this2 = sqr(); - final Rational val2 = val.sqr(); - final int c = this2.compareTo(val2); - if (c == 0) { - return 0; - } else if (sig > 0 && c > 0 || sig < 0 && c < 0) { - return 1; - } else { - return -1; - } - } /* BigSurd.compareTo */ - - /** - * Return a string in the format (number/denom)*()^(1/2). - * If the discriminant equals 1, print just the prefactor. - * - * @return the human-readable version in base 10 - * @since 2011-02-12 - */ - @Override - public String toString() { - if (disc.compareTo(Rational.ONE) != 0 && disc.compareTo(Rational.ZERO) != 0) { - return "(" + pref.toString() + ")*(" + disc.toString() + ")^(1/2)"; - } else { - return pref.toString(); - } - } /* BigSurd.toString */ - - /** - * Return a double value representation. - * - * @return The value with double precision. - * @since 2011-02-12 - */ - public double doubleValue() { - /* - * First compute the square to prevent overflows if the two pieces of - * the prefactor and the discriminant are of very different magnitude. - */ - final Rational p2 = pref.pow(2).multiply(disc); - System.out.println("dv sq " + p2.toString()); - final double res = p2.doubleValue(); - System.out.println("dv sq " + res); - return pref.signum() >= 0 ? Math.sqrt(res) : -Math.sqrt(res); - } /* BigSurd.doubleValue */ - - /** - * Return a float value representation. - * - * @return The value with single precision. - * @since 2011-02-12 - */ - public float floatValue() { - return (float) doubleValue(); - } /* BigSurd.floatValue */ - - /** - * True if the value is integer. - * Equivalent to the indication whether a conversion to an integer - * can be exact. - * - * @since 2011-02-12 - */ - public boolean isBigInteger() { - return pref.isBigInteger() && (disc.signum() == 0 || disc.compareTo(Rational.ONE) == 0); - } /* BigSurd.isBigInteger */ - - /** - * True if the value is rational. - * Equivalent to the indication whether a conversion to a Rational can be - * exact. - * - * @since 2011-02-12 - */ - public boolean isRational() { - return disc.signum() == 0 || disc.compareTo(Rational.ONE) == 0; - } /* BigSurd.isRational */ - - /** - * Convert to a rational value if possible - * - * @since 2012-02-15 - */ - public Rational toRational() { - if (isRational()) { - return pref; - } else { - throw new ArithmeticException("Undefined conversion " + toFancyString() + " to Rational."); - } - } /* BigSurd.toRational */ - - /** - * The sign: 1 if the number is >0, 0 if ==0, -1 if <0 - * - * @return the signum of the value. - * @since 2011-02-12 - */ - public int signum() { - /* - * Since the disc is kept positive, this is the same - * as the sign of the prefactor. This works because a zero discriminant - * is always copied over to the prefactor, not hidden. - */ - return pref.signum(); - } /* BigSurd.signum */ - - /** - * Normalize to squarefree discriminant. - * - * @throws Error - * @since 2011-02-12 - */ - protected void normalize() throws Error { - /* - * Move squares out of the numerator and denominator of the discriminant - */ - if (disc.signum() != 0) { - /* - * square-free part of the numerator: numer = numC*some^2 - */ - final BigInteger numC = BigIntegerMath.core(disc.numer()); - /* - * extract the perfect square of the numerator - */ - BigInteger sq = disc.numer().divide(numC); - /* - * extract the associated square root - */ - BigInteger sqf = BigIntegerMath.isqrt(sq); - - /* - * move sqf over to the pre-factor - */ - pref = pref.multiply(sqf); - - final BigInteger denC = BigIntegerMath.core(disc.denom()); - sq = disc.denom().divide(denC); - sqf = BigIntegerMath.isqrt(sq); - pref = pref.divide(sqf); - - disc = new Rational(numC, denC); - } else { - pref = Rational.ZERO; - } - } /* BigSurd.normalize */ - - /** - * Normalize to coprime numerator and denominator in prefactor and - * discriminant - * - * @throws Error - * @since 2011-02-12 - */ - protected void normalizeG() throws Error { - /* - * Is there a common factor between the numerator of the prefactor - * and the denominator of the discriminant ? - */ - BigInteger d = pref.numer().abs().gcd(disc.denom()); - if (d.compareTo(BigInteger.ONE) > 0) { - pref = pref.divide(d); - /* - * instead of multiplying with the square of d, using two steps - * offers a change to recognize the common factor.. - */ - disc = disc.multiply(d); - disc = disc.multiply(d); - } - /* - * Is there a common factor between the denominator of the prefactor - * and the numerator of the discriminant ? - */ - d = pref.denom().gcd(disc.numer()); - if (d.compareTo(BigInteger.ONE) > 0) { - pref = pref.multiply(d); - /* - * instead of dividing through the square of d, using two steps - * offers a change to recognize the common factor.. - */ - disc = disc.divide(d); - disc = disc.divide(d); - } - } /* BigSurd.normalizeG */ - - /** - * Return the approximate floating point representation. - * - * @param mc - * Description of the accuracy needed. - * @return A representation with digits valid as described by mc - * @since 2012-02-15 - */ - public BigDecimal BigDecimalValue(final MathContext mc) { - /* - * the relative error of the result equals the relative error of the - * prefactor plus half of the relative error of the discriminant. - * So adding 3 digits temporarily is sufficient. - */ - final MathContext locmc = new MathContext(mc.getPrecision() + 3, mc.getRoundingMode()); - /* - * first the square root of the discriminant - */ - final BigDecimal sqrdis = BigDecimalMath.sqrt(disc.BigDecimalValue(locmc), locmc); - /* - * Then multiply by the prefactor. If sqrdis is a terminating decimal - * fraction, - * we prevent early truncation of the result by truncating later. - */ - final BigDecimal res = sqrdis.multiply(pref.BigDecimalValue(mc)); - return BigDecimalMath.scalePrec(res, mc); - } /* BigDecimalValue */ - -} /* BigSurd */ diff --git a/core/src/main/java/org/nevec/rjm/BigSurdVec.java b/core/src/main/java/org/nevec/rjm/BigSurdVec.java deleted file mode 100644 index ff7c7be0..00000000 --- a/core/src/main/java/org/nevec/rjm/BigSurdVec.java +++ /dev/null @@ -1,650 +0,0 @@ -package org.nevec.rjm; - -import java.math.BigDecimal; -import java.math.BigInteger; -import java.math.MathContext; -import java.util.Vector; - -import it.cavallium.warppi.util.Error; -import it.cavallium.warppi.util.Utils; - -/** - * A BigSurdVec represents an algebraic sum or differences of values which each - * term an instance of BigSurd. This mainly means that sums or differences of - * two BigSurd (or two BigSurdVec) can be represented (exactly) as a BigSurdVec. - * - * @since 2012-02-15 - * @author Richard J. Mathar - */ -public class BigSurdVec implements Comparable { - /** - * The value of zero. - */ - static public BigSurdVec ZERO = new BigSurdVec(); - - /** - * The value of one. - */ - static public BigSurdVec ONE = new BigSurdVec(BigSurd.ONE); - - /** - * Internal representation: Each term as a single BigSurd. The value zero is - * represented by an empty vector. - */ - Vector terms; - - /** - * Default ctor, which represents the zero. - * - * @since 2012-02-15 - */ - public BigSurdVec() { - terms = new Vector<>(); - } /* ctor */ - - /** - * ctor given the value of a BigSurd. - * - * @param a - * The value to be represented by this vector. - * @since 2012-02-15 - */ - public BigSurdVec(final BigSurd a) { - terms = new Vector<>(1); - terms.add(a); - } /* ctor */ - - /** - * ctor given two values, which (when added) represent this number a+b. - * - * @param a - * The value to be represented by the first term of the vector. - * @param b - * The value to be represented by the second term of the vector. - * @since 2012-02-15 - */ - public BigSurdVec(final BigSurd a, final BigSurd b) { - terms = new Vector<>(2); - terms.add(a); - terms.add(b); - try { - normalize(); - } catch (final Error e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } /* ctor */ - - /** - * Combine terms that can be written as a single surd. This unites for - * example the terms sqrt(90) and sqrt(10) to 4*sqrt(10). - * - * @throws Error - * - * @since 2012-02-15 - */ - protected void normalize() throws Error { - /* - * nothing to be done if at most one term - */ - if (terms.size() <= 1) { - return; - } - - final Vector newter = new Vector<>(); - newter.add(terms.firstElement()); - /* - * add j-th element to the existing vector and combine were possible - */ - for (int j = 1; j < terms.size(); j++) { - final BigSurd todo = terms.elementAt(j); - boolean merged = false; - for (int ex = 0; ex < newter.size(); ex++) { - BigSurd v = newter.elementAt(ex); - /* - * try to merge terms[j] and newter[ex]. todo = r * v with r a - * rational number is needed. Replaces v with v+todo = v*(1+r) - * if this reduction works. - */ - final BigSurd r = todo.divide(v); - if (r.isRational()) { - /* compute r+1 */ - final Rational newpref = r.toRational().add(1); - /* - * eliminate accidental zeros; overwrite with v*(1+r). - */ - if (newpref.compareTo(Rational.ZERO) == 0) { - newter.removeElementAt(ex); - } else { - v = v.multiply(newpref); - newter.setElementAt(v, ex); - } - merged = true; - break; - } - } - /* - * append if none of the existing elements matched - */ - if (!merged) { - newter.add(todo); - } - } - - /* overwrite old version */ - terms = newter; - } /* normalize */ - - /** - * Compare algebraic value with oth. Returns -1, 0 or +1 depending on - * whether this is smaller, equal to or larger than oth. - * - * @param oth - * The value with which this is to be compared. - * @return 0 or +-1. - * @since 2012-02-15 - */ - @Override - public int compareTo(final BigSurdVec oth) { - BigSurdVec diff; - try { - diff = this.subtract(oth); - return diff.signum(); - } catch (final Error e) { - // TODO Auto-generated catch block - e.printStackTrace(); - return 0; - } - } /* compareTo */ - - /** - * Sign function. Returns -1, 0 or +1 depending on whether this is smaller, - * equal to or larger than zero. - * - * @return 0 or +-1. - * @throws Error - * @since 2012-02-15 - */ - public int signum() throws Error { - /* - * the case of zero is unique, because no (reduced) vector of surds - * other than the one element 0 itself can add/subtract to zero. - */ - if (terms.size() == 0) { - return 0; - } - - /* - * if there is one term: forward to the signum function of BigSurd - */ - if (terms.size() == 1) { - return terms.firstElement().signum(); - } - - /* - * if all terms have a common sign: take that one offsig is the index of - * the first "offending" term in the sense that its sign doese not agree - * with the term[0]. - */ - final int sig0 = terms.elementAt(0).signum(); - int offsig = 1; - for (; offsig < terms.size(); offsig++) { - if (terms.elementAt(offsig).signum() != sig0) { - break; - } - } - if (offsig >= terms.size()) { - return sig0; - } - - /* - * if there are two terms (now known to have different sign): forward to - * the comparison of the two elements as BigSurds - */ - if (terms.size() == 2) { - return terms.elementAt(0).compareTo(terms.elementAt(1).negate()); - } - - /* - * if there are three terms, move the one with the offending sign to the - * other side and square both sides (which looses the sign) to remove - * all but one surds. The difference of the squared sides contains at - * most two terms, which reduces to the case above. t(0)+t(offbar) <> - * -t(offs) - */ - if (terms.size() == 3) { - BigSurdVec lhs; - if (offsig == 2) { - lhs = new BigSurdVec(terms.elementAt(0), terms.elementAt(1)); - } else { - lhs = new BigSurdVec(terms.elementAt(0), terms.elementAt(2)); - } - lhs = lhs.sqr(); - /* - * Strange line: this line isn't used, but it's present in this - * code! - * - * - * - * BigSurd rhs = new BigSurd(terms.elementAt(offsig).sqr(), - * Rational.ONE); - * - * - * - */ - if (lhs.compareTo(lhs) > 0) { - /* - * dominating sign was t(0)+t(offbar) - */ - return terms.elementAt(0).signum(); - } else { - return terms.elementAt(offsig).signum(); - } - } - - /* - * for a larger number of terms: take a floating point representation - * with a small but correct number of digits, and resume with the sign - * of that one. - */ - return floatValue() > 0. ? 1 : -1; - - } /* signum */ - - /** - * Construct an approximate floating point representation - * - * @param mc - * The intended accuracy of the result. - * @return A truncated version with the precision described by mc - */ - public BigDecimal BigDecimalValue(final MathContext mc) { - /* - * simple cases with one term forwarded to the BigSurd class - */ - if (terms.size() == 0) { - return BigDecimal.ZERO; - } else if (terms.size() == 1) { - return terms.firstElement().BigDecimalValue(mc); - } - - /* - * To reduce cancellation errors, loop over increasing local precision - * until we are stable to the required result. Keep the old (less - * precise) estimate in res[0], and the newer, more precise in res[1]. - */ - final BigDecimal[] res = new BigDecimal[2]; - res[0] = BigDecimal.ZERO; - for (int addpr = 1;; addpr += 3) { - final MathContext locmc = new MathContext(mc.getPrecision() + addpr, mc.getRoundingMode()); - res[1] = BigDecimal.ZERO; - for (final BigSurd j : terms) { - res[1] = BigDecimalMath.addRound(res[1], j.BigDecimalValue(locmc)); - } - if (addpr > 1) { - final BigDecimal err = res[1].subtract(res[0]).abs(); - final int prec = BigDecimalMath.err2prec(res[1], err); - if (prec > mc.getPrecision()) { - break; - } - } - res[0] = res[1]; - } - return BigDecimalMath.scalePrec(res[1], mc); - - } /* BigDecimalValue */ - - /** - * Construct an approximate floating point representation - * - * @return A truncated version with the precision described by mc - */ - public double doubleValue() { - final BigDecimal bd = BigDecimalValue(MathContext.DECIMAL128); - return bd.doubleValue(); - } /* doubleValue */ - - /** - * Construct an approximate floating point representation - * - * @return A truncated version with the precision described by mc - */ - public double floatValue() { - final BigDecimal bd = BigDecimalValue(MathContext.DECIMAL64); - return bd.floatValue(); - } /* floatValue */ - - /** - * Add two vectors algebraically. - * - * @param val - * The value to be added to this. - * @return The new value representing this+val. - * @throws Error - */ - public BigSurdVec add(final BigSurdVec val) throws Error { - final BigSurdVec sum = new BigSurdVec(); - /* - * concatenate the vectors and eliminate common overlaps - */ - for (final BigSurd term : terms) { - if (term.compareTo(BigSurd.ZERO) != 0) { - sum.terms.add(term); - } - } - for (final BigSurd term : val.terms) { - if (term.compareTo(BigSurd.ZERO) != 0) { - sum.terms.add(term); - } - } - sum.normalize(); - return sum; - } /* add */ - - /** - * Add two vectors algebraically. - * - * @param val - * The value to be added to this. - * @return The new value representing this+val. - * @throws Error - */ - public BigSurdVec add(final BigSurd val) throws Error { - final BigSurdVec sum = new BigSurdVec(); - /* - * concatenate the vectors and eliminate common overlaps - */ - sum.terms.addAll(terms); - sum.terms.add(val); - sum.normalize(); - return sum; - } /* add */ - - /** - * Subtract another number. - * - * @param val - * The value to be subtracted from this. - * @return The new value representing this-val. - * @throws Error - */ - public BigSurdVec subtract(final BigSurdVec val) throws Error { - final BigSurdVec sum = new BigSurdVec(); - /* - * concatenate the vectors and eliminate common overlaps - */ - sum.terms.addAll(terms); - for (final BigSurd s : val.terms) { - sum.terms.add(s.negate()); - } - sum.normalize(); - return sum; - } /* subtract */ - - /** - * Subtract another number. - * - * @param val - * The value to be subtracted from this. - * @return The new value representing this-val. - * @throws Error - */ - public BigSurdVec subtract(final BigSurd val) throws Error { - final BigSurdVec sum = new BigSurdVec(); - /* - * concatenate the vectors and eliminate common overlaps - */ - sum.terms.addAll(terms); - sum.terms.add(val.negate()); - sum.normalize(); - return sum; - } /* subtract */ - - /** - * Compute the negative. - * - * @return -this. - * @since 2012-02-15 - */ - public BigSurdVec negate() { - /* - * accumulate the negated elements of term one by one - */ - final BigSurdVec resul = new BigSurdVec(); - for (final BigSurd s : terms) { - resul.terms.add(s.negate()); - } - /* - * no normalization step here, because the negation of all terms does - * not introduce new common factors - */ - return resul; - } /* negate */ - - /** - * Compute the square. - * - * @return this value squared. - * @throws Error - * @since 2012-02-15 - */ - public BigSurdVec sqr() throws Error { - /* - * Binomial expansion. First the sum of the terms squared, then 2 times - * the mixed products. - */ - final BigSurdVec resul = new BigSurdVec(); - for (int i = 0; i < terms.size(); i++) { - resul.terms.add(new BigSurd(terms.elementAt(i).sqr(), Rational.ONE)); - } - for (int i = 0; i < terms.size() - 1; i++) { - for (int j = i + 1; j < terms.size(); j++) { - resul.terms.add(terms.elementAt(i).multiply(terms.elementAt(j)).multiply(2)); - } - } - resul.normalize(); - return resul; - } /* sqr */ - - /** - * Multiply by another square root. - * - * @param val - * a second number of this type. - * @return the product of this with the val. - * @throws Error - * @since 2011-02-12 - */ - public BigSurdVec multiply(final BigSurd val) throws Error { - final BigSurdVec resul = new BigSurdVec(); - for (final BigSurd s : terms) { - resul.terms.add(s.multiply(val)); - } - resul.normalize(); - return resul; - } /* multiply */ - - public BigSurdVec multiply(final BigSurdVec val) throws Error { - BigSurdVec resul = new BigSurdVec(); - for (final BigSurd s : terms) { - resul.terms.add(s); - } - for (final BigSurd s : val.terms) { - resul = resul.multiply(s); - } - return resul; - } /* multiply */ - - public BigSurdVec divide(final BigSurd val) throws Error { - final BigSurdVec resul = new BigSurdVec(); - for (final BigSurd s : terms) { - resul.terms.add(s.divide(val)); - } - resul.normalize(); - return resul; - } /* multiply */ - - public BigSurdVec divide(final BigSurdVec val) throws Error { - BigSurdVec resul = new BigSurdVec(); - resul.terms = terms; - for (final BigSurd s : val.terms) { - resul = resul.divide(s); - } - return resul; - } /* divide */ - - /** - * True if the value is rational. Equivalent to the indication whether a - * conversion to a Rational can be exact. - * - * @since 2011-02-12 - */ - public boolean isRational() { - boolean val = false; - for (final BigSurd s : terms) { - val = s.isRational(); - if (val == false) { - break; - } - } - return val; - } /* BigSurdVec.isRational */ - - /** - * True if the value is BigInteger. Equivalent to the indication whether a - * conversion to a BigInteger can be exact. - * - * @since 2011-02-12 - */ - public boolean isBigInteger() { - boolean val = false; - for (final BigSurd s : terms) { - val = s.isBigInteger(); - if (val == false) { - break; - } - } - return val; - } /* BigSurdVec.isRational */ - - /** - * Convert to a rational value if possible - * - * @since 2012-02-15 - */ - public Rational toRational() { - Rational rat = Rational.ZERO; - if (isRational() == false) { - throw new ArithmeticException("Undefined conversion " + toString() + " to Rational."); - } - for (final BigSurd s : terms) { - rat = rat.add(s.pref); - } - return rat; - } /* BigSurd.toRational */ - - /** - * Convert to a BigInteger value if possible - * - * @since 2012-02-15 - */ - public BigInteger toBigInteger() { - BigDecimal tmp = BigDecimal.ZERO.setScale(Utils.scale, Utils.scaleMode); - if (isBigInteger() == false) { - throw new ArithmeticException("Undefined conversion " + toString() + " to Rational."); - } - for (final BigSurd s : terms) { - tmp = BigDecimalMath.addRound(tmp, s.pref.BigDecimalValue(new MathContext(Utils.scale, Utils.scaleMode2))); - } - return tmp.toBigInteger(); - } /* BigSurd.toRational */ - - /** - * Convert to a BigDecimal value if possible - * - * @since 2012-02-15 - */ - public BigDecimal toBigDecimal() { - BigDecimal tmp = BigDecimal.ZERO.setScale(Utils.scale, Utils.scaleMode); - for (final BigSurd s : terms) { - tmp = BigDecimalMath.addRound(tmp, s.BigDecimalValue(new MathContext(Utils.scale, Utils.scaleMode2))); - } - return tmp; - } /* BigSurd.toBigDecimal */ - - /** - * Return a string in the format (number/denom)*()^(1/2). If the - * discriminant equals 1, print just the prefactor. - * - * @return the human-readable version in base 10 - * @since 2012-02-16 - */ - @Override - public String toString() { - /* - * simple cases with one term forwarded to the BigSurd class - */ - if (terms.size() == 0) { - return new String("0"); - } else { - String s = new String(); - for (int t = 0; t < terms.size(); t++) { - final BigSurd bs = terms.elementAt(t); - if (bs.signum() > 0) { - s += "+"; - } - s += bs.toString(); - } - return s; - } - } /* toString */ - - public String toFancyString() { - if (terms.size() == 0) { - return new String("0"); - } else { - BigInteger denominator = BigInteger.ONE; - for (int i = 0; i < terms.size(); i++) { - denominator = denominator.multiply(terms.elementAt(i).pref.b); - } - String s = ""; - if (denominator.compareTo(BigInteger.ONE) != 0) { - s += "("; - } - for (int t = 0; t < terms.size(); t++) { - final BigSurd bs = terms.elementAt(t); - if (bs.signum() > 0 && t > 0) { - s += "+"; - } - if (bs.isBigInteger()) { - s += bs.BigDecimalValue(new MathContext(Utils.scale, Utils.scaleMode2)).toBigInteger().toString(); - } else if (bs.isRational()) { - s += bs.toRational().toString(); - } else { - final BigInteger numerator = bs.pref.multiply(denominator).numer(); - if (numerator.compareTo(BigInteger.ONE) != 0) { - s += numerator.toString(); - s += "*"; - // s += "("; Radice quadrata. non servono le parentesi. - } - s += "Ⓐ"; - if (bs.disc.isInteger()) { - s += bs.disc.toString(); - } else { - s += "(" + bs.disc.toString() + ")"; - } - if (numerator.compareTo(BigInteger.ONE) != 0) { - // s += ")"; Radice quadrata. non servono le parentesi. - } - } - } - if (denominator.compareTo(BigInteger.ONE) != 0) { - s += ")"; - s += "/"; - s += denominator; - } - return s; - } - } - -} /* BigSurdVec */ diff --git a/core/src/main/java/org/nevec/rjm/Euler.java b/core/src/main/java/org/nevec/rjm/Euler.java deleted file mode 100644 index c29a09cb..00000000 --- a/core/src/main/java/org/nevec/rjm/Euler.java +++ /dev/null @@ -1,73 +0,0 @@ -package org.nevec.rjm; - -import java.math.BigInteger; -import java.util.Vector; - -/** - * Euler numbers - * - * @see A000364 in the OEIS. - * @since 2008-10-30 - * @author Richard J. Mathar - */ -public class Euler { - /* - * The list of all Euler numbers as a vector, n=0,2,4,.... - */ - static protected Vector a = new Vector<>(); - - /** - * Ctor(). Fill the hash list initially with E_0 to E_3. - */ - public Euler() { - if (Euler.a.size() == 0) { - Euler.a.add(BigInteger.ONE); - Euler.a.add(BigInteger.ONE); - Euler.a.add(new BigInteger("5")); - Euler.a.add(new BigInteger("61")); - } - } - - /** - * Compute a coefficient in the internal table. - * - * @param n - * the zero-based index of the coefficient. n=0 for the E_0 term. - */ - protected void set(final int n) { - while (n >= Euler.a.size()) { - BigInteger val = BigInteger.ZERO; - boolean sigPos = true; - final int thisn = Euler.a.size(); - for (int i = thisn - 1; i > 0; i--) { - BigInteger f = new BigInteger("" + Euler.a.elementAt(i).toString()); - f = f.multiply(BigIntegerMath.binomial(2 * thisn, 2 * i)); - if (sigPos) { - val = val.add(f); - } else { - val = val.subtract(f); - } - sigPos = !sigPos; - } - if (thisn % 2 == 0) { - val = val.subtract(BigInteger.ONE); - } else { - val = val.add(BigInteger.ONE); - } - Euler.a.add(val); - } - } - - /** - * The Euler number at the index provided. - * - * @param n - * the index, non-negative. - * @return the E_0=E_1=1 , E_2=5, E_3=61 etc - */ - public BigInteger at(final int n) { - set(n); - return Euler.a.elementAt(n); - } - -} /* Euler */ diff --git a/core/src/main/java/org/nevec/rjm/EulerPhi.java b/core/src/main/java/org/nevec/rjm/EulerPhi.java deleted file mode 100644 index 639b34b1..00000000 --- a/core/src/main/java/org/nevec/rjm/EulerPhi.java +++ /dev/null @@ -1,66 +0,0 @@ -package org.nevec.rjm; - -import java.math.BigInteger; - -/** - * Euler totient function. - * - * @see A000010 in the OEIS. - * @since 2008-10-14 - * @since 2012-03-04 Adapted to new Ifactor representation. - * @author Richard J. Mathar - */ -public class EulerPhi { - /** - * Default constructor. - * Does nothing(). - */ - public EulerPhi() {} - - /** - * Compute phi(n). - * - * @param n - * The positive argument of the function. - * @return phi(n) - */ - public BigInteger at(final int n) { - return at(new BigInteger("" + n)); - } /* at */ - - /** - * Compute phi(n). - * - * @param n - * The positive argument of the function. - * @return phi(n) - */ - public BigInteger at(final BigInteger n) { - if (n.compareTo(BigInteger.ZERO) <= 0) { - throw new ArithmeticException("negative argument " + n + " of EulerPhi"); - } - final Ifactor prFact = new Ifactor(n); - BigInteger phi = n; - if (n.compareTo(BigInteger.ONE) > 0) { - for (int i = 0; i < prFact.primeexp.size(); i += 2) { - final BigInteger p = new BigInteger(prFact.primeexp.elementAt(i).toString()); - final BigInteger p_1 = p.subtract(BigInteger.ONE); - phi = phi.multiply(p_1).divide(p); - } - } - return phi; - } /* at */ - - /** - * Test program. - * It takes one argument n and prints the value phi(n).
- * java -cp . org.nevec.rjm.EulerPhi n
- * - * @since 2006-08-14 - */ - public static void main(final String[] args) throws ArithmeticException { - final EulerPhi a = new EulerPhi(); - final int n = new Integer(args[0]).intValue(); - System.out.println("phi(" + n + ") = " + a.at(n)); - } -} /* EulerPhi */ diff --git a/core/src/main/java/org/nevec/rjm/Factorial.java b/core/src/main/java/org/nevec/rjm/Factorial.java deleted file mode 100644 index 175bab0b..00000000 --- a/core/src/main/java/org/nevec/rjm/Factorial.java +++ /dev/null @@ -1,79 +0,0 @@ -package org.nevec.rjm; - -import java.math.BigInteger; -import java.util.Vector; - -/** - * Factorials. - * - * @since 2006-06-25 - * @since 2012-02-15 Storage of the values based on Ifactor, not BigInteger. - * @author Richard J. Mathar - */ -public class Factorial { - /** - * The list of all factorials as a vector. - */ - static Vector a = new Vector<>(); - - /** - * ctor(). - * Initialize the vector of the factorials with 0!=1 and 1!=1. - */ - public Factorial() { - if (Factorial.a.size() == 0) { - Factorial.a.add(Ifactor.ONE); - Factorial.a.add(Ifactor.ONE); - } - } /* ctor */ - - /** - * Compute the factorial of the non-negative integer. - * - * @param n - * the argument to the factorial, non-negative. - * @return the factorial of n. - */ - public BigInteger at(final int n) { - /* - * extend the internal list if needed. - */ - growto(n); - return Factorial.a.elementAt(n).n; - } /* at */ - - /** - * Compute the factorial of the non-negative integer. - * - * @param n - * the argument to the factorial, non-negative. - * @return the factorial of n. - */ - public Ifactor toIfactor(final int n) { - /* - * extend the internal list if needed. - */ - growto(n); - return Factorial.a.elementAt(n); - } /* at */ - - /** - * Extend the internal table to cover up to n! - * - * @param n - * The maximum factorial to be supported. - * @since 2012-02-15 - */ - private void growto(final int n) { - /* - * extend the internal list if needed. Size to be 2 for n<=1, 3 for n<=2 - * etc. - */ - while (Factorial.a.size() <= n) { - final int lastn = Factorial.a.size() - 1; - final Ifactor nextn = new Ifactor(lastn + 1); - Factorial.a.add(Factorial.a.elementAt(lastn).multiply(nextn)); - } - } /* growto */ - -} /* Factorial */ diff --git a/core/src/main/java/org/nevec/rjm/Harmonic.java b/core/src/main/java/org/nevec/rjm/Harmonic.java deleted file mode 100644 index 5058c3a2..00000000 --- a/core/src/main/java/org/nevec/rjm/Harmonic.java +++ /dev/null @@ -1,43 +0,0 @@ -package org.nevec.rjm; - -/** - * Harmonic numbers. - * H(n) is the sum of the inverses of the integers from 1 to n. - * - * @since 2008-10-19 - * @author Richard J. Mathar - */ -public class Harmonic { - /** - * ctor() - * Does nothing. - */ - public Harmonic() {} - - /** - * The Harmonic number at the index specified - * - * @param n - * the index, non-negative. - * @return the H_1=1 for n=1, H_2=3/2 for n=2 etc. - * For values of n less than 1, zero is returned. - */ - public Rational at(final int n) { - if (n < 1) { - return new Rational(0, 1); - } else { - /* - * start with 1 as the result - */ - Rational a = new Rational(1, 1); - - /* - * add 1/i for i=2..n - */ - for (int i = 2; i <= n; i++) { - a = a.add(new Rational(1, i)); - } - return a; - } - } -} /* Harmonic */ diff --git a/core/src/main/java/org/nevec/rjm/Ifactor.java b/core/src/main/java/org/nevec/rjm/Ifactor.java deleted file mode 100644 index fe9948e3..00000000 --- a/core/src/main/java/org/nevec/rjm/Ifactor.java +++ /dev/null @@ -1,835 +0,0 @@ -package org.nevec.rjm; - -import java.math.BigInteger; -import java.util.Collections; -import java.util.Vector; - -import it.cavallium.warppi.util.Error; - -/** - * Factored integers. - * This class contains a non-negative integer with the prime factor - * decomposition attached. - * - * @since 2006-08-14 - * @since 2012-02-14 The internal representation contains the bases, and becomes - * sparser if few - * prime factors are present. - * @author Richard J. Mathar - */ -public class Ifactor implements Cloneable, Comparable { - /** - * The standard representation of the number - */ - public BigInteger n; - - /* - * The bases and powers of the prime factorization. - * representation n = primeexp[0]^primeexp[1]*primeexp[2]^primeexp[3]*... - * The value 0 is represented by an empty vector, the value 1 by a vector of - * length 1 - * with a single power of 0. - */ - public Vector primeexp; - - final public static Ifactor ONE = new Ifactor(1); - - final public static Ifactor ZERO = new Ifactor(0); - - /** - * Constructor given an integer. - * constructor with an ordinary integer - * - * @param number - * the standard representation of the integer - */ - public Ifactor(int number) { - n = new BigInteger("" + number); - primeexp = new Vector<>(); - if (number > 1) { - int primindx = 0; - final Prime primes = new Prime(); - /* - * Test division against all primes. - */ - while (number > 1) { - int ex = 0; - /* - * primindx=0 refers to 2, =1 to 3, =2 to 5, =3 to 7 etc - */ - final int p = primes.at(primindx).intValue(); - while (number % p == 0) { - ex++; - number /= p; - if (number == 1) { - break; - } - } - if (ex > 0) { - primeexp.add(new Integer(p)); - primeexp.add(new Integer(ex)); - } - primindx++; - } - } else if (number == 1) { - primeexp.add(new Integer(1)); - primeexp.add(new Integer(0)); - } - } /* Ifactor */ - - /** - * Constructor given a BigInteger . - * Constructor with an ordinary integer, calling a prime factor - * decomposition. - * - * @param number - * the BigInteger representation of the integer - */ - public Ifactor(BigInteger number) { - n = number; - primeexp = new Vector<>(); - if (number.compareTo(BigInteger.ONE) == 0) { - primeexp.add(new Integer(1)); - primeexp.add(new Integer(0)); - } else { - int primindx = 0; - final Prime primes = new Prime(); - /* - * Test for division against all primes. - */ - while (number.compareTo(BigInteger.ONE) == 1) { - int ex = 0; - final BigInteger p = primes.at(primindx); - while (number.remainder(p).compareTo(BigInteger.ZERO) == 0) { - ex++; - number = number.divide(p); - if (number.compareTo(BigInteger.ONE) == 0) { - break; - } - } - if (ex > 0) { - primeexp.add(new Integer(p.intValue())); - primeexp.add(new Integer(ex)); - } - primindx++; - } - } - } /* Ifactor */ - - /** - * Constructor given a list of exponents of the prime factor decomposition. - * - * @param pows - * the vector with the sorted list of exponents. - * pows[0] is the exponent of 2, pows[1] the exponent of 3, - * pows[2] the exponent of 5 etc. - * Note that this list does not include the primes, but assumes a - * continuous prime-smooth basis. - */ - public Ifactor(final Vector pows) { - primeexp = new Vector<>(2 * pows.size()); - if (pows.size() > 0) { - n = BigInteger.ONE; - final Prime primes = new Prime(); - /* - * Build the full number by the product of all powers of the primes. - */ - for (int primindx = 0; primindx < pows.size(); primindx++) { - final int ex = pows.elementAt(primindx).intValue(); - final BigInteger p = primes.at(primindx); - n = n.multiply(p.pow(ex)); - primeexp.add(new Integer(p.intValue())); - primeexp.add(new Integer(ex)); - } - } else { - n = BigInteger.ZERO; - } - } /* Ifactor */ - - /** - * Copy constructor. - * - * @param oth - * the value to be copied - */ - public Ifactor(final Ifactor oth) { - n = oth.n; - primeexp = oth.primeexp; - } /* Ifactor */ - - /** - * Deep copy. - * - * @since 2009-08-14 - */ - @Override - public Ifactor clone() { - /* - * Line not used: - * - * Vector p = (Vector)primeexp.clone(); - * - */ - final Ifactor cl = new Ifactor(0); - cl.n = new BigInteger("" + n); - return cl; - } /* Ifactor.clone */ - - /** - * Comparison of two numbers. - * The value of this method is in allowing the Vector<>.contains() calls - * that use the value, - * not the reference for comparison. - * - * @param oth - * the number to compare this with. - * @return true if both are the same numbers, false otherwise. - */ - public boolean equals(final Ifactor oth) { - return n.compareTo(oth.n) == 0; - } /* Ifactor.equals */ - - /** - * Multiply with another positive integer. - * - * @param oth - * the second factor. - * @return the product of both numbers. - */ - public Ifactor multiply(final BigInteger oth) { - /* - * the optimization is to factorize oth _before_ multiplying - */ - return multiply(new Ifactor(oth)); - } /* Ifactor.multiply */ - - /** - * Multiply with another positive integer. - * - * @param oth - * the second factor. - * @return the product of both numbers. - */ - public Ifactor multiply(final int oth) { - /* - * the optimization is to factorize oth _before_ multiplying - */ - return multiply(new Ifactor(oth)); - } /* Ifactor.multiply */ - - /** - * Multiply with another positive integer. - * - * @param oth - * the second factor. - * @return the product of both numbers. - */ - public Ifactor multiply(final Ifactor oth) { - /* - * This might be done similar to the lcm() implementation by adding - * the powers of the components and calling the constructor with the - * list of exponents. This here is the simplest implementation, but slow - * because - * it calls another prime factorization of the product: - * return( new Ifactor(n.multiply(oth.n))) ; - */ - return multGcdLcm(oth, 0); - } - - /** - * Lowest common multiple of this with oth. - * - * @param oth - * the second parameter of lcm(this,oth) - * @return the lowest common multiple of both numbers. Returns zero - * if any of both arguments is zero. - */ - public Ifactor lcm(final Ifactor oth) { - return multGcdLcm(oth, 2); - } - - /** - * Greatest common divisor of this and oth. - * - * @param oth - * the second parameter of gcd(this,oth) - * @return the lowest common multiple of both numbers. Returns zero - * if any of both arguments is zero. - */ - public Ifactor gcd(final Ifactor oth) { - return multGcdLcm(oth, 1); - } - - /** - * Multiply with another positive integer. - * - * @param oth - * the second factor. - * @param type - * 0 to multiply, 1 for gcd, 2 for lcm - * @return the product, gcd or lcm of both numbers. - */ - protected Ifactor multGcdLcm(final Ifactor oth, final int type) { - final Ifactor prod = new Ifactor(0); - /* - * skip the case where 0*something =0, falling thru to the empty - * representation for 0 - */ - if (primeexp.size() != 0 && oth.primeexp.size() != 0) { - /* - * Cases of 1 times something return something. - * Cases of lcm(1, something) return something. - * Cases of gcd(1, something) return 1. - */ - if (primeexp.firstElement().intValue() == 1 && type == 0) { - return oth; - } else if (primeexp.firstElement().intValue() == 1 && type == 2) { - return oth; - } else if (primeexp.firstElement().intValue() == 1 && type == 1) { - return this; - } else if (oth.primeexp.firstElement().intValue() == 1 && type == 0) { - return this; - } else if (oth.primeexp.firstElement().intValue() == 1 && type == 2) { - return this; - } else if (oth.primeexp.firstElement().intValue() == 1 && type == 1) { - return oth; - } else { - int idxThis = 0; - int idxOth = 0; - switch (type) { - case 0: - prod.n = n.multiply(oth.n); - break; - case 1: - prod.n = n.gcd(oth.n); - break; - case 2: - /* - * the awkward way, lcm = product divided by gcd - */ - prod.n = n.multiply(oth.n).divide(n.gcd(oth.n)); - break; - } - - /* - * scan both representations left to right, increasing prime - * powers - */ - while (idxOth < oth.primeexp.size() || idxThis < primeexp.size()) { - if (idxOth >= oth.primeexp.size()) { - /* - * exhausted the list in oth.primeexp; copy over the - * remaining 'this' - * if multiplying or lcm, discard if gcd. - */ - if (type == 0 || type == 2) { - prod.primeexp.add(primeexp.elementAt(idxThis)); - prod.primeexp.add(primeexp.elementAt(idxThis + 1)); - } - idxThis += 2; - } else if (idxThis >= primeexp.size()) { - /* - * exhausted the list in primeexp; copy over the - * remaining 'oth' - */ - if (type == 0 || type == 2) { - prod.primeexp.add(oth.primeexp.elementAt(idxOth)); - prod.primeexp.add(oth.primeexp.elementAt(idxOth + 1)); - } - idxOth += 2; - } else { - Integer p; - int ex; - switch (primeexp.elementAt(idxThis).compareTo(oth.primeexp.elementAt(idxOth))) { - case 0: - /* same prime bases p in both factors */ - p = primeexp.elementAt(idxThis); - switch (type) { - case 0: - /* product means adding exponents */ - ex = primeexp.elementAt(idxThis + 1).intValue() + oth.primeexp.elementAt(idxOth + 1).intValue(); - break; - case 1: - /* gcd means minimum of exponents */ - ex = Math.min(primeexp.elementAt(idxThis + 1).intValue(), oth.primeexp.elementAt(idxOth + 1).intValue()); - break; - default: - /* lcm means maximum of exponents */ - ex = Math.max(primeexp.elementAt(idxThis + 1).intValue(), oth.primeexp.elementAt(idxOth + 1).intValue()); - break; - } - prod.primeexp.add(p); - prod.primeexp.add(new Integer(ex)); - idxOth += 2; - idxThis += 2; - break; - case 1: - /* - * this prime base bigger than the other and - * taken later - */ - if (type == 0 || type == 2) { - prod.primeexp.add(oth.primeexp.elementAt(idxOth)); - prod.primeexp.add(oth.primeexp.elementAt(idxOth + 1)); - } - idxOth += 2; - break; - default: - /* - * this prime base smaller than the other and - * taken now - */ - if (type == 0 || type == 2) { - prod.primeexp.add(primeexp.elementAt(idxThis)); - prod.primeexp.add(primeexp.elementAt(idxThis + 1)); - } - idxThis += 2; - } - } - } - } - } - return prod; - } /* Ifactor.multGcdLcm */ - - /** - * Integer division through another positive integer. - * - * @param oth - * the denominator. - * @return the division of this through the oth, discarding the remainder. - */ - public Ifactor divide(final Ifactor oth) { - /* - * todo: it'd probably be faster to cancel the gcd(this,oth) first in - * the prime power - * representation, which would avoid a more strenous factorization of - * the integer ratio - */ - return new Ifactor(n.divide(oth.n)); - } /* Ifactor.divide */ - - /** - * Summation with another positive integer - * - * @param oth - * the other term. - * @return the sum of both numbers - */ - public Ifactor add(final BigInteger oth) { - /* - * avoid refactorization if oth is zero... - */ - if (oth.compareTo(BigInteger.ZERO) != 0) { - return new Ifactor(n.add(oth)); - } else { - return this; - } - } /* Ifactor.add */ - - /** - * Exponentiation with a positive integer. - * - * @param exponent - * the non-negative exponent - * @return n^exponent. If exponent=0, the result is 1. - */ - public Ifactor pow(final int exponent) throws ArithmeticException { - /* - * three simple cases first - */ - if (exponent < 0) { - throw new ArithmeticException("Cannot raise " + toString() + " to negative " + exponent); - } else if (exponent == 0) { - return new Ifactor(1); - } else if (exponent == 1) { - return this; - } - - /* - * general case, the vector with the prime factor powers, which are - * component-wise - * exponentiation of the individual prime factor powers. - */ - final Ifactor pows = new Ifactor(0); - for (int i = 0; i < primeexp.size(); i += 2) { - final Integer p = primeexp.elementAt(i); - final int ex = primeexp.elementAt(i + 1).intValue(); - pows.primeexp.add(p); - pows.primeexp.add(new Integer(ex * exponent)); - } - return pows; - } /* Ifactor.pow */ - - /** - * Pulling the r-th root. - * - * @param r - * the positive or negative (nonzero) root. - * @return n^(1/r). - * The return value falls into the Ifactor class if r is positive, - * but if r is negative - * a Rational type is needed. - * @throws Error - * @since 2009-05-18 - */ - public Rational root(final int r) throws ArithmeticException, Error { - if (r == 0) { - throw new ArithmeticException("Cannot pull zeroth root of " + toString()); - } else if (r < 0) { - /* - * a^(-1/b)= 1/(a^(1/b)) - */ - final Rational invRoot = root(-r); - return Rational.ONE.divide(invRoot); - } else { - final BigInteger pows = BigInteger.ONE; - for (int i = 0; i < primeexp.size(); i += 2) { - /* - * all exponents must be multiples of r to succeed (that is, to - * stay in the range of rational results). - */ - final int ex = primeexp.elementAt(i + 1).intValue(); - if (ex % r != 0) { - throw new ArithmeticException("Cannot pull " + r + "th root of " + toString()); - } - - pows.multiply(new BigInteger("" + primeexp.elementAt(i)).pow(ex / r)); - } - /* - * convert result to a Rational; unfortunately this will loose the - * prime factorization - */ - return new Rational(pows); - } - } /* Ifactor.root */ - - /** - * The set of positive divisors. - * - * @return the vector of divisors of the absolute value, sorted. - * @since 2010-08-27 - */ - public Vector divisors() { - /* - * Recursive approach: the divisors of p1^e1*p2^e2*..*py^ey*pz^ez are - * the divisors that don't contain the factor pz, and the - * the divisors that contain any power of pz between 1 and up to ez - * multiplied - * by 1 or by a product that contains the factors p1..py. - */ - final Vector d = new Vector<>(); - if (n.compareTo(BigInteger.ZERO) == 0) { - return d; - } - d.add(BigInteger.ONE); - if (n.compareTo(BigInteger.ONE) > 0) { - /* Computes sigmaIncopml(p1^e*p2^e2...*py^ey) */ - final Ifactor dp = dropPrime(); - - /* get ez */ - final int ez = primeexp.lastElement().intValue(); - - final Vector partd = dp.divisors(); - - /* obtain pz by lookup in the prime list */ - final BigInteger pz = new BigInteger(primeexp.elementAt(primeexp.size() - 2).toString()); - - /* - * the output contains all products of the form partd[]*pz^ez, ez>0, - * and with the exception of the 1, all these are appended. - */ - for (int i = 1; i < partd.size(); i++) { - d.add(partd.elementAt(i)); - } - for (int e = 1; e <= ez; e++) { - final BigInteger pzez = pz.pow(e); - for (int i = 0; i < partd.size(); i++) { - d.add(partd.elementAt(i).multiply(pzez)); - } - } - } - Collections.sort(d); - return d; - } /* Ifactor.divisors */ - - /** - * Sum of the divisors of the number. - * - * @return the sum of all divisors of the number, 1+....+n. - */ - public Ifactor sigma() { - return sigma(1); - } /* Ifactor.sigma */ - - /** - * Sum of the k-th powers of divisors of the number. - * - * @return the sum of all divisors of the number, 1^k+....+n^k. - */ - public Ifactor sigma(final int k) { - /* - * the question is whether keeping a factorization is worth the effort - * or whether one should simply multiply these to return a BigInteger... - */ - if (n.compareTo(BigInteger.ONE) == 0) { - return Ifactor.ONE; - } else if (n.compareTo(BigInteger.ZERO) == 0) { - return Ifactor.ZERO; - } else { - /* - * multiplicative: sigma_k(p^e) = [p^(k*(e+1))-1]/[p^k-1] - * sigma_0(p^e) = e+1. - */ - Ifactor resul = Ifactor.ONE; - for (int i = 0; i < primeexp.size(); i += 2) { - final int ex = primeexp.elementAt(i + 1).intValue(); - if (k == 0) { - resul = resul.multiply(ex + 1); - } else { - final Integer p = primeexp.elementAt(i); - final BigInteger num = new BigInteger(p.toString()).pow(k * (ex + 1)).subtract(BigInteger.ONE); - final BigInteger deno = new BigInteger(p.toString()).pow(k).subtract(BigInteger.ONE); - /* - * This division is of course exact, no remainder - * The costly prime factorization is hidden here. - */ - final Ifactor f = new Ifactor(num.divide(deno)); - resul = resul.multiply(f); - } - } - return resul; - } - } /* Ifactor.sigma */ - - /** - * Divide through the highest possible power of the highest prime. - * If the current number is the prime factor product p1^e1 * p2*e2* - * p3^e3*...*py^ey * pz^ez, - * the value returned has the final factor pz^ez eliminated, which gives - * p1^e1 * p2*e2* p3^e3*...*py^ey. - * - * @return the new integer obtained by removing the highest prime power. - * If this here represents 0 or 1, it is returned without change. - * @since 2006-08-20 - */ - public Ifactor dropPrime() { - /* - * the cases n==1 or n ==0 - */ - if (n.compareTo(BigInteger.ONE) <= 0) { - return this; - } - - /* - * The cases n>1 - * Start empty. Copy all but the last factor over to the result - * the vector with the new prime factor powers, which contain the - * old prime factor powers up to but not including the last one. - */ - final Ifactor pows = new Ifactor(0); - pows.n = BigInteger.ONE; - for (int i = 0; i < primeexp.size() - 2; i += 2) { - pows.primeexp.add(primeexp.elementAt(i)); - pows.primeexp.add(primeexp.elementAt(i + 1)); - final BigInteger p = new BigInteger(primeexp.elementAt(i).toString()); - final int ex = primeexp.elementAt(i + 1).intValue(); - pows.n = pows.n.multiply(p.pow(ex)); - } - return pows; - } /* Ifactor.dropPrime */ - - /** - * Test whether this is a square of an integer (perfect square). - * - * @return true if this is an integer squared (including 0), else false - */ - public boolean issquare() { - /* - * check the exponents, located at the odd-indexed positions - */ - for (int i = 1; i < primeexp.size(); i += 2) { - if (primeexp.elementAt(i).intValue() % 2 != 0) { - return false; - } - } - return true; - } /* Ifactor.issquare */ - - /** - * The sum of the prime factor exponents, with multiplicity. - * - * @return the sum over the primeexp numbers - */ - public int bigomega() { - int resul = 0; - for (int i = 1; i < primeexp.size(); i += 2) { - resul += primeexp.elementAt(i).intValue(); - } - return resul; - } /* Ifactor.bigomega */ - - /** - * The sum of the prime factor exponents, without multiplicity. - * - * @return the number of distinct prime factors. - * @since 2008-10-16 - */ - public int omega() { - return primeexp.size() / 2; - } /* Ifactor.omega */ - - /** - * The square-free part. - * - * @return the minimum m such that m times this number is a square. - * @since 2008-10-16 - */ - public BigInteger core() { - BigInteger resul = BigInteger.ONE; - for (int i = 0; i < primeexp.size(); i += 2) { - if (primeexp.elementAt(i + 1).intValue() % 2 != 0) { - resul = resul.multiply(new BigInteger(primeexp.elementAt(i).toString())); - } - } - return resul; - } /* Ifactor.core */ - - /** - * The Moebius function. - * 1 if n=1, else, if k is the number of distinct prime factors, return - * (-1)^k, - * else, if k has repeated prime factors, return 0. - * - * @return the moebius function. - */ - public int moebius() { - if (n.compareTo(BigInteger.ONE) <= 0) { - return 1; - } - /* accumulate number of different primes in k */ - int k = 1; - for (int i = 0; i < primeexp.size(); i += 2) { - final int e = primeexp.elementAt(i + 1).intValue(); - if (e > 1) { - return 0; - } else if (e == 1) { - /* accumulates (-1)^k */ - k *= -1; - } - - } - return k; - } /* Ifactor.moebius */ - - /** - * Maximum of two values. - * - * @param oth - * the number to compare this with. - * @return the larger of the two values. - */ - public Ifactor max(final Ifactor oth) { - if (n.compareTo(oth.n) >= 0) { - return this; - } else { - return oth; - } - } /* Ifactor.max */ - - /** - * Minimum of two values. - * - * @param oth - * the number to compare this with. - * @return the smaller of the two values. - */ - public Ifactor min(final Ifactor oth) { - if (n.compareTo(oth.n) <= 0) { - return this; - } else { - return oth; - } - } /* Ifactor.min */ - - /** - * Maximum of a list of values. - * - * @param set - * list of numbers. - * @return the largest in the list. - */ - public static Ifactor max(final Vector set) { - Ifactor resul = set.elementAt(0); - for (int i = 1; i < set.size(); i++) { - resul = resul.max(set.elementAt(i)); - } - return resul; - } /* Ifactor.max */ - - /** - * Minimum of a list of values. - * - * @param set - * list of numbers. - * @return the smallest in the list. - */ - public static Ifactor min(final Vector set) { - Ifactor resul = set.elementAt(0); - for (int i = 1; i < set.size(); i++) { - resul = resul.min(set.elementAt(i)); - } - return resul; - } /* Ifactor.min */ - - /** - * Compare value against another Ifactor - * - * @param oth - * The value to be compared agains. - * @return 1, 0 or -1 according to being larger, equal to or smaller than - * oth. - * @since 2012-02-15 - */ - @Override - public int compareTo(final Ifactor oth) { - return n.compareTo(oth.n); - } /* compareTo */ - - /** - * Convert to printable format - * - * @return a string of the form n:prime^pow*prime^pow*prime^pow... - */ - @Override - public String toString() { - String resul = new String(n.toString() + ":"); - if (n.compareTo(BigInteger.ONE) == 0) { - resul += "1"; - } else { - boolean firstMul = true; - for (int i = 0; i < primeexp.size(); i += 2) { - if (!firstMul) { - resul += "*"; - } - if (primeexp.elementAt(i + 1).intValue() > 1) { - resul += primeexp.elementAt(i).toString() + "^" + primeexp.elementAt(i + 1).toString(); - } else { - resul += primeexp.elementAt(i).toString(); - } - firstMul = false; - } - } - return resul; - } /* Ifactor.toString */ - - /** - * Test program. - * It takes a single argument n and prints the integer factorizaton.
- * java -cp . org.nevec.rjm.Ifactor n
- */ - public static void main(final String[] args) throws Exception { - final BigInteger n = new BigInteger(args[0]); - System.out.println(new Ifactor(n)); - } /* Ifactor.main */ -} /* Ifactor */ diff --git a/core/src/main/java/org/nevec/rjm/PartitionsP.java b/core/src/main/java/org/nevec/rjm/PartitionsP.java deleted file mode 100644 index 1af7559e..00000000 --- a/core/src/main/java/org/nevec/rjm/PartitionsP.java +++ /dev/null @@ -1,88 +0,0 @@ -package org.nevec.rjm; - -import java.math.BigInteger; -import java.util.Vector; - -/** - * Number of partitions. - * - * @since 2008-10-15 - * @author Richard J. Mathar - */ -public class PartitionsP { - /** - * The list of all partitions as a vector. - */ - static protected Vector a = new Vector<>(); - - /** - * The maximum integer covered by the high end of the list. - */ - static protected BigInteger nMax = new BigInteger("-1"); - - /** - * Default constructor initializing a list of partitions up to 7. - */ - public PartitionsP() { - if (PartitionsP.a.size() == 0) { - PartitionsP.a.add(new BigInteger("" + 1)); - PartitionsP.a.add(new BigInteger("" + 1)); - PartitionsP.a.add(new BigInteger("" + 2)); - PartitionsP.a.add(new BigInteger("" + 3)); - PartitionsP.a.add(new BigInteger("" + 5)); - PartitionsP.a.add(new BigInteger("" + 7)); - } - PartitionsP.nMax = new BigInteger("" + (PartitionsP.a.size() - 1)); - } /* ctor */ - - /** - * return the number of partitions of i - * - * @param i - * the zero-based index into the list of partitions - * @return the ith partition number. This is 1 if i=0 or 1, 2 if i=2 and so - * forth. - */ - public BigInteger at(final int i) { - /* - * If the current list is too small, increase in intervals - * of 3 until the list has at least i elements. - */ - while (i > PartitionsP.nMax.intValue()) { - growto(PartitionsP.nMax.add(new BigInteger("" + 3))); - } - return PartitionsP.a.elementAt(i); - } /* at */ - - /** - * extend the list of known partitions up to n - * - * @param n - * the maximum integer hashed after the call. - */ - private void growto(final BigInteger n) { - while (PartitionsP.a.size() <= n.intValue()) { - BigInteger per = new BigInteger("0"); - final BigInteger cursiz = new BigInteger("" + PartitionsP.a.size()); - for (int k = 0; k < PartitionsP.a.size(); k++) { - final BigInteger tmp = PartitionsP.a.elementAt(k).multiply(BigIntegerMath.sigma(PartitionsP.a.size() - k)); - per = per.add(tmp); - } - PartitionsP.a.add(per.divide(cursiz)); - } - PartitionsP.nMax = new BigInteger("" + (PartitionsP.a.size() - 1)); - } /* growto */ - - /** - * Test program. - * It takes one integer argument n and prints P(n).
- * java -cp . org.nevec.rjm.PartitionsP n
- * - * @since 2008-10-15 - */ - public static void main(final String[] args) throws Exception { - final PartitionsP a = new PartitionsP(); - final int n = new Integer(args[0]).intValue(); - System.out.println("P(" + n + ")=" + a.at(n)); - } -} diff --git a/core/src/main/java/org/nevec/rjm/Prime.java b/core/src/main/java/org/nevec/rjm/Prime.java deleted file mode 100644 index 2b1111a9..00000000 --- a/core/src/main/java/org/nevec/rjm/Prime.java +++ /dev/null @@ -1,321 +0,0 @@ -package org.nevec.rjm; - -import java.math.BigInteger; -import java.util.Vector; - -/** - * Prime numbers. - * The implementation is a very basic computation of the set of all primes - * on demand, growing infinitely without any defined upper limit. - * The effects of such scheme are (i) the lookup-times become shorter after - * a while as more and more primes have been used and stored. The applications - * appear to become faster. (ii) Using the implementation for factorizations - * may easily require all available memory and stall finally, because indeed - * a dense list of primes with growing upper bound is kept without any hashing - * or lagging scheme. - * - * @since 2006-08-11 - * @author Richard J. Mathar - */ -public class Prime { - /** - * The list of all numbers as a vector. - */ - static Vector a = new Vector<>(); - - /** - * The maximum integer covered by the high end of the list. - */ - static protected BigInteger nMax = new BigInteger("-1"); - - /** - * Default constructor initializing a list of primes up to 17. - * 17 is enough to call the Miller-Rabin tests on the first 7 primes without - * further - * action. - */ - public Prime() { - if (Prime.a.size() == 0) { - Prime.a.add(new BigInteger("" + 2)); - Prime.a.add(new BigInteger("" + 3)); - Prime.a.add(new BigInteger("" + 5)); - Prime.a.add(new BigInteger("" + 7)); - Prime.a.add(new BigInteger("" + 11)); - Prime.a.add(new BigInteger("" + 13)); - Prime.a.add(new BigInteger("" + 17)); - } - Prime.nMax = Prime.a.lastElement(); - } - - /** - * Test if a number is a prime. - * - * @param n - * the integer to be tested for primality - * @return true if prime, false if not - */ - public boolean contains(final BigInteger n) { - /* - * not documented - * return ( n.isProbablePrime() ) ; - */ - switch (millerRabin(n)) { - case -1: - return false; - case 1: - return true; - } - growto(n); - return Prime.a.contains(n); - } - - /** - * Test whether a number n is a strong pseudoprime to base a. - * - * @param n - * the integer to be tested for primality - * @param a - * the base - * @return true if the test is passed, so n may be a prime. - * false if the test is not passed, so n is not a prime. - * @since 2010-02-25 - */ - public boolean isSPP(final BigInteger n, final BigInteger a) { - final BigInteger two = new BigInteger("" + 2); - - /* - * numbers less than 2 are not prime - */ - if (n.compareTo(two) == -1) { - return false; - } else if (n.compareTo(two) == 0) { - return true; - } else if (n.remainder(two).compareTo(BigInteger.ZERO) == 0) { - return false; - } else { - /* - * q= n- 1 = d *2^s with d odd - */ - final BigInteger q = n.subtract(BigInteger.ONE); - final int s = q.getLowestSetBit(); - final BigInteger d = q.shiftRight(s); - - /* - * test whether a^d = 1 (mod n) - */ - if (a.modPow(d, n).compareTo(BigInteger.ONE) == 0) { - return true; - } - - /* - * test whether a^(d*2^r) = -1 (mod n), 0<=r= Prime.a.size()) { - growto(Prime.nMax.add(new BigInteger("" + 5))); - } - return Prime.a.elementAt(i); - } - - /** - * return the count of primes <= n - * - * @param n - * the upper limit of the scan - * @return the ith prime. This is 2 if i=0, 3 if i=1 and so forth. - */ - public BigInteger pi(final BigInteger n) { - /* - * If the current list is too small, increase in intervals - * of 5 until the list has at least i elements. - */ - growto(n); - BigInteger r = new BigInteger("0"); - for (int i = 0; i < Prime.a.size(); i++) { - if (Prime.a.elementAt(i).compareTo(n) <= 0) { - r = r.add(BigInteger.ONE); - } - } - return r; - } - - /** - * return the smallest prime larger than n - * - * @param n - * lower limit of the search - * @return the next larger prime. - * @since 2008-10-16 - */ - public BigInteger nextprime(final BigInteger n) { - /* if n <=1, return 2 */ - if (n.compareTo(BigInteger.ONE) <= 0) { - return Prime.a.elementAt(0); - } - - /* - * If the currently largest element in the list is too small, increase - * in intervals - * of 5 until the list has at least i elements. - */ - while (Prime.a.lastElement().compareTo(n) <= 0) { - growto(Prime.nMax.add(new BigInteger("" + 5))); - } - for (int i = 0; i < Prime.a.size(); i++) { - if (Prime.a.elementAt(i).compareTo(n) == 1) { - return Prime.a.elementAt(i); - } - } - return Prime.a.lastElement(); - } - - /** - * return the largest prime smaller than n - * - * @param n - * upper limit of the search - * @return the next smaller prime. - * @since 2008-10-17 - */ - public BigInteger prevprime(final BigInteger n) { - /* if n <=2, return 0 */ - if (n.compareTo(BigInteger.ONE) <= 0) { - return BigInteger.ZERO; - } - - /* - * If the currently largest element in the list is too small, increase - * in intervals - * of 5 until the list has at least i elements. - */ - while (Prime.a.lastElement().compareTo(n) < 0) { - growto(Prime.nMax.add(new BigInteger("" + 5))); - } - - for (int i = 0; i < Prime.a.size(); i++) { - if (Prime.a.elementAt(i).compareTo(n) >= 0) { - return Prime.a.elementAt(i - 1); - } - } - return Prime.a.lastElement(); - } - - /** - * extend the list of known primes up to n - * - * @param n - * the maximum integer known to be prime or not prime after the - * call. - */ - protected void growto(final BigInteger n) { - while (Prime.nMax.compareTo(n) == -1) { - Prime.nMax = Prime.nMax.add(BigInteger.ONE); - boolean isp = true; - for (int p = 0; p < Prime.a.size(); p++) { - /* - * Test the list of known primes only up to sqrt(n) - */ - if (Prime.a.get(p).multiply(Prime.a.get(p)).compareTo(Prime.nMax) == 1) { - break; - } - - /* - * The next case means that the p'th number in the list of known - * primes divides - * nMax and nMax cannot be a prime. - */ - if (Prime.nMax.remainder(Prime.a.get(p)).compareTo(BigInteger.ZERO) == 0) { - isp = false; - break; - } - } - if (isp) { - Prime.a.add(Prime.nMax); - } - } - } - - /** - * Test program. - * Usage: java -cp . org.nevec.rjm.Prime n
- * This takes a single argument (n) and prints prime(n), the previous and - * next prime, and pi(n). - * - * @since 2006-08-14 - */ - public static void main(final String[] args) throws Exception { - final Prime a = new Prime(); - final int n = new Integer(args[0]).intValue(); - if (n >= 1) { - if (n >= 2) { - System.out.println("prime(" + (n - 1) + ") = " + a.at(n - 1)); - } - System.out.println("prime(" + n + ") = " + a.at(n)); - System.out.println("prime(" + (n + 1) + ") = " + a.at(n + 1)); - System.out.println("pi(" + n + ") = " + a.pi(new BigInteger("" + n))); - } - } -} /* Prime */ diff --git a/core/src/main/java/org/nevec/rjm/RatPoly.java b/core/src/main/java/org/nevec/rjm/RatPoly.java deleted file mode 100644 index 6bb27552..00000000 --- a/core/src/main/java/org/nevec/rjm/RatPoly.java +++ /dev/null @@ -1,1045 +0,0 @@ -package org.nevec.rjm; - -import java.math.BigInteger; -import java.math.MathContext; -import java.math.RoundingMode; -import java.util.Random; -import java.util.Scanner; -import java.util.Vector; - -import it.cavallium.warppi.util.Error; - -/** - * A one-parameter polynomial with rational coefficients. - * Alternatively to be interpreted as a sequence which has the polynomial as an - * (approximate) - * generating function. - * - * @since 2006-06-25 - * @author Richard J. Mathar - */ -class RatPoly { - /** - * The list of all coefficients, ascending exponents. Starting with a0, then - * a1, representing - * a value a0+a1*x+a2*x^2+a3*x^3+... - */ - protected Vector a; - - /** - * Default ctor. - * Initializes the zero-valued polynomial x=0. - */ - public RatPoly() { - a = new Vector<>(); - } /* ctor */ - - /** - * Constructor with an explicit list of coefficients. - * - * @param L - * the coefficients a0, a1, a2, a3,.., A deep copy of the these - * is created. - */ - public RatPoly(final Vector L) { - a = new Vector<>(); - for (int i = 0; i < L.size(); i++) { - a.add(L.elementAt(i).clone()); - } - simplify(); - } /* ctor */ - - /** - * Constructor with a comma-separated list as the list of coefficients. - * - * @param L - * the string of the form a0,a1,a2,a3 with the coefficients - */ - public RatPoly(final String L) throws NumberFormatException { - a = new Vector<>(); - final Scanner sc = new Scanner(L); - sc.useDelimiter(","); - while (sc.hasNext()) { - final String tok = sc.next(); - a.add(new Rational(tok)); - } - simplify(); - sc.close(); - } /* ctor */ - - /** - * Constructor from a hypergeometric series. - * - * @param A - * the list of values in the numerator of AFB - * @param B - * the list of values in the denominator of AFB - * @param nmax - * the order of the truncated polynomial representation - * @throws Error - * @since 2008-11-13 - */ - public RatPoly(final Vector A, final Vector B, final int nmax) throws Error { - /* - * To allow common initialization with the signature below, - * the main body is assembled in a separate function. - */ - init(A, B, nmax); - } - - /** - * Constructor from a hypergeometric series. - * - * @param A - * the list of values in the numerator of AFB. - * At least one of these values must be a negative integer, which - * implicitly determines - * the order of the new polynomial. - * @param B - * the list of values in the denominator of AFB - * @throws Error - * @since 2009-08-05 - */ - public RatPoly(final Vector A, final Vector B) throws Error { - BigInteger Nmax = BigInteger.ONE.negate(); - for (int j = 0; j < A.size(); j++) { - if (A.elementAt(j).compareTo(BigInteger.ZERO) <= 0) { - if (Nmax.compareTo(BigInteger.ZERO) < 0) { - Nmax = A.elementAt(j).negate(); - } else { - Nmax = Nmax.min(A.elementAt(j).negate()); - } - } - } - if (Nmax.compareTo(BigInteger.ZERO) < 0) { - throw new ArithmeticException("Infinite Number of Terms in Series " + Nmax.toString()); - } - - final int nmax = Nmax.intValue() - 1; - init(A, B, nmax); - } /* ctor */ - - /** - * Constructor from a hypergeometric series. - * - * @param A - * the list of values in the numerator of AFB - * @param B - * the list of values in the denominator of AFB - * @param nmax - * the order of the truncated polynomial representation - * @throws Error - * @since 2008-11-13 - */ - protected void init(final Vector A, final Vector B, final int nmax) throws Error { - a = new Vector<>(); - final Factorial f = new Factorial(); - for (int n = 0; n <= nmax; n++) { - Rational c = new Rational(1, 1); - for (int j = 0; j < A.size(); j++) { - final Rational aEl = new Rational(A.elementAt(j)); - c = c.multiply(aEl.Pochhammer(n)); - } - for (int j = 0; j < B.size(); j++) { - final Rational bEl = new Rational(B.elementAt(j)); - c = c.divide(bEl.Pochhammer(n)); - } - c = c.divide(f.at(n)); - a.add(c); - } - simplify(); - } /* init */ - - /** - * Create a copy of this. - * - * @since 2008-11-07 - */ - @Override - @SuppressWarnings("unchecked") - public RatPoly clone() { - final RatPoly clo = new RatPoly(); - clo.a = (Vector) a.clone(); - return clo; - } /* clone */ - - /** - * Retrieve a polynomial coefficient. - * - * @param n - * the zero-based index of the coefficient. n=0 for the constant - * term. - * @return the polynomial coefficient in front of x^n. - */ - public Rational at(final int n) { - if (n < a.size()) { - return a.elementAt(n); - } else { - return new Rational(0, 1); - } - } /* at */ - - /** - * Horner scheme to find the function value at the argument x - * - * @param x - * The argument of the polynomial - * @param mc - * The context determining the precision of the value returned. - * @since 2008-10-26 - */ - public BigComplex valueOf(final BigComplex x, final MathContext mc) { - /* result is initialized to zero */ - BigComplex f = new BigComplex(); - for (int i = degree(); i >= 0; i--) { - f = f.multiply(x, mc).add(a.elementAt(i).BigDecimalValue(mc)); - } - return f; - } /* valueOf */ - - /** - * Horner scheme to find the function value at the argument x - * - * @param x - * The argument of the polynomial - * @since 2008-11-13 - */ - public Rational valueOf(final Rational x) { - /* result is initialized to zero */ - Rational f = new Rational(0, 1); - for (int i = degree(); i >= 0; i--) { - f = f.multiply(x).add(a.elementAt(i)); - } - return f; - } /* valueOf */ - - /** - * Horner scheme to find the function value at the argument x - * - * @param x - * The argument of the polynomial - * @since 2008-11-13 - */ - public Rational valueOf(final int x) { - return valueOf(new Rational(x, 1)); - } /* valueOf */ - - /** - * Horner scheme to evaluate the function at the argument x - * - * @param x - * The argument of the polynomial - * @since 2010-08-27 - */ - public Rational valueOf(final BigInteger x) { - return valueOf(new Rational(x)); - } /* valueOf */ - - /* - * Set a polynomial coefficient. - * - * @param n the zero-based index of the coefficient. n=0 for the constant - * term. - * If the polynomial has not yet the degree to need this coefficient, - * the intermediate coefficients are implicitly set to zero. - * - * @param value the new value of the coefficient. - */ - public void set(final int n, final Rational value) { - if (n < a.size()) { - a.set(n, value); - } else { - /* - * fill intermediate powers with coefficients of zero - */ - while (a.size() < n) { - a.add(new Rational(0, 1)); - } - a.add(value); - } - } /* set */ - - /** - * Set a polynomial coefficient. - * - * @param n - * the zero-based index of the coefficient. n=0 for the constant - * term. - * If the polynomial has not yet the degree to need this - * coefficient, - * the intermediate coefficients are implicitly set to zero. - * @param value - * the new value of the coefficient. - */ - public void set(final int n, final BigInteger value) { - final Rational val2 = new Rational(value, BigInteger.ONE); - set(n, val2); - } /* set */ - - /** - * Set a polynomial coefficient. - * - * @param n - * the zero-based index of the coefficient. n=0 for the constant - * term. - * If the polynomial has not yet the degree to need this - * coefficient, - * the intermediate coefficients are implicitly set to zero. - * @param value - * the new value of the coefficient. - */ - public void set(final int n, final int value) { - final Rational val2 = new Rational(value, 1); - set(n, val2); - } /* set */ - - /* - * Set to the taylor series of exp(x) up to degree nmax. - * - * @param nmax the maximum polynomial degree - */ - public void setExp(final int nmax) { - a.clear(); - final Factorial factorial = new Factorial(); - for (int n = 0; n <= nmax; n++) { - set(n, new Rational(BigInteger.ONE, factorial.at(n))); - } - } /* setExp */ - - /** - * Set to the taylor series representing 0+x. - */ - public void setx() { - a.clear(); - /* coefficient 0/1=0 */ - a.add(new Rational(0, 1)); - /* coefficient 1/1=1 */ - a.add(new Rational(1, 1)); - } /* setx */ - - /** - * Count of coefficients. One more than the degree of the polynomial. - * - * @return the number of polynomial coefficients. - */ - public int size() { - return a.size(); - } /* size */ - - /** - * Polynomial degree. - * - * @return the polynomial degree. - */ - public int degree() { - return a.size() - 1; - } /* degree */ - - /** - * Lower Polynomial degree. - * - * @return The smallest exponent n such that [x^n] of the polynomial is - * nonzero. - * If the polynmial is identical zero, the result is (still) 0. - * @since 2010-08-27 - */ - public int ldegree() { - for (int n = 0; n < a.size(); n++) { - if (a.elementAt(n).compareTo(BigInteger.ZERO) != 0) { - return n; - } - } - return 0; - } /* ldegree */ - - /** - * Multiply by a constant factor. - * - * @param val - * the factor - * @return the product of this with the factor. - * All coefficients of this have been multiplied individually by the - * factor. - */ - public RatPoly multiply(final Rational val) { - final RatPoly resul = new RatPoly(); - if (val.compareTo(BigInteger.ZERO) != 0) { - for (int n = 0; n < a.size(); n++) { - resul.set(n, a.elementAt(n).multiply(val)); - } - } - return resul; - } /* multiply */ - - /** - * Multiply by a constant factor. - * - * @param val - * the factor - * @return the product of this with the factor. - * All coefficients of this have been multiplied individually by the - * factor. - * @since 2010-08-27 - */ - public RatPoly multiply(final BigInteger val) { - final RatPoly resul = new RatPoly(); - if (val.compareTo(BigInteger.ZERO) != 0) { - for (int n = 0; n < a.size(); n++) { - resul.set(n, a.elementAt(n).multiply(val)); - } - } - return resul; - } /* multiply */ - - /** - * Multiply by another polynomial - * - * @param val - * the other polynomial - * @return the product of this with the other polynomial - */ - public RatPoly multiply(final RatPoly val) { - final RatPoly resul = new RatPoly(); - /* - * the degree of the result is the sum of the two degrees. - */ - final int nmax = degree() + val.degree(); - for (int n = 0; n <= nmax; n++) { - Rational coef = new Rational(0, 1); - for (int nleft = 0; nleft <= n; nleft++) { - coef = coef.add(at(nleft).multiply(val.at(n - nleft))); - } - resul.set(n, coef); - } - resul.simplify(); - return resul; - } /* multiply */ - - /** - * Raise to a positive power. - * - * @param n - * The non-negative exponent of the power - * @return The n-th power of this. - */ - public RatPoly pow(final int n) throws ArithmeticException { - RatPoly resul = new RatPoly("1"); - if (n < 0) { - throw new ArithmeticException("negative polynomial power " + n); - } else { - /* - * this ought probably be done with some binary representation - * of the power and a smaller number of multiplications. - */ - for (int i = 1; i <= n; i++) { - resul = resul.multiply(this); - } - resul.simplify(); - return resul; - } - } /* pow */ - - /** - * Raise to a rational power. - * The result is the taylor expansion of this, truncated at the first - * term that remains undetermined based on the current number of - * coefficients. - * - * @param r - * the exponent of the power - * @return This^r . - * @throws Error - * @since 2009-05-18 - */ - public RatPoly pow(final Rational r) throws ArithmeticException, Error { - /* - * split (a0+a1*x+a2*x^2+...)^r = a0^r*(1+a1/a0*r+a2/a0*r^2+..)^r - */ - Rational f = at(0); - f = f.pow(r); - - /* - * scale the polynomial by division through the expansion coefficient of - * the absolute term - */ - final RatPoly red = divide(a.elementAt(0)); - - /* - * and remove the leading term (now equal to 1) - */ - red.set(0, 0); - - /* - * Binomial expansion of the rest. sum_{l=0..infinity} - * binomial(r,l)*red^l - */ - RatPoly resul = new RatPoly("1"); - - final int d = degree(); - for (int l = 1; l <= d; l++) { - final Rational b = Rational.binomial(r, l); - resul = resul.add(red.pow(l).multiply(b)); - } - return resul.multiply(f); - } /* pow */ - - /** - * Add another polynomial - * - * @param val - * The other polynomial - * @return The sum of this and the other polynomial - * @since 2008-10-25 - */ - public RatPoly add(final RatPoly val) { - final RatPoly resul = new RatPoly(); - /* - * the degree of the result is the larger of the two degrees (before - * simplify() at least). - */ - final int nmax = degree() > val.degree() ? degree() : val.degree(); - for (int n = 0; n <= nmax; n++) { - final Rational coef = at(n).add(val.at(n)); - resul.set(n, coef); - } - resul.simplify(); - return resul; - } /* add */ - - /** - * Subtract another polynomial - * - * @param val - * The other polynomial - * @return The difference between this and the other polynomial - * @since 2008-10-25 - */ - public RatPoly subtract(final RatPoly val) { - final RatPoly resul = new RatPoly(); - /* - * the degree of the result is the larger of the two degrees (before - * simplify() at least). - */ - final int nmax = degree() > val.degree() ? degree() : val.degree(); - for (int n = 0; n <= nmax; n++) { - final Rational coef = at(n).subtract(val.at(n)); - resul.set(n, coef); - } - resul.simplify(); - return resul; - } /* subtract */ - - /** - * Divide by a constant. - * - * @param val - * the constant through which the coefficients will be divided. - * @return the Taylor expansion of this/val . - * @throws Error - * @since 2009-05-18 - */ - public RatPoly divide(final Rational val) throws Error { - if (val.compareTo(Rational.ZERO) != 0) { - final RatPoly resul = new RatPoly(); - for (int n = 0; n < a.size(); n++) { - resul.set(n, a.elementAt(n).divide(val)); - } - return resul; - } else { - throw new ArithmeticException("Cannot divide " + toPString() + " through zero."); - } - } /* divide */ - - /** - * Divide by another polynomial. - * - * @param val - * the other polynomial - * @param nmax - * the maximum degree of the Taylor expansion of the result. - * @return the Taylor expansion of this/val up to degree nmax. - * @throws Error - */ - public RatPoly divide(final RatPoly val, final int nmax) throws Error { - final RatPoly num = this; - final RatPoly denom = val; - - /* - * divide by a common smallest power/degree - */ - while (num.at(0).compareTo(BigInteger.ZERO) == 0 && denom.at(0).compareTo(BigInteger.ZERO) == 0) { - num.a.remove(0); - denom.a.remove(0); - if (num.size() <= 1 || denom.size() <= 1) { - break; - } - } - - final RatPoly resul = new RatPoly(); - /* - * todo: If the polynomial division is exact, we could leave - * the loop earlier, indeed - */ - for (int n = 0; n <= nmax; n++) { - Rational coef = num.at(n); - for (int nres = 0; nres < n; nres++) { - coef = coef.subtract(resul.at(nres).multiply(denom.at(n - nres))); - } - coef = coef.divide(denom.at(0)); - resul.set(n, coef); - } - resul.simplify(); - return resul; - } /* divide */ - - /** - * Divide by another polynomial. - * - * @param val - * the other polynomial - * @return A vector with [0] containg the polynomial of degree which is the - * difference of thisdegree and the degree of val. [1] the remainder - * polynomial. - * This = returnvalue[0] + returnvalue[1]/val . - * @throws Error - * @since 2012-03-01 - */ - public RatPoly[] divideAndRemainder(final RatPoly val) throws Error { - final RatPoly[] ret = new RatPoly[2]; - /* - * remove any high-order zeros - */ - final RatPoly valSimpl = val.clone(); - valSimpl.simplify(); - final RatPoly thisSimpl = clone(); - thisSimpl.simplify(); - - /* - * catch the case with val equal to zero - */ - if (valSimpl.degree() == 0 && valSimpl.a.firstElement().compareTo(Rational.ZERO) == 0) { - throw new ArithmeticException("Division through zero polynomial"); - } - /* - * degree of this smaller than degree of val: remainder is this - */ - if (thisSimpl.degree() < valSimpl.degree()) { - /* - * leading polynomial equals zero - */ - ret[0] = new RatPoly(); - ret[1] = thisSimpl; - } else { - /* - * long division. Highest degree by dividing the highest degree - * of this thru val. - */ - ret[0] = new RatPoly(); - ret[0].set(thisSimpl.degree() - valSimpl.degree(), thisSimpl.a.lastElement().divide(valSimpl.a.lastElement())); - - /* - * recurrences: build this - val*(1-termresult) and feed this - * into another round of division. Have intermediate - * ret[0]+ret[1]/val. - */ - ret[1] = thisSimpl.subtract(ret[0].multiply(valSimpl)); - - /* - * any remainder left ? - */ - if (ret[1].degree() < valSimpl.degree()) { - ; - } else { - final RatPoly rem[] = ret[1].divideAndRemainder(val); - ret[0] = ret[0].add(rem[0]); - ret[1] = rem[1]; - } - } - return ret; - } /* divideAndRemainder */ - - /** - * Print as a comma-separated list of coefficients. - * - * @return The representation a0,a1,a2,a3,... - * This is a sort of opposite of the ctor that takes a string as an - * argument. - * @since 2008-10-25 - */ - @Override - public String toString() { - String str = new String(); - for (int n = 0; n < a.size(); n++) { - if (n == 0) { - str += a.elementAt(n).toString(); - } else { - str += "," + a.elementAt(n).toString(); - } - } - /* - * print at least a sole zero - */ - if (str.length() == 0) { - str = "0"; - } - return str; - } /* toString */ - - /** - * Print as a polyomial in x. - * - * @return To representation a0+a1*x+a2*x^2+... - * This does not print the terms with coefficients equal to zero. - * @since 2008-10-26 - */ - public String toPString() { - String str = new String(); - for (int n = 0; n < a.size(); n++) { - final BigInteger num = a.elementAt(n).a; - if (num.compareTo(BigInteger.ZERO) != 0) { - str += " "; - if (num.compareTo(BigInteger.ZERO) > 0) { - str += "+"; - } - str += a.elementAt(n).toString(); - if (n > 0) { - str += "*x"; - if (n > 1) { - str += "^" + n; - } - } - } - } - /* - * print at least a sole zero - */ - if (str.length() == 0) { - str = "0"; - } - return str; - } /* toPString */ - - /** - * Simplify the representation. - * Trailing values with zero coefficients (at high powers) are deleted. - * This modifies the polynomial on the stop (does not return another - * instance) - */ - private void simplify() { - int n = a.size() - 1; - if (n >= 0) { - while (a.elementAt(n).compareTo(BigInteger.ZERO) == 0) { - a.remove(n); - if (--n < 0) { - break; - } - } - } - } /* simplify */ - - /** - * First derivative. - * - * @return The first derivative with respect to the indeterminate variable. - * @since 2008-10-26 - */ - public RatPoly derive() { - if (a.size() <= 1) { - /* - * derivative of the constant is just zero - */ - return new RatPoly(); - } else { - final RatPoly d = new RatPoly(); - for (int i = 1; i <= degree(); i++) { - final Rational c = a.elementAt(i).multiply(i); - d.set(i - 1, c); - } - return d; - } - } /* derive */ - - /** - * Scale coefficients such that the coefficient in front of the maximum - * degree is unity. - * - * @return The scaled polynomial - * @throws Error - * @since 2008-10-26 - */ - public RatPoly monic() throws Error { - final RatPoly m = new RatPoly(); - final int d = degree(); - for (int i = 0; i <= d; i++) { - final Rational c = a.elementAt(i).divide(a.elementAt(d)); - m.set(i, c); - } - return m; - } /* monic */ - - /** - * Mobius transform. - * - * @param maxdeg - * the maximum polynomial degree of the result - * @return the sequence of coefficients is the Mobius transform of the - * original sequence. - * @since 2008-12-02 - */ - public RatPoly mobiusT(final int maxdeg) { - /* - * Start with the polynomial 0 - */ - final RatPoly r = new RatPoly(); - for (int i = 1; i <= maxdeg; i++) { - Rational c = new Rational(); - for (int d = 1; d <= i && d < a.size(); d++) { - if (i % d == 0) { - final Ifactor m = new Ifactor(i / d); - c = c.add(a.elementAt(d).multiply(m.moebius())); - } - } - r.set(i, c); - } - r.simplify(); - return r; - } /* mobiusT */ - - /** - * Inverse Mobius transform. - * - * @param maxdeg - * the maximum polynomial degree of the result - * @return the sequence of coefficients is the inverse Mobius transform of - * the original sequence. - * @since 2008-12-02 - */ - public RatPoly mobiusTInv(final int maxdeg) { - /* - * Start with the polynomial 0 - */ - final RatPoly r = new RatPoly(); - for (int i = 1; i <= maxdeg; i++) { - Rational c = new Rational(); - for (int d = 1; d <= i && d < a.size(); d++) { - if (i % d == 0) { - c = c.add(a.elementAt(d)); - } - } - r.set(i, c); - } - r.simplify(); - return r; - } /* mobiusTInv */ - - /** - * Binomial transform. - * - * @param maxdeg - * the maximum polynomial degree of the result - * @return the sequence of coefficients is the binomial transform of the - * original sequence. - * @since 2008-10-26 - */ - public RatPoly binomialT(final int maxdeg) { - final RatPoly r = new RatPoly(); - for (int i = 0; i <= maxdeg; i++) { - Rational c = new Rational(0, 1); - for (int j = 0; j <= i && j < a.size(); j++) { - c = c.add(a.elementAt(j).multiply(BigIntegerMath.binomial(i, j))); - } - r.set(i, c); - } - r.simplify(); - return r; - } /* binomialT */ - - /** - * Inverse Binomial transform. - * - * @param maxdeg - * the maximum polynomial degree of the result - * @return the sequence of coefficients is the inverse binomial transform of - * the original sequence. - * @since 2008-10-26 - */ - public RatPoly binomialTInv(final int maxdeg) { - final RatPoly r = new RatPoly(); - for (int i = 0; i <= maxdeg; i++) { - Rational c = new Rational(0, 1); - for (int j = 0; j <= i && j < a.size(); j++) { - if ((j + i) % 2 != 0) { - c = c.subtract(a.elementAt(j).multiply(BigIntegerMath.binomial(i, j))); - } else { - c = c.add(a.elementAt(j).multiply(BigIntegerMath.binomial(i, j))); - } - } - r.set(i, c); - } - r.simplify(); - return r; - } /* binomialTInv */ - - /** - * Truncate polynomial degree. - * - * @param newdeg - * The maximum degree of the result. - * @return The polynomial with all coefficients beyond deg set to zero. - * If newdeg =3, for example the polynomial returned has at most - * degree 3. - * If newdeg is larger than the degree of this, zeros (at the higher - * orders of x) - * are appended. That polynomial would have formal degree larger - * than this. - * @since 2008-10-26 - */ - public RatPoly trunc(final int newdeg) { - final RatPoly t = new RatPoly(); - for (int i = 0; i <= newdeg; i++) { - t.set(i, at(i)); - } - t.simplify(); - return t; - } /* trunc */ - - /** - * Generate the roots of the polynomial in floating point arithmetic. - * - * @see Durand - * Kerner method - * @param the - * number of floating point digits - * @throws Error - * @since 2008-10-26 - */ - public Vector roots(final int digits) throws Error { - final RatPoly mon = monic(); - - final Random rand = new Random(); - final MathContext mc = new MathContext(digits + 3, RoundingMode.DOWN); - - Vector res = new Vector<>(); - - final int d = mon.degree(); - double randRad = 0.; - for (int i = 0; i <= d; i++) { - /* scale coefficient at maximum degree */ - final double absi = Math.abs(mon.at(i).doubleValue()); - if (absi > randRad) { - randRad = absi; - } - } - randRad += 1.0; - - /* - * initial values randomly in radius 1+randRad - */ - for (int i = 0; i < d; i++) { - final double rad = randRad * rand.nextDouble(); - final double phi = 2.0 * 3.14159 * rand.nextDouble(); - res.add(i, new BigComplex(rad * Math.cos(phi), rad * Math.sin(phi))); - } - - /* - * iterate until convr indicates that all values changed by less than - * the digits - * precision indicates. - */ - boolean convr = false; - for (; !convr;)// ORIGINAL LINE: for(int itr =0 ; ! convr ; itr++) - { - convr = true; - final Vector resPlus = new Vector<>(); - for (int v = 0; v < d; v++) { - /* - * evaluate f(x)/(x-root1)/(x-root2)/... (x-rootdegr), Newton - * method - */ - BigComplex thisx = res.elementAt(v); - BigComplex nv = mon.valueOf(thisx, mc); - for (int j = 0; j < d; j++) { - if (j != v) { - nv = nv.divide(thisx.subtract(res.elementAt(j)), mc); - } - } - - /* is this value converged ? */ - if (nv.abs(mc).doubleValue() > thisx.abs(mc).doubleValue() * Math.pow(10.0, -digits)) { - convr = false; - } - - thisx = thisx.subtract(nv); - - /* If unstable, start over */ - if (thisx.abs(MathContext.DECIMAL32).doubleValue() > randRad) { - return roots(digits); - } - - resPlus.add(thisx); - } - res = resPlus; - - } - return res; - } /* roots */ - - /** - * Generate the integer roots of the polynomial. - * - * @return The vector of integer roots, with multiplicity. - * The shows alternatingly first a root then its multiplicty, then - * another root and multiplicty etc. - * @since 2008-10-26 - */ - public Vector iroots() { - /* The vector of the roots */ - final Vector res = new Vector<>(); - - final int lowd = ldegree(); - if (lowd == 0 && a.elementAt(0).compareTo(BigInteger.ZERO) == 0) { - /* - * Case of polynomial identical to zero: - * reported as a simple root of value 0. - */ - res.add(BigInteger.ZERO); - res.add(BigInteger.ONE); - return res; - } - - /* - * multiply all coefs with the lcm() to get an integer polynomial - * start with denominator of first non-zero coefficient. - */ - BigInteger lcmDeno = a.elementAt(lowd).b; - for (int i = lowd + 1; i < degree(); i++) { - lcmDeno = BigIntegerMath.lcm(lcmDeno, a.elementAt(i).b); - } - - /* - * and eventually get the integer polynomial by ignoring the - * denominators - */ - final Vector ipo = new Vector<>(); - for (int i = 0; i < a.size(); i++) { - final BigInteger d = a.elementAt(i).a.multiply(lcmDeno).divide(a.elementAt(i).b); - ipo.add(d); - } - - final BigIntegerPoly p = new BigIntegerPoly(ipo); - /* - * collect the integer roots (multiple roots only once). Since we - * removed the zero already above, cand does not contain zeros. - */ - final Vector cand = p.iroots(); - for (int i = 0; i < cand.size(); i++) { - final BigInteger r = cand.elementAt(i); - final int deg = p.rootDeg(r); - res.add(r); - res.add(new BigInteger("" + deg)); - } - - return res; - } /* iroots */ - -} /* RatPoly */ diff --git a/core/src/main/java/org/nevec/rjm/Rational.java b/core/src/main/java/org/nevec/rjm/Rational.java deleted file mode 100644 index f11574a8..00000000 --- a/core/src/main/java/org/nevec/rjm/Rational.java +++ /dev/null @@ -1,900 +0,0 @@ -package org.nevec.rjm; - -import java.math.BigDecimal; -import java.math.BigInteger; -import java.math.MathContext; -import java.math.RoundingMode; - -import it.cavallium.warppi.util.Error; -import it.cavallium.warppi.util.Errors; - -/** - * Fractions (rational numbers). They are divisions of two BigInteger numbers, - * reduced to coprime numerator and denominator. - * - * @since 2006-06-25 - * @author Richard J. Mathar - */ -public class Rational implements Cloneable, Comparable { - /** - * numerator - */ - BigInteger a; - - /** - * denominator, always larger than zero. - */ - BigInteger b; - - /** - * The maximum and minimum value of a standard Java integer, 2^31. - * - * @since 2009-05-18 - */ - static public BigInteger MAX_INT = new BigInteger("2147483647"); - static public BigInteger MIN_INT = new BigInteger("-2147483648"); - - /** - * The constant 1. - */ - public static Rational ONE = new Rational(1, 1); - /** - * The constant 0. - */ - static public Rational ZERO = new Rational(); - - /** - * The constant 1/2 - * - * @since 2010-05-25 - */ - static public Rational HALF = new Rational(1, 2); - - /** - * Default ctor, which represents the zero. - * - * @since 2007-11-17 - */ - public Rational() { - a = BigInteger.ZERO; - b = BigInteger.ONE; - } - - /** - * ctor from a numerator and denominator. - * - * @param a - * the numerator. - * @param b - * the denominator. - */ - public Rational(final BigInteger a, final BigInteger b) { - this.a = a; - this.b = b; - normalize(); - } - - /** - * ctor from a numerator. - * - * @param a - * the BigInteger. - */ - public Rational(final BigInteger a) { - this.a = a; - b = new BigInteger("1"); - } - - /** - * ctor from a numerator and denominator. - * - * @param a - * the numerator. - * @param b - * the denominator. - */ - public Rational(final int a, final int b) { - this(new BigInteger("" + a), new BigInteger("" + b)); - } - - /** - * ctor from an integer. - * - * @param n - * the integer to be represented by the new instance. - * @since 2010-07-18 - */ - public Rational(final int n) { - this(n, 1); - } - - /** - * ctor from a string representation. - * - * @param str - * the string. This either has a slash in it, separating two - * integers, or, if there is no slash, is representing the - * numerator with implicit denominator equal to 1. Warning: this - * does not yet test for a denominator equal to zero - */ - public Rational(final String str) { - this(str, 10); - } - - /** - * ctor from a string representation in a specified base. - * - * @param str - * the string. This either has a slash in it, separating two - * integers, or, if there is no slash, is just representing the - * numerator. - * @param radix - * the number base for numerator and denominator Warning: this - * does not yet test for a denominator equal to zero - */ - public Rational(final String str, final int radix) { - final int hasslah = str.indexOf("/"); - if (hasslah == -1) { - a = new BigInteger(str, radix); - b = new BigInteger("1", radix); - /* no normalization necessary here */ - } else { - /* - * create numerator and denominator separately - */ - a = new BigInteger(str.substring(0, hasslah), radix); - b = new BigInteger(str.substring(hasslah + 1), radix); - normalize(); - } - } - - /** - * Create a copy. - * - * @since 2008-11-07 - */ - @Override - public Rational clone() { - /* - * protected access means this does not work return new - * Rational(a.clone(), b.clone()) ; - */ - final BigInteger aclon = new BigInteger("" + a); - final BigInteger bclon = new BigInteger("" + b); - return new Rational(aclon, bclon); - } /* Rational.clone */ - - /** - * Multiply by another fraction. - * - * @param val - * a second rational number. - * @return the product of this with the val. - */ - public Rational multiply(final Rational val) { - final BigInteger num = a.multiply(val.a); - final BigInteger deno = b.multiply(val.b); - /* - * Normalization to an coprime format will be done inside the ctor() and - * is not duplicated here. - */ - return new Rational(num, deno); - } /* Rational.multiply */ - - /** - * Multiply by a BigInteger. - * - * @param val - * a second number. - * @return the product of this with the value. - */ - public Rational multiply(final BigInteger val) { - final Rational val2 = new Rational(val, BigInteger.ONE); - return multiply(val2); - } /* Rational.multiply */ - - /** - * Multiply by an integer. - * - * @param val - * a second number. - * @return the product of this with the value. - */ - public Rational multiply(final int val) { - final BigInteger tmp = new BigInteger("" + val); - return multiply(tmp); - } /* Rational.multiply */ - - /** - * Power to an integer. - * - * @param exponent - * the exponent. - * @return this value raised to the power given by the exponent. If the - * exponent is 0, the value 1 is returned. - */ - public Rational pow(final int exponent) { - if (exponent == 0) { - return new Rational(1, 1); - } - - final BigInteger num = a.pow(Math.abs(exponent)); - final BigInteger deno = b.pow(Math.abs(exponent)); - if (exponent > 0) { - return new Rational(num, deno); - } else { - return new Rational(deno, num); - } - } /* Rational.pow */ - - /** - * Power to an integer. - * - * @param exponent - * the exponent. - * @return this value raised to the power given by the exponent. If the - * exponent is 0, the value 1 is returned. - * @throws Error - * @since 2009-05-18 - */ - public Rational pow(final BigInteger exponent) throws Error { - /* test for overflow */ - if (exponent.compareTo(Rational.MAX_INT) == 1) { - throw new Error(Errors.NUMBER_TOO_LARGE); - } - if (exponent.compareTo(Rational.MIN_INT) == -1) { - throw new Error(Errors.NUMBER_TOO_SMALL); - } - - /* promote to the simpler interface above */ - return pow(exponent.intValue()); - } /* Rational.pow */ - - /** - * r-th root. - * - * @param r - * the inverse of the exponent. 2 for the square root, 3 for the - * third root etc - * @return this value raised to the inverse power given by the root - * argument, this^(1/r). - * @throws Error - * @since 2009-05-18 - */ - public Rational root(final BigInteger r) throws Error { - /* test for overflow */ - if (r.compareTo(Rational.MAX_INT) == 1) { - throw new Error(Errors.NUMBER_TOO_LARGE); - } - if (r.compareTo(Rational.MIN_INT) == -1) { - throw new Error(Errors.NUMBER_TOO_SMALL); - } - - final int rthroot = r.intValue(); - /* cannot pull root of a negative value with even-valued root */ - if (compareTo(Rational.ZERO) == -1 && rthroot % 2 == 0) { - throw new Error(Errors.NEGATIVE_PARAMETER); - } - - /* - * extract a sign such that we calculate |n|^(1/r), still r carrying any - * sign - */ - final boolean flipsign = compareTo(Rational.ZERO) == -1 && rthroot % 2 != 0 ? true : false; - - /* - * delegate the main work to ifactor#root() - */ - final Ifactor num = new Ifactor(a.abs()); - final Ifactor deno = new Ifactor(b); - final Rational resul = num.root(rthroot).divide(deno.root(rthroot)); - if (flipsign) { - return resul.negate(); - } else { - return resul; - } - } /* Rational.root */ - - /** - * Raise to a rational power. - * - * @param exponent - * The exponent. - * @return This value raised to the power given by the exponent. If the - * exponent is 0, the value 1 is returned. - * @throws Error - * @since 2009-05-18 - */ - public Rational pow(final Rational exponent) throws Error { - if (exponent.a.compareTo(BigInteger.ZERO) == 0) { - return new Rational(1, 1); - } - - /* - * calculate (a/b)^(exponent.a/exponent.b) as - * ((a/b)^exponent.a)^(1/exponent.b) = tmp^(1/exponent.b) - */ - final Rational tmp = pow(exponent.a); - return tmp.root(exponent.b); - } /* Rational.pow */ - - /** - * Divide by another fraction. - * - * @param val - * A second rational number. - * @return The value of this/val - * @throws Error - */ - public Rational divide(final Rational val) throws Error { - if (val.compareTo(Rational.ZERO) == 0) { - throw new Error(Errors.DIVISION_BY_ZERO); - } - final BigInteger num = a.multiply(val.b); - final BigInteger deno = b.multiply(val.a); - /* - * Reduction to a coprime format is done inside the ctor, and not - * repeated here. - */ - return new Rational(num, deno); - } /* Rational.divide */ - - /** - * Divide by an integer. - * - * @param val - * a second number. - * @return the value of this/val - * @throws Error - */ - public Rational divide(final BigInteger val) throws Error { - if (val.compareTo(BigInteger.ZERO) == 0) { - throw new Error(Errors.DIVISION_BY_ZERO); - } - final Rational val2 = new Rational(val, BigInteger.ONE); - return divide(val2); - } /* Rational.divide */ - - /** - * Divide by an integer. - * - * @param val - * A second number. - * @return The value of this/val - * @throws Error - */ - public Rational divide(final int val) throws Error { - if (val == 0) { - throw new Error(Errors.DIVISION_BY_ZERO); - } - final Rational val2 = new Rational(val, 1); - return divide(val2); - } /* Rational.divide */ - - /** - * Add another fraction. - * - * @param val - * The number to be added - * @return this+val. - */ - public Rational add(final Rational val) { - final BigInteger num = a.multiply(val.b).add(b.multiply(val.a)); - final BigInteger deno = b.multiply(val.b); - return new Rational(num, deno); - } /* Rational.add */ - - /** - * Add another integer. - * - * @param val - * The number to be added - * @return this+val. - */ - public Rational add(final BigInteger val) { - final Rational val2 = new Rational(val, BigInteger.ONE); - return add(val2); - } /* Rational.add */ - - /** - * Add another integer. - * - * @param val - * The number to be added - * @return this+val. - * @since May 26 2010 - */ - public Rational add(final int val) { - final BigInteger val2 = a.add(b.multiply(new BigInteger("" + val))); - return new Rational(val2, b); - } /* Rational.add */ - - /** - * Compute the negative. - * - * @return -this. - */ - public Rational negate() { - return new Rational(a.negate(), b); - } /* Rational.negate */ - - /** - * Subtract another fraction. - * - * @param val - * the number to be subtracted from this - * @return this - val. - */ - public Rational subtract(final Rational val) { - final Rational val2 = val.negate(); - return add(val2); - } /* Rational.subtract */ - - /** - * Subtract an integer. - * - * @param val - * the number to be subtracted from this - * @return this - val. - */ - public Rational subtract(final BigInteger val) { - final Rational val2 = new Rational(val, BigInteger.ONE); - return subtract(val2); - } /* Rational.subtract */ - - /** - * Subtract an integer. - * - * @param val - * the number to be subtracted from this - * @return this - val. - */ - public Rational subtract(final int val) { - final Rational val2 = new Rational(val, 1); - return subtract(val2); - } /* Rational.subtract */ - - /** - * binomial (n choose m). - * - * @param n - * the numerator. Equals the size of the set to choose from. - * @param m - * the denominator. Equals the number of elements to select. - * @return the binomial coefficient. - * @since 2006-06-27 - * @author Richard J. Mathar - * @throws Error - */ - public static Rational binomial(final Rational n, final BigInteger m) throws Error { - if (m.compareTo(BigInteger.ZERO) == 0) { - return Rational.ONE; - } - Rational bin = n; - for (BigInteger i = new BigInteger("2"); i.compareTo(m) != 1; i = i.add(BigInteger.ONE)) { - bin = bin.multiply(n.subtract(i.subtract(BigInteger.ONE))).divide(i); - } - return bin; - } /* Rational.binomial */ - - /** - * binomial (n choose m). - * - * @param n - * the numerator. Equals the size of the set to choose from. - * @param m - * the denominator. Equals the number of elements to select. - * @return the binomial coefficient. - * @since 2009-05-19 - * @author Richard J. Mathar - * @throws Error - */ - public static Rational binomial(final Rational n, final int m) throws Error { - if (m == 0) { - return Rational.ONE; - } - Rational bin = n; - for (int i = 2; i <= m; i++) { - bin = bin.multiply(n.subtract(i - 1)).divide(i); - } - return bin; - } /* Rational.binomial */ - - /** - * Hankel's symbol (n,k) - * - * @param n - * the first parameter. - * @param k - * the second parameter, greater or equal to 0. - * @return Gamma(n+k+1/2)/k!/GAMMA(n-k+1/2) - * @since 2010-07-18 - * @author Richard J. Mathar - * @throws Error - */ - public static Rational hankelSymb(final Rational n, final int k) throws Error { - if (k == 0) { - return Rational.ONE; - } else if (k < 0) { - throw new Error(Errors.NEGATIVE_PARAMETER); - } - Rational nkhalf = n.subtract(k).add(Rational.HALF); - nkhalf = nkhalf.Pochhammer(2 * k); - final Factorial f = new Factorial(); - return nkhalf.divide(f.at(k)); - } /* Rational.binomial */ - - /** - * Get the numerator. - * - * @return The numerator of the reduced fraction. - */ - public BigInteger numer() { - return a; - } - - /** - * Get the denominator. - * - * @return The denominator of the reduced fraction. - */ - public BigInteger denom() { - return b; - } - - /** - * Absolute value. - * - * @return The absolute (non-negative) value of this. - */ - public Rational abs() { - return new Rational(a.abs(), b.abs()); - } - - /** - * floor(): the nearest integer not greater than this. - * - * @return The integer rounded towards negative infinity. - */ - public BigInteger floor() { - /* - * is already integer: return the numerator - */ - if (b.compareTo(BigInteger.ONE) == 0) { - return a; - } else if (a.compareTo(BigInteger.ZERO) > 0) { - return a.divide(b); - } else { - return a.divide(b).subtract(BigInteger.ONE); - } - } /* Rational.floor */ - - /** - * ceil(): the nearest integer not smaller than this. - * - * @return The integer rounded towards positive infinity. - * @since 2010-05-26 - */ - public BigInteger ceil() { - /* - * is already integer: return the numerator - */ - if (b.compareTo(BigInteger.ONE) == 0) { - return a; - } else if (a.compareTo(BigInteger.ZERO) > 0) { - return a.divide(b).add(BigInteger.ONE); - } else { - return a.divide(b); - } - } /* Rational.ceil */ - - /** - * Remove the fractional part. - * - * @return The integer rounded towards zero. - */ - public BigInteger trunc() { - /* - * is already integer: return the numerator - */ - if (b.compareTo(BigInteger.ONE) == 0) { - return a; - } else { - return a.divide(b); - } - } /* Rational.trunc */ - - /** - * Compares the value of this with another constant. - * - * @param val - * the other constant to compare with - * @return -1, 0 or 1 if this number is numerically less than, equal to, or - * greater than val. - */ - @Override - public int compareTo(final Rational val) { - /* - * Since we have always kept the denominators positive, simple - * cross-multiplying works without changing the sign. - */ - final BigInteger left = a.multiply(val.b); - final BigInteger right = val.a.multiply(b); - return left.compareTo(right); - } /* Rational.compareTo */ - - /** - * Compares the value of this with another constant. - * - * @param val - * the other constant to compare with - * @return -1, 0 or 1 if this number is numerically less than, equal to, or - * greater than val. - */ - public int compareTo(final BigInteger val) { - final Rational val2 = new Rational(val, BigInteger.ONE); - return compareTo(val2); - } /* Rational.compareTo */ - - /** - * Return a string in the format number/denom. If the denominator equals 1, - * print just the numerator without a slash. - * - * @return the human-readable version in base 10 - */ - @Override - public String toString() { - if (b.compareTo(BigInteger.ONE) != 0) { - return a.toString() + "/" + b.toString(); - } else { - return a.toString(); - } - } /* Rational.toString */ - - /** - * Return a double value representation. - * - * @return The value with double precision. - * @since 2008-10-26 - */ - public double doubleValue() { - /* - * To meet the risk of individual overflows of the exponents of a - * separate invocation a.doubleValue() or b.doubleValue(), we divide - * first in a BigDecimal environment and convert the result. - */ - final BigDecimal adivb = new BigDecimal(a).divide(new BigDecimal(b), MathContext.DECIMAL128); - return adivb.doubleValue(); - } /* Rational.doubleValue */ - - /** - * Return a float value representation. - * - * @return The value with single precision. - * @since 2009-08-06 - */ - public float floatValue() { - final BigDecimal adivb = new BigDecimal(a).divide(new BigDecimal(b), MathContext.DECIMAL128); - return adivb.floatValue(); - } /* Rational.floatValue */ - - /** - * Return a representation as BigDecimal. - * - * @param mc - * the mathematical context which determines precision, rounding - * mode etc - * @return A representation as a BigDecimal floating point number. - * @since 2008-10-26 - */ - public BigDecimal BigDecimalValue(final MathContext mc) { - /* - * numerator and denominator individually rephrased - */ - final BigDecimal n = new BigDecimal(a); - final BigDecimal d = new BigDecimal(b); - /* - * the problem with n.divide(d,mc) is that the apparent precision might - * be smaller than what is set by mc if the value has a precise - * truncated representation. 1/4 will appear as 0.25, independent of mc - */ - return BigDecimalMath.scalePrec(n.divide(d, mc), mc); - } /* Rational.BigDecimalValue */ - - /** - * Return a string in floating point format. - * - * @param digits - * The precision (number of digits) - * @return The human-readable version in base 10. - * @since 2008-10-25 - */ - public String toFString(final int digits) { - if (b.compareTo(BigInteger.ONE) != 0) { - final MathContext mc = new MathContext(digits, RoundingMode.DOWN); - final BigDecimal f = new BigDecimal(a).divide(new BigDecimal(b), mc); - return f.toString(); - } else { - return a.toString(); - } - } /* Rational.toFString */ - - /** - * Compares the value of this with another constant. - * - * @param val - * The other constant to compare with - * @return The arithmetic maximum of this and val. - * @since 2008-10-19 - */ - public Rational max(final Rational val) { - if (compareTo(val) > 0) { - return this; - } else { - return val; - } - } /* Rational.max */ - - /** - * Compares the value of this with another constant. - * - * @param val - * The other constant to compare with - * @return The arithmetic minimum of this and val. - * @since 2008-10-19 - */ - public Rational min(final Rational val) { - if (compareTo(val) < 0) { - return this; - } else { - return val; - } - } /* Rational.min */ - - /** - * Compute Pochhammer's symbol (this)_n. - * - * @param n - * The number of product terms in the evaluation. - * @return Gamma(this+n)/Gamma(this) = this*(this+1)*...*(this+n-1). - * @since 2008-10-25 - */ - public Rational Pochhammer(final BigInteger n) { - if (n.compareTo(BigInteger.ZERO) < 0) { - return null; - } else if (n.compareTo(BigInteger.ZERO) == 0) { - return Rational.ONE; - } else { - /* - * initialize results with the current value - */ - Rational res = new Rational(a, b); - BigInteger i = BigInteger.ONE; - for (; i.compareTo(n) < 0; i = i.add(BigInteger.ONE)) { - res = res.multiply(add(i)); - } - return res; - } - } /* Rational.pochhammer */ - - /** - * Compute pochhammer's symbol (this)_n. - * - * @param n - * The number of product terms in the evaluation. - * @return Gamma(this+n)/GAMMA(this). - * @since 2008-11-13 - */ - public Rational Pochhammer(final int n) { - return Pochhammer(new BigInteger("" + n)); - } /* Rational.pochhammer */ - - /** - * True if the value is integer. Equivalent to the indication whether a - * conversion to an integer can be exact. - * - * @since 2010-05-26 - */ - public boolean isBigInteger() { - return b.abs().compareTo(BigInteger.ONE) == 0; - } /* Rational.isBigInteger */ - - /** - * True if the value is integer and in the range of the standard integer. - * Equivalent to the indication whether a conversion to an integer can be - * exact. - * - * @since 2010-05-26 - */ - public boolean isInteger() { - if (!isBigInteger()) { - return false; - } - return a.compareTo(Rational.MAX_INT) <= 0 && a.compareTo(Rational.MIN_INT) >= 0; - } /* Rational.isInteger */ - - /** - * Conversion to an integer value, if this can be done exactly. - * - * @throws Error - * - * @since 2011-02-13 - */ - int intValue() throws Error { - if (!isInteger()) { - throw new Error(Errors.CONVERSION_ERROR); - } - return a.intValue(); - } - - /** - * Conversion to a BigInteger value, if this can be done exactly. - * - * @throws Error - * - * @since 2012-03-02 - */ - BigInteger BigIntegerValue() throws Error { - if (!isBigInteger()) { - throw new Error(Errors.CONVERSION_ERROR); - } - return a; - } - - /** - * True if the value is a fraction of two integers in the range of the - * standard integer. - * - * @since 2010-05-26 - */ - public boolean isIntegerFrac() { - return a.compareTo(Rational.MAX_INT) <= 0 && a.compareTo(Rational.MIN_INT) >= 0 && b.compareTo(Rational.MAX_INT) <= 0 && b.compareTo(Rational.MIN_INT) >= 0; - } /* Rational.isIntegerFrac */ - - /** - * The sign: 1 if the number is >0, 0 if ==0, -1 if <0 - * - * @return the signum of the value. - * @since 2010-05-26 - */ - public int signum() { - return b.signum() * a.signum(); - } /* Rational.signum */ - - /** - * Common lcm of the denominators of a set of rational values. - * - * @param vals - * The list/set of the rational values. - * @return LCM(denom of first, denom of second, ..,denom of last) - * @since 2012-03-02 - */ - static public BigInteger lcmDenom(final Rational[] vals) { - BigInteger l = BigInteger.ONE; - for (final Rational val : vals) { - l = BigIntegerMath.lcm(l, val.b); - } - return l; - } /* Rational.lcmDenom */ - - /** - * Normalize to coprime numerator and denominator. Also copy a negative sign - * of the denominator to the numerator. - * - * @since 2008-10-19 - */ - protected void normalize() { - /* - * compute greatest common divisor of numerator and denominator - */ - final BigInteger g = a.gcd(b); - if (g.compareTo(BigInteger.ONE) > 0) { - a = a.divide(g); - b = b.divide(g); - } - if (b.compareTo(BigInteger.ZERO) == -1) { - a = a.negate(); - b = b.negate(); - } - } /* Rational.normalize */ -} /* Rational */ diff --git a/core/src/main/java/org/nevec/rjm/SafeMathContext.java b/core/src/main/java/org/nevec/rjm/SafeMathContext.java deleted file mode 100644 index 95dd90e0..00000000 --- a/core/src/main/java/org/nevec/rjm/SafeMathContext.java +++ /dev/null @@ -1,27 +0,0 @@ -package org.nevec.rjm; - -import java.math.MathContext; -import java.math.RoundingMode; - -import it.cavallium.warppi.Engine; -import it.cavallium.warppi.Platform.ConsoleUtils; - -public final class SafeMathContext { - - public static MathContext newMathContext(int precision) { - if (precision <= 0) { - Engine.getPlatform().getConsoleUtils().out().print(ConsoleUtils.OUTPUTLEVEL_DEBUG_MIN, "Warning! MathContext precision is <= 0 (" + precision + ")"); - precision = 1; - } - return new MathContext(precision); - } - - public static MathContext newMathContext(int precision, final RoundingMode roundingMode) { - if (precision <= 0) { - Engine.getPlatform().getConsoleUtils().out().print(ConsoleUtils.OUTPUTLEVEL_DEBUG_MIN, "Warning! MathContext precision is <= 0 (" + precision + ")"); - precision = 1; - } - return new MathContext(precision, roundingMode); - } - -} diff --git a/core/src/main/java/org/nevec/rjm/Wigner3j.java b/core/src/main/java/org/nevec/rjm/Wigner3j.java deleted file mode 100644 index 114ddfa3..00000000 --- a/core/src/main/java/org/nevec/rjm/Wigner3j.java +++ /dev/null @@ -1,628 +0,0 @@ -package org.nevec.rjm; - -import java.math.BigInteger; -import java.util.Scanner; - -import it.cavallium.warppi.util.Error; - -/** - * Exact representations of Wigner 3jm and 3nj values of half-integer arguments. - * - * @see R. J. Mathar, Corrigendum to - * "Universal factorzation fo 3n-j (j>2) symbols ..[J. Phys. A: Math. Gen.37 (2004) 3259]" - * - * @see R. J. Mathar, Symmetries in - * Wigner 18-j and 21-j Symbols - * @since 2011-02-15 - * @author Richard J. Mathar - */ -public class Wigner3j { - /** - * Test programs. This supports three types of direct evaluations:
- * java -cp . org.nevec.rjm.Wigner3j 3jm 2j1+1 2j2+1 2j3+1 2m1+1 2m2+1 2m3+1 - *
- * java -cp . org.nevec.rjm.Wigner3j 6j 2j1+1 2j2+2 .. 2j6+1
- * java -cp . org.nevec.rjm.Wigner3j 9j 2j1+1 2j2+2 .. 2j9+1
- * The first command line argument is one of the three tags which determine - * whether a 3jm, a 6j or a 9j symbol will be computed. The other arguments - * are 6 or 9 integer values, which are the physical (half-integer) values - * multplied by 2 and augmented by 1. The order of the 6 or 9 values is as - * reading the corresponding standard symbol as first row, then second row - * (and for the 9j symbol) third row. - * - * @since 2011-02-15 - * @author Richard J. Mathar - * @throws Error - */ - static public void main(final String args[]) throws Error { - if (args[0].compareTo("6j") == 0) { - try { - final String m1 = "6"; - final String t1 = "1 2 -3 -1 5 6"; - final String t2 = "4 -5 3 -4 -2 -6"; - String j = ""; - for (int i = 1; i <= 6; i++) { - j += args[i] + " "; - } - final BigSurdVec w = Wigner3j.wigner3j(m1, t1, t2, j); - System.out.println(w.toString()); - } catch (final Exception e) { - System.out.println(e.getMessage()); - } - } else if (args[0].compareTo("9j") == 0) { - try { - final String m1 = "9"; - final String t1 = "1 3 2 4 6 5 7 9 8"; - final String t2 = "2 8 5 6 3 9 7 4 1"; - String j = ""; - for (int i = 1; i <= 9; i++) { - j += args[i] + " "; - } - final BigSurdVec w = Wigner3j.wigner3j(m1, t1, t2, j); - System.out.println(w.toString()); - } catch (final Exception e) { - System.out.println(e.getMessage()); - } - } else if (args[0].compareTo("3jm") == 0) { - final int j1 = new Integer(args[1]).intValue(); - final int j2 = new Integer(args[2]).intValue(); - final int j3 = new Integer(args[3]).intValue(); - final int m1 = new Integer(args[4]).intValue(); - final int m2 = new Integer(args[5]).intValue(); - final int m3 = new Integer(args[6]).intValue(); - try { - BigSurd w = Wigner3j.wigner3jm(j1, j2, j3, m1, m2, m3); - System.out.println(w.toString()); - w = w.multiply(new BigSurd(j3 + 1, 1)); - System.out.println("CG factor sqrt" + (j3 + 1) + "sign " + (j2 - j2 - m3) / 2 + " " + w.toString()); - } catch (final Exception e) { - System.out.println(e.getMessage()); - } - } else { - System.out.println("usage:"); - System.out.println(args[0] + " 6j 2j1+1 2j2+1 2j3+1 2j4+1 2j5+1 2j6+1"); - System.out.println(args[0] + " 9j 2j1+1 2j2+1 2j3+1 2j4+1 2j5+1 2j6+1.. 2j9+1 "); - System.out.println(args[0] + " 3jm 2j1+1 2j2+1 2j3+1 2m1+1 2m2+1 2m3+1 "); - } - } /* Wigner3j.main */ - - /** - * The Wigner 3jm symbol (j1,j2,j3,m1,m2,m3). All arguments of the function - * are the actual parameters multiplied by 2, so they all allow an integer - * representation. - * - * @param j1 - * integer representing 2*j1 - * @param j2 - * integer representing 2*j2 - * @param j3 - * integer representing 2*j3 - * @param m1 - * integer representing 2*m1 - * @param m2 - * integer representing 2*m2 - * @param m3 - * integer representing 2*m3 - * @return The value of the symbol. Zero if any of the triangular - * inequalities is violated or some parameters are out of range. - * @since 2011-02-13 - * @author Richard J. Mathar - * @throws Error - */ - static public BigSurd wigner3jm(final int j1, final int j2, final int j3, final int m1, final int m2, final int m3) - throws Error { - final Rational J1 = new Rational(j1, 2); - final Rational J2 = new Rational(j2, 2); - final Rational J3 = new Rational(j3, 2); - final Rational M1 = new Rational(m1, 2); - final Rational M2 = new Rational(m2, 2); - final Rational M3 = new Rational(m3, 2); - return Wigner3j.wigner3jm(J1, J2, J3, M1, M2, M3); - } /* wigner3jm */ - - /** - * Wigner 3jn symbol. For the 6j symbol, the input of the 3 lines is - * "1 2 3 1 5 6", "4 5 3 4 2 6" "2j1+1 2j2+1 2j3+1 2l1+1 2l2+1 2l3+1" - * - * @param m1 - * The information on the number of angular momenta. - * @param t1 - * The list of one half of the triads, indexing j, whitespace - * separated - * @param t2 - * The list of the second half of the triads, indexing j, - * whitespace separated - * @param j - * The list of the integer values of the angular momenta. They - * are actually the doubled j-values plus 1, whitespace - * separated. Only as many as announced by the m1 parameter are - * used; trailing numbers are ignored. - * @see A. Bar-Shalom and M. Klapisch, - * NJGRAF... - * , Comp. Phys Comm. 50 (3) (1988) 375 - * @since 2011-02-13 - * @since 2012-02-15 Upgraded return value to BigSurdVec - * @author Richard J. Mathar - * @throws Error - */ - static public BigSurdVec wigner3j(final String m1, final String t1, final String t2, final String j) throws Error { - /* - * The first number in the line "m" is the number of angular momenta. - * The rest of the line is ignored. - */ - Scanner s = new Scanner(m1); - final int m = s.nextInt(); - if (m % 3 != 0) { - s.close(); - throw new IllegalArgumentException("Angular momenta " + m + " not a multiple of three."); - } - - /* - * Scan the numbers in the line "j". Excess numbers beyond what has been - * announced in the "m" line are ignored. - */ - final int[] jvec = new int[m]; - final int[] tvec = new int[2 * m]; - - s.close(); - - /* - * the third row contains positive 2j+1. - */ - s = new Scanner(j); - int ji = 0; - while (s.hasNextInt() && ji < m) { - jvec[ji++] = s.nextInt(); - if (jvec[ji - 1] < 1) { - s.close(); - throw new IllegalArgumentException("Illegal value " + jvec[ji - 1] + " for 2j+1."); - } - } - - s.close(); - - /* - * the first two rows contain signed values of indices into the j list - */ - s = new Scanner(t1); - int ti = 0; - while (s.hasNextInt()) { - tvec[ti++] = s.nextInt(); - } - - s.close(); - - s = new Scanner(t2); - while (s.hasNextInt()) { - tvec[ti++] = s.nextInt(); - } - - /* - * Basic sanity checks. All indices in the first two lines address a - * number in the third line, and each index occurs exactly twice. - */ - if (ji % 3 != 0) { - s.close(); - throw new IllegalArgumentException("j-count " + ji + " not a multiple of three."); - } - if (ti != 2 * ji) { - s.close(); - throw new IllegalArgumentException("triad-count " + ti + " not twice j-count " + ji); - } - - final int[] jfreq = new int[m]; - for (ji = 0; ji < jfreq.length; ji++) { - jfreq[ji] = 0; - } - - /* - * maintain a 0-based index which shows where the j-value has its first - * and second occurrence in the flattened list of triads. - */ - final int[][] jhash = new int[m][2]; - - for (ti = 0; ti < 2 * m; ti++) { - final int t = tvec[ti]; - if (t == 0 || Math.abs(t) > jvec.length) { - s.close(); - throw new IllegalArgumentException("Triad index " + t + " out of bounds"); - } - if (jfreq[Math.abs(t) - 1] >= 2) { - s.close(); - throw new IllegalArgumentException("Node " + t + " referenced more than twice"); - } - jhash[Math.abs(t) - 1][jfreq[Math.abs(t) - 1]] = ti; - jfreq[Math.abs(t) - 1]++; - } - - /* - * Move on from the 2j+1 values of the input to the j-values. Subtract - * one and divide through 2. - */ - final Rational[] J = new Rational[jvec.length]; - for (ji = 0; ji < jvec.length; ji++) { - J[ji] = new Rational(jvec[ji] - 1, 2); - } - - /* - * Convert the 1-based indices to 0-based indices, loosing the sign - * information. - */ - final int[] triadidx = new int[tvec.length]; - for (ti = 0; ti < tvec.length; ti++) { - triadidx[ti] = Math.abs(tvec[ti]) - 1; - } - - /* - * The M-values are all null (undetermined) at the start. - */ - final Rational[] M = new Rational[J.length]; - s.close(); - return Wigner3j.wigner3j(tvec, J, M, triadidx); - } /* wigner3j */ - - /** - * Wigner 3jn symbol. Computes sum_{mi} (-1)^(j1-m1+j2-m2+...) - * triad(triadidx[0..2])*triad(triadidx[3..5])*... where each factor is a - * Wigner-3jm symbol with each sign of m_i occurring once at the - * corresponding l-value. - * - * @param triadidx - * 0-based indices into the list of J - * @param J - * The list of J-values - * @param M - * The list of M-values associated with the J. This contains null - * where the parameter has not yet been set by an outer loop. - * @since 2011-02-13 - * @since 2012-02-15 Upgraded to return BigSurdVec - * @author Richard J. Mathar - * @throws Error - */ - static private BigSurdVec wigner3j(final int[] tvec, final Rational[] J, final Rational[] M, final int[] triadidx) - throws Error { - /* - * The result of the computation. The sum over all m-combinations of the - * triads. - */ - BigSurdVec res = new BigSurdVec(); - - /* - * First step is to monitor the triangular conditions on the J. If at - * least one is violated, the result is zero. Loop over the triads. - */ - for (int t = 0; t < triadidx.length; t += 3) { - /* Ensure |J[t]-J[t+1]| <= J[t+2] <= J[t]+J[t+1] */ - if (J[triadidx[t]].subtract(J[triadidx[t + 1]]).abs().compareTo(J[triadidx[t + 2]]) > 0) { - return res; - } - if (J[triadidx[t]].add(J[triadidx[t + 1]]).compareTo(J[triadidx[t + 2]]) < 0) { - return res; - } - } - - /* - * the index of the preferred member of the triad list. Preference given - * to those dangling in triads where alreaday two others are fixed, then - * to members where at least one is fixed, then to smallest associated - * J-values. - */ - int freeM = -1; - int freeMrank = -1; - for (int i = 0; i < triadidx.length; i++) { - /* - * found an m-value which has not yet been summed over. - */ - if (M[triadidx[i]] == null) { - /* - * two cases: value is fixed implicitly because already two - * others values are set in the triad. or it is still to - * maintain its own explicit loop. - */ - final int triadn = i / 3; - final int triadr = i % 3; - /* - * the neighbors in the triad have indices triadn*3+ (tiradr+1) - * mod 3 and triadn*3+(triadr+2) mod3 - */ - final int nei1 = 3 * triadn + (triadr + 1) % 3; - final int nei2 = 3 * triadn + (triadr + 2) % 3; - - /* - * found a candidate for which the two other values are already - * set. - */ - if (M[triadidx[nei1]] != null && M[triadidx[nei2]] != null) { - freeM = i; - break; - } else { - /* - * rough work load estimator: basically (2J1+1)*(2J2+1) - */ - Rational wt = J[triadidx[i]].multiply(2).add(1); - if (M[triadidx[nei1]] == null) { - wt = wt.multiply(J[triadidx[nei1]].multiply(2).add(1)); - } - if (M[triadidx[nei2]] == null) { - wt = wt.multiply(J[triadidx[nei2]].multiply(2).add(1)); - } - final int thiswt = wt.intValue(); - if (freeM < 0 || thiswt < freeMrank) { - freeM = i; - freeMrank = thiswt; - } - } - } - } - - if (freeM >= 0) { - /* - * found an m-value which has not yet been summed over. - */ - if (M[triadidx[freeM]] == null) { - final Rational[] childM = new Rational[M.length]; - for (int ji = 0; ji < M.length; ji++) { - if (M[ji] != null) { - childM[ji] = M[ji]; - } - } - - /* - * two cases: value is fixed implicitly because already two - * others values are set in the triad. or it is still to - * maintain its own explicit loop. - */ - final int triadn = freeM / 3; - final int triadr = freeM % 3; - /* - * the neighbors in the triad have indices triadn*3+ (triadr+1) - * mod 3 and triadn*3+(triadr+2) mod3 - */ - final int nei1 = 3 * triadn + (triadr + 1) % 3; - final int nei2 = 3 * triadn + (triadr + 2) % 3; - if (M[triadidx[nei1]] == null || M[triadidx[nei2]] == null) { - /* - * The J-value is J[triadidx[freeM]]. Loop from -J to +J, - * the allowed range. - */ - Rational newm = J[triadidx[freeM]].negate(); - while (newm.compareTo(J[triadidx[freeM]]) <= 0) { - childM[triadidx[freeM]] = tvec[freeM] > 0 ? newm : newm.negate(); - res = res.add(Wigner3j.wigner3j(tvec, J, childM, triadidx)); - newm = newm.add(Rational.ONE); - } - } else { - /* - * Set its value and the value at its companion j-value. Sum - * of the three m-values in the triad is to be zero for a - * non-zero contribution. - */ - Rational m1 = M[triadidx[nei1]]; - Rational m2 = M[triadidx[nei2]]; - /* - * negate if these are the second occurrences of the J in - * the triads - */ - if (tvec[nei1] < 0) { - m1 = m1.negate(); - } - if (tvec[nei2] < 0) { - m2 = m2.negate(); - } - /* m3 = -(m1+m2) */ - final Rational newm = tvec[freeM] > 0 ? m1.add(m2).negate() : m1.add(m2); - /* - * No contribution if the m-value enforced by the other two - * entries is outside the range -|J|..|J| enforced by its - * associated J-value. One could essentially remove this - * branching and let wigner3j() decide on this, but this is - * inefficient. - */ - if (newm.abs().compareTo(J[triadidx[freeM]]) <= 0) { - childM[triadidx[freeM]] = newm; - res = res.add(Wigner3j.wigner3j(tvec, J, childM, triadidx)); - } - /* - * zero contribution if this m-value cannot be set to any - * value compatible with the triangular conditions. - */ - } - return res; - } - } - - /* - * reached the bottom of the loop where all M-values are assigned. Build - * the product over all Wigner-3jm values and the associated sign. - */ - res = BigSurdVec.ONE; - for (int ji = 0; ji < triadidx.length; ji += 3) { - Rational m1 = M[triadidx[ji]]; - Rational m2 = M[triadidx[ji + 1]]; - Rational m3 = M[triadidx[ji + 2]]; - /* - * negate if these are associated with in-flowing vectors in the - * triads - */ - if (tvec[ji] < 0) { - m1 = m1.negate(); - } - if (tvec[ji + 1] < 0) { - m2 = m2.negate(); - } - if (tvec[ji + 2] < 0) { - m3 = m3.negate(); - } - res = res.multiply(Wigner3j.wigner3jm(J[triadidx[ji]], J[triadidx[ji + 1]], J[triadidx[ji + 2]], m1, m2, m3)); - - /* - * if a partial product yields zero, the total product is zero, too, - * and offers an early exit. - */ - if (res.signum() == 0) { - return BigSurdVec.ZERO; - } - } - /* - * The overal sign is product_{J-Mpairs} (-1)^(J-M). This is an integer - * because all the J-M are integer. - */ - Rational sig = new Rational(); - for (int ji = 0; ji < J.length; ji++) { - sig = sig.add(J[ji]).subtract(M[ji]); - } - /* - * sign depends on the sum being even or odd. We assume that "sig" is - * integer and look only at the numerator - */ - if (sig.a.abs().testBit(0)) { - res = res.negate(); - } - return res; - } /* wigner3j */ - - /** - * The Wigner 3jm symbol (j1,j2,j3,m1,m2,m3). Warning: there is no check - * that each argument is indeed half-integer. - * - * @param j1 - * integer or half-integer j1 - * @param j2 - * integer or half-integer j2 - * @param j3 - * integer or half-integer j3 - * @param m1 - * integer or half-integer m1 - * @param m2 - * integer or half-integer m2 - * @param m3 - * integer or half-integer m3 - * @return The value of the symbol. Zero if any of the triangular - * inequalities is violated or some parameters are out of range. - * @since 2011-02-13 - * @author Richard J. Mathar - * @throws Error - */ - static protected BigSurd wigner3jm(final Rational j1, final Rational j2, final Rational j3, final Rational m1, - final Rational m2, final Rational m3) throws Error { - /* - * Check that m1+m2+m3 = 0 - */ - if (m1.add(m2).add(m3).signum() != 0) { - return BigSurd.ZERO; - } - - /* - * Check that j1+j2+j3 is integer - */ - if (j1.add(j2).add(j3).isBigInteger() == false) { - return BigSurd.ZERO; - } - - /* - * Check that |j1-j2|<=j3 <= |j1+j2| - */ - final Rational j1m2 = j1.subtract(j2); - if (j1m2.abs().compareTo(j3) > 0) { - return BigSurd.ZERO; - } - final Rational j1p2 = j1.add(j2); - if (j1p2.abs().compareTo(j3) < 0) { - return BigSurd.ZERO; - } - - /* - * Check that |m_i| <= j_i - */ - if (m1.abs().compareTo(j1) > 0 || m2.abs().compareTo(j2) > 0 || m3.abs().compareTo(j3) > 0) { - return BigSurd.ZERO; - } - - /* - * Check that m_i-j_i are integer. - */ - if (!m1.subtract(j1).isBigInteger() || !m2.subtract(j2).isBigInteger() || !m3.subtract(j3).isBigInteger()) { - return BigSurd.ZERO; - } - - /* - * (-)^(j1-j2-m3)*delta(-m3,m1+m2)*sqrt[ (j3+j1-j2)! (j3-j1+j2)! - * (j1+j2-j3)! /(j1+j2+j3+1)! - * *(j3-m)!*(j3+m)!(j1-m1)!*(j1+m1)!*(j2-m2)!*(j2+m2)!] *sum_k - * (-1)^k/[k!(j1+j2-j3-k)!(j1-m1-k)!(j2+m2-k)!(j3-j2+m1+k)!)*(j3-j1-m2+k - * )!] - */ - - /* - * It is tacitly assumed that all the major j_i, m_i values are in the - * integer range. This is implicitly plausible since otherwise the - * execution times of the following loop over the k-values would be - * immense. - */ - int j1j2jk = j1p2.subtract(j3).intValue(); - int j1m1k = j1.subtract(m1).intValue(); - int j2m2k = j2.add(m2).intValue(); - int jj2m1k = j3.subtract(j2).add(m1).intValue(); - int jj1m2k = j3.subtract(j1).subtract(m2).intValue(); - - int k = Math.max(0, -jj2m1k); - k = Math.max(k, -jj1m2k); - if (k > 0) { - j1j2jk -= k; - j1m1k -= k; - j2m2k -= k; - jj2m1k += k; - jj1m2k += k; - } - - final Factorial f = new Factorial(); - Rational sumk = new Rational(); - while (true) { - final BigInteger d = f.at(k).multiply(f.at(j1j2jk)).multiply(f.at(j1m1k)).multiply(f.at(j2m2k)).multiply(f.at(jj2m1k)).multiply(f.at(jj1m2k)); - if (k % 2 == 0) { - sumk = sumk.add(new Rational(BigInteger.ONE, d)); - } else { - sumk = sumk.subtract(new Rational(BigInteger.ONE, d)); - } - j1j2jk--; - j1m1k--; - j2m2k--; - jj2m1k++; - jj1m2k++; - if (j1j2jk < 0 || j1m1k < 0 || j2m2k < 0) { - break; - } - k++; - } - /* - * sign factor (-1)^(j1-j2-m3) - */ - if (j1m2.subtract(m3).intValue() % 2 != 0) { - sumk = sumk.negate(); - } - - k = j1m2.add(j3).intValue(); - BigInteger s = f.at(k); - k = j3.subtract(j1m2).intValue(); - s = s.multiply(f.at(k)); - k = j1p2.subtract(j3).intValue(); - s = s.multiply(f.at(k)); - k = j3.add(m3).intValue(); - s = s.multiply(f.at(k)); - k = j3.subtract(m3).intValue(); - s = s.multiply(f.at(k)); - k = j1.add(m1).intValue(); - s = s.multiply(f.at(k)); - k = j1.subtract(m1).intValue(); - s = s.multiply(f.at(k)); - k = j2.add(m2).intValue(); - s = s.multiply(f.at(k)); - k = j2.subtract(m2).intValue(); - s = s.multiply(f.at(k)); - k = j1p2.add(j3).intValue(); - k++; - final Rational disc = new Rational(s, f.at(k)); - return new BigSurd(sumk, disc); - } /* wigner3jm */ - -} /* Wigner3j */ diff --git a/core/src/main/java/org/nevec/rjm/Wigner3jGUI.java b/core/src/main/java/org/nevec/rjm/Wigner3jGUI.java deleted file mode 100644 index 8cffd15f..00000000 --- a/core/src/main/java/org/nevec/rjm/Wigner3jGUI.java +++ /dev/null @@ -1,335 +0,0 @@ -package org.nevec.rjm; - -import java.awt.Color; -import java.awt.Font; -import java.awt.GridBagConstraints; -import java.awt.GridBagLayout; -import java.awt.Label; -import java.awt.TextArea; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.util.Scanner; - -import javax.swing.JButton; -import javax.swing.JFrame; -import javax.swing.JList; -import javax.swing.event.ListSelectionEvent; -import javax.swing.event.ListSelectionListener; - -import it.cavallium.warppi.util.Error; - -/** - * An interactive interface to the Wigner3j class. The GUI allows to preselect - * one of the symbols if the number of j-terms is small (6j up to 15j), or to - * enter any other connectivity for the triads of j-values. The actual j-values - * are entered as integers (2j+1) and the computation of one value (in exact - * square root representation) is started manually. - * - * @since 2011-02-15 - */ -public class Wigner3jGUI implements ActionListener, ListSelectionListener { - /** - * The master window of the session - */ - JFrame fram; - - /* - * global labels - */ - Label Lbl0; - Label Lbl1; - - JButton sear; - JList searJ; - String[] searOpt = { "6j", "9j", "12j 1st", "12j 2nd (not symm)", "15j 1st", "15j 2nd", "15j 3rd", "15j 4th", "15j 5th" }; - - /** - * Field with the triads inputs - */ - TextArea inpGtria; - - /** - * Field with the J-value inputs - */ - TextArea inpGjval; - - /** - * Field of the outputs. - */ - TextArea outG; - - GridBagLayout gridbag; - GridBagConstraints gridconstr; - - /** - * @since 2011-02-15 - */ - public void init() { - fram = new JFrame("Wigner3jGUI"); - - Lbl0 = new Label("Input: (Triads upper area, values 2J+1 second area"); - Lbl1 = new Label("Output:"); - - sear = new JButton("Compute"); - sear.setActionCommand("compute"); - sear.addActionListener(this); - sear.setToolTipText("Compute a general 3jn value"); - - searJ = new JList(searOpt); - searJ.setLayoutOrientation(JList.HORIZONTAL_WRAP); - searJ.addListSelectionListener(this); - - final Font defFont = new Font("Monospaced", Font.PLAIN, 11); - - fram.setBackground(new Color(250, 250, 250)); - fram.setForeground(new Color(0, 0, 0)); - final Color fg = new Color(0, 200, 0); - final Color bg = new Color(10, 10, 10); - - gridbag = new GridBagLayout(); - fram.setLayout(gridbag); - - gridconstr = new GridBagConstraints(); - gridconstr.gridx = 0; - gridconstr.gridy = GridBagConstraints.RELATIVE; - - inpGtria = new TextArea("", 4, 80); - inpGtria.setFont(defFont); - inpGtria.setForeground(fg); - inpGtria.setBackground(bg); - - inpGjval = new TextArea("", 10, 80); - inpGjval.setFont(defFont); - inpGjval.setForeground(fg); - inpGjval.setBackground(bg); - - outG = new TextArea("", 12, 80); - outG.setEditable(false); - outG.setFont(defFont); - outG.setForeground(fg); - outG.setBackground(bg); - - fram.add(Lbl0); - gridbag.setConstraints(Lbl0, gridconstr); - - fram.add(inpGtria); - gridbag.setConstraints(inpGtria, gridconstr); - - fram.add(inpGjval); - gridbag.setConstraints(inpGjval, gridconstr); - - fram.add(sear); - gridbag.setConstraints(sear, gridconstr); - fram.add(searJ); - gridbag.setConstraints(searJ, gridconstr); - - fram.add(Lbl1); - gridbag.setConstraints(Lbl1, gridconstr); - - fram.add(outG); - gridbag.setConstraints(outG, gridconstr); - - fram.pack(); - fram.setVisible(true); - } /* init */ - - /** - * @throws Error - * @since 2010-08-27 - */ - public void compute() throws Error { - final String tr = inpGtria.getText(); - final String[] trias = new String[4]; - - /* - * Read the trias configuration from inpGtria into trias[0..2], skipping - * lines that start with a hash mark. - */ - Scanner s = new Scanner(tr); - for (int l = 0; l < 3;) { - try { - trias[l] = s.nextLine().trim(); - if (!trias[l].startsWith("#")) { - l++; - } - } catch (final Exception e) { - s.close(); - outG.setText("ERROR: less than 3 lines in the triad definition"); - return; - } - } - - s.close(); - - /* - * Read the J values from inpGjval into trias[3] in a loop - */ - final String j = inpGjval.getText(); - s = new Scanner(j); - while (true) { - try { - trias[3] = s.nextLine().trim(); - } catch (final Exception e) { - s.close(); - return; - } - if (!trias[3].startsWith("#")) { - try { - final BigSurdVec w = Wigner3j.wigner3j(trias[0], trias[1], trias[2], trias[3]); - outG.append(w.toString() + " = " + w.doubleValue()); - } catch (final Exception e) { - outG.append(e.toString()); - e.printStackTrace(); - } - outG.append(" # J = "); - final Scanner num = new Scanner(trias[3]); - while (num.hasNextInt()) { - final int twoj1 = num.nextInt(); - final Rational jfrac = new Rational(twoj1 - 1, 2); - outG.append(jfrac.toString() + " "); - } - outG.append("\n"); - num.close(); - } - } - } /* compute */ - - /** - * Interpreter parser loop. - * - * @param e - * the information on which button had been pressed in the GUI - * @since 2011-02-15 - */ - @Override - public void actionPerformed(final ActionEvent e) { - final String lin = e.getActionCommand(); - /* - * debugging System.out.println("Ac"+e.paramString()) ; - * System.out.println(lin) ; - */ - if (lin == "compute") { - outG.setText(""); - try { - compute(); - } catch (final Error e1) { - // TODO Auto-generated catch block - e1.printStackTrace(); - } - } - } /* actionPerformed */ - - /** - * Interpreter parser loop. - * - * @param e - * the information on which of the 3jn templates had been - * selected in the Menu - * @since 2011-02-18 - */ - @Override - public void valueChanged(final ListSelectionEvent e) { - switch (searJ.getMinSelectionIndex()) { - case 0: - inpGtria.setText("6\n"); - inpGtria.append("1 2 -3 -1 5 6\n"); - inpGtria.append("4 -5 3 -4 -2 -6"); - outG.setText(""); - break; - case 1: - /* - * Yutsis Figure 18.1 index map. j1=1, j2=2, j3=3 k1=4, k2=5, - * k3=6 - * l1=7, l2=8, l3=9 - */ - inpGtria.setText("9\n"); - inpGtria.append("1 3 2 4 6 5 7 9 8 # (j1 j3 j2) (k1 k3 k2) (l1 l3 l2)\n"); - inpGtria.append("-2 -8 -5 -6 -3 -9 -7 -4 -1 # (j2 l2 k2) (k3 j3 l3) (l1 k1 j1)"); - outG.setText(""); - break; - case 2: - /* - * Yutsis Figure 19.1 and 19.2, index map, including the sign - * reveral of the l. Assume input order j1..j4, l1..l4, k1..k4. - * j1=1, j2=2, j3=3, j4=4 l1=5, l2=6, l3=7, l4=8 k1=9, k2=10, - * k3=11, - * k4=12 - */ - inpGtria.setText("12\n"); - inpGtria.append("1 12 -8 -1 5 -2 2 6 -3 3 7 -4 # (j1 k4 l4) (j1 l1 j2) (j2 l2 j3) (j3 l3 j4)\n"); - inpGtria.append("4 8 -9 9 -5 -10 10 -6 -11 11 -7 -12 # (j4 l4 k1) (k1 l1 k2) (k2 l2 k3) (k3 l3 k4)"); - outG.setText(""); - break; - case 3: - inpGtria.setText("12\n"); - inpGtria.append("1 5 9 -9 -2 -7 2 11 8 -8 -12 -4 # (j1 l1 k1) (k1 j2 l3 ) (j2 k3 l4) (l4 k4 j4)\n"); - inpGtria.append("4 7 10 -10 -3 -5 3 6 12 -6 -11 -1 # (j4 l3 k2) (k2 j3 l1) (j3 l2 k4) (l2 k3 j1)"); - outG.setText(""); - break; - case 4: - /* - * Yutsis Figure 20.2 to 20.3, index map. j1=1, j2=2, j3=3, - * j4=4, - * j5=5 l1=6, l2=7, l3=8, l4=9, l5=10 k1=11, k2=12, k3=13, - * k4=14, - * k5=15 - */ - inpGtria.setText("15\n"); - inpGtria.append("1 -6 2 -2 -7 3 -3 -8 4 -4 -9 5 -5 -10 11 # (j1 l1 j2)(j2 l2 j3)(j3 l3 j4)(j4 l4 j5)(j5 l5 k1)\n"); - inpGtria.append("-11 6 12 -12 7 13 -13 8 14 -14 9 15 -15 10 -1 # (k1 l1 k2)(k2 l2 k3)(k3 l3 k4)(k4 l4 k5)(k5 l5 j1)"); - outG.setText(""); - break; - case 5: - inpGtria.setText("15\n"); - inpGtria.append("-1 -6 2 -2 -7 3 -3 -8 4 -4 -9 5 1 -5 -10 # (j1 l1 j2)(j2 l2 j3)(j3 l3 j4)(j4 l4 j5)(j1 j5 l5)\n"); - inpGtria.append("11 -15 10 9 15 -14 8 14 -13 7 13 -12 6 12 -11 # (k1 k5 l5)(l4 k5 k4)(l3 k4 k3)(l2 k3 k2)(l1 k2 k1)"); - outG.setText(""); - break; - case 6: - /* - * Yutsis Figure 20.4a, index map. k1=1, k1'=2, k=3, k'=4, k2=5, - * k2'=6 p1=7, p=8, p2=9, j1=10, j1'=11 j=12 j'=13 j2=14 j2'=15 - */ - inpGtria.setText("15\n"); - inpGtria.append("-13 -12 -8 12 14 10 -10 -1 7 -7 -11 -2 2 4 6 # (j' j p)(j j2 j1)(j1 k1 p1)(p1 j1' k1')(k1' k' k2')\n"); - inpGtria.append("-4 -3 8 1 3 5 -14 -5 9 -15 -6 -9 15 11 13 # (k' k p)(k1 k k2)(j2 k2 p2)(j2' k2' p2)(j2' j1' j')"); - outG.setText(""); - break; - case 7: - /* - * Yutsis Figure 20.5a, index map. j1=1, k1=2 s1=3 k1'=4 j1'=5 - * p=6 - * l=7 s=8 l'=9 p'=10 j2=11 k2=12 s2=13 k2'=14 j2'=15 - */ - inpGtria.setText("15\n"); - inpGtria.append("-14 -12 -8 12 11 -10 -11 13 -7 7 -1 3 2 1 6 # (k2' k2 s)(k2 j2 p')(j2 s2 l)(l j1 s1)(k1 j1 p)\n"); - inpGtria.append("-4 -2 8 10 4 5 9 -5 -3 -13 -9 -15 15 -6 14 # (k1' k1 s)(p' k1' j1')(l' j1' s1)(s2 l' j2')(j2' p k2')"); - outG.setText(""); - break; - case 8: - /* - * Yutsis Figure 20.6, index map. k1=1 k1'=2 j1=3 l1=4 l1'=5 - * k2=6 - * k2'=7 j2=8 l2=9 l2'=10 k3=11 k3'=12 j3=13 l3=14 l3'=15 - */ - inpGtria.setText("15\n"); - inpGtria.append("-15 1 -7 -4 -11 7 5 4 -3 -12 -5 6 12 -9 -1 # (l3' k1 k2')(l1 k3 k2')(l1' l1 j1)(k3' l1' k2)(k3' l2 k1)\n"); - inpGtria.append("9 -8 10 -10 11 -2 -14 -6 2 14 -13 15 3 8 13 # (l2 j2 l2')(l2' k3 k1')(l3 k2 k1')(l3 j3 l3')(j1 j2 j3)"); - outG.setText(""); - break; - } - } /* valueChanged */ - - /** - * Main entry point. not taking any command line options:
- * java -jar Wigner3jGUI.jar
- * - * @since 2012-02-16 - * @author Richard J. Mathar - */ - public static void main(final String[] args) { - final Wigner3jGUI g = new Wigner3jGUI(); - g.init(); - } /* main */ - -} /* Wigner3jGUI */ diff --git a/desktop/pom.xml b/desktop/pom.xml index 58f9edd3..c16aa18e 100644 --- a/desktop/pom.xml +++ b/desktop/pom.xml @@ -10,20 +10,18 @@ warppi-desktop - bundle - WarpPI Calculator Desktop WarpPI Calculator desktop project it.cavallium warppi-core - ${project.version} + 0.9.0a2 it.cavallium warppi-engine-jogl - ${project.version} + 0.9.0a2 org.fusesource.jansi @@ -55,23 +53,15 @@ + + org.apache.maven.plugins + maven-compiler-plugin + org.apache.maven.plugins maven-source-plugin - - org.apache.felix - maven-bundle-plugin - true - - - * - it.cavallium.warppi.* - warppi-desktop - - - org.apache.maven.plugins diff --git a/engine-jogl/pom.xml b/engine-jogl/pom.xml index c828b7a5..aa7a3f36 100644 --- a/engine-jogl/pom.xml +++ b/engine-jogl/pom.xml @@ -10,15 +10,13 @@ warppi-engine-jogl - bundle - WarpPI Calculator JOGL Engine WarpPI Calculator engine-jogl project it.cavallium warppi-core - ${project.version} + 0.9.0a2 org.jogamp.jogl @@ -38,16 +36,8 @@ - org.apache.felix - maven-bundle-plugin - true - - - * - it.cavallium.warppi.* - warppi-engine-jogl - - + org.apache.maven.plugins + maven-compiler-plugin org.apache.maven.plugins diff --git a/libs/raspi2fb b/hardware/libs/raspi2fb similarity index 100% rename from libs/raspi2fb rename to hardware/libs/raspi2fb diff --git a/hardware/pom.xml b/hardware/pom.xml index 1af4fd9b..e51cb1f7 100644 --- a/hardware/pom.xml +++ b/hardware/pom.xml @@ -18,12 +18,12 @@ it.cavallium warppi-core - ${project.version} + 0.9.0a2 it.cavallium warppi-engine-jogl - ${project.version} + 0.9.0a2 com.pi4j diff --git a/pom.xml b/pom.xml index 5bf95ad3..daf4c2cb 100755 --- a/pom.xml +++ b/pom.xml @@ -39,6 +39,8 @@ core + util + bigdecimal-math Flow rules diff --git a/rules/pom.xml b/rules/pom.xml index 0e81bb93..52156d1d 100644 --- a/rules/pom.xml +++ b/rules/pom.xml @@ -9,7 +9,6 @@ 0.9.0a2 warppi-rules - bundle WarpPI Calculator Rules WarpPI Calculator rules project @@ -18,23 +17,15 @@ it.cavallium warppi-core - ${project.version} + 0.9.0a2 - org.apache.felix - maven-bundle-plugin - true - - - * - it.cavallium.warppi.* - warppi-rules - - + org.apache.maven.plugins + maven-compiler-plugin org.apache.maven.plugins diff --git a/teavm/pom.xml b/teavm/pom.xml index 1493dc03..fc8c9325 100644 --- a/teavm/pom.xml +++ b/teavm/pom.xml @@ -17,12 +17,12 @@ it.cavallium warppi-core - ${project.version} + 0.9.0a2 it.cavallium warppi-rules - ${project.version} + 0.9.0a2 org.teavm diff --git a/util/.classpath b/util/.classpath new file mode 100644 index 00000000..5e8a55fe --- /dev/null +++ b/util/.classpath @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/util/.gitignore b/util/.gitignore new file mode 100644 index 00000000..b83d2226 --- /dev/null +++ b/util/.gitignore @@ -0,0 +1 @@ +/target/ diff --git a/util/.project b/util/.project new file mode 100644 index 00000000..d1a589e7 --- /dev/null +++ b/util/.project @@ -0,0 +1,23 @@ + + + warppi-util + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.m2e.core.maven2Nature + + diff --git a/util/pom.xml b/util/pom.xml new file mode 100644 index 00000000..84e1c6a6 --- /dev/null +++ b/util/pom.xml @@ -0,0 +1,38 @@ + + + 4.0.0 + + + it.cavallium + warppi + 0.9.0a2 + + warppi-util + + warppi-util + WarpPI Util + + + + cavallium + Andrea Cavalli + andrea@warp.ovh + https://cavallium.it/ + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + org.apache.maven.plugins + maven-source-plugin + + + + \ No newline at end of file diff --git a/core/src/main/java/it/cavallium/warppi/util/Error.java b/util/src/main/java/it/cavallium/warppi/util/Error.java similarity index 100% rename from core/src/main/java/it/cavallium/warppi/util/Error.java rename to util/src/main/java/it/cavallium/warppi/util/Error.java diff --git a/core/src/main/java/it/cavallium/warppi/util/Errors.java b/util/src/main/java/it/cavallium/warppi/util/Errors.java similarity index 100% rename from core/src/main/java/it/cavallium/warppi/util/Errors.java rename to util/src/main/java/it/cavallium/warppi/util/Errors.java From 1d4c2ec8a7433da956059ed558d80b1616c3fb38 Mon Sep 17 00:00:00 2001 From: Andrea Cavalli Date: Mon, 15 Oct 2018 23:10:44 +0200 Subject: [PATCH 11/18] Removed useless dependencies --- Flow | 2 +- bigdecimal-math | 2 +- core/pom.xml | 8 +- .../java/it/cavallium/warppi/Platform.java | 18 +- .../it/cavallium/warppi/device/Keyboard.java | 126 ++-- .../warppi/device/KeyboardAWTValues.java | 700 ++++++++++++++++++ .../warppi/device/graphicengine/RAWFont.java | 16 - .../cavallium/warppi/gui/DisplayManager.java | 3 +- .../graphicengine/impl/common/PngSkin.java | 15 +- .../graphicengine/impl/common/RFTFont.java | 16 - .../java/it/cavallium/warppi/util/Utils.java | 54 -- desktop/pom.xml | 6 +- ...PngReader.java => DesktopImageReader.java} | 22 +- .../warppi/desktop/DesktopImageUtils.java | 16 + .../warppi/desktop/DesktopPlatform.java | 6 +- .../warppi/desktop/DesktopPngUtils.java | 14 - engine-jogl/pom.xml | 4 +- hardware/pom.xml | 6 +- ...ngReader.java => HardwareImageReader.java} | 6 +- .../warppi/hardware/HardwareImageUtils.java | 14 + .../warppi/hardware/HardwarePlatform.java | 6 +- .../warppi/hardware/HardwarePngUtils.java | 14 - pom.xml | 2 +- rules/pom.xml | 4 +- teavm/pom.xml | 6 +- ...VMPngReader.java => TeaVMImageReader.java} | 6 +- .../warppi/teavm/TeaVMImageUtils.java | 14 + .../cavallium/warppi/teavm/TeaVMPlatform.java | 6 +- .../cavallium/warppi/teavm/TeaVMPngUtils.java | 14 - util/pom.xml | 2 +- 30 files changed, 871 insertions(+), 257 deletions(-) create mode 100644 core/src/main/java/it/cavallium/warppi/device/KeyboardAWTValues.java rename desktop/src/main/java/it/cavallium/warppi/desktop/{DesktopPngReader.java => DesktopImageReader.java} (68%) create mode 100644 desktop/src/main/java/it/cavallium/warppi/desktop/DesktopImageUtils.java delete mode 100644 desktop/src/main/java/it/cavallium/warppi/desktop/DesktopPngUtils.java rename hardware/src/main/java/it/cavallium/warppi/hardware/{HardwarePngReader.java => HardwareImageReader.java} (87%) create mode 100644 hardware/src/main/java/it/cavallium/warppi/hardware/HardwareImageUtils.java delete mode 100644 hardware/src/main/java/it/cavallium/warppi/hardware/HardwarePngUtils.java rename teavm/src/main/java/it/cavallium/warppi/teavm/{TeaVMPngReader.java => TeaVMImageReader.java} (87%) create mode 100644 teavm/src/main/java/it/cavallium/warppi/teavm/TeaVMImageUtils.java delete mode 100644 teavm/src/main/java/it/cavallium/warppi/teavm/TeaVMPngUtils.java diff --git a/Flow b/Flow index 9acbcc2e..fc24680f 160000 --- a/Flow +++ b/Flow @@ -1 +1 @@ -Subproject commit 9acbcc2e0af4961688490eb3447d990d157a46a8 +Subproject commit fc24680f8f2178fa550174c75d7dbacb6065358c diff --git a/bigdecimal-math b/bigdecimal-math index 92a67122..b08a4a6a 160000 --- a/bigdecimal-math +++ b/bigdecimal-math @@ -1 +1 @@ -Subproject commit 92a67122350a38af726beae58d2582fb867a4c61 +Subproject commit b08a4a6aca41fbc6bf0c4bd252f716d518d368a5 diff --git a/core/pom.xml b/core/pom.xml index b4e2c2ca..39c3ca38 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -6,7 +6,7 @@ it.cavallium warppi - 0.9.0a2 + 0.9.0a3 warppi-core @@ -17,17 +17,17 @@ it.cavallium warppi-flow - 0.9.0a2 + 0.9.0a3 it.cavallium warppi-util - 0.9.0a2 + 0.9.0a3 it.cavallium bigdecimal-math - 0.9.0a2 + 0.9.0a3 it.unimi.dsi diff --git a/core/src/main/java/it/cavallium/warppi/Platform.java b/core/src/main/java/it/cavallium/warppi/Platform.java index 5951cfaf..47a4c906 100644 --- a/core/src/main/java/it/cavallium/warppi/Platform.java +++ b/core/src/main/java/it/cavallium/warppi/Platform.java @@ -20,7 +20,7 @@ public interface Platform { StorageUtils getStorageUtils(); - PngUtils getPngUtils(); + ImageUtils getImageUtils(); Settings getSettings(); @@ -171,19 +171,17 @@ public interface Platform { void close() throws IOException; } - - public interface PngUtils { - - PngReader load(InputStream resourceStream); - - public interface PngReader { - + + public interface ImageUtils { + + ImageReader load(InputStream resourceStream) throws IOException; + + public interface ImageReader { + int[] getImageMatrix(); int[] getSize(); - } - } public interface Settings { diff --git a/core/src/main/java/it/cavallium/warppi/device/Keyboard.java b/core/src/main/java/it/cavallium/warppi/device/Keyboard.java index 8aa67e43..3e91d26c 100644 --- a/core/src/main/java/it/cavallium/warppi/device/Keyboard.java +++ b/core/src/main/java/it/cavallium/warppi/device/Keyboard.java @@ -1,7 +1,5 @@ package it.cavallium.warppi.device; -import java.awt.event.KeyEvent; - import it.cavallium.warppi.Engine; import it.cavallium.warppi.Platform.ConsoleUtils; import it.cavallium.warppi.StaticVars; @@ -126,10 +124,10 @@ public class Keyboard { */ public static void debugKey(final int keyCode, boolean released) { switch (keyCode) { - case KeyEvent.VK_ESCAPE: + case KeyboardAWTValues.VK_ESCAPE: debugKey(Key.BACK, released); break; - case KeyEvent.VK_S: + case KeyboardAWTValues.VK_S: if (Keyboard.shift) { debugKey(Key.ARCSINE, released); } else if (Keyboard.alpha) { @@ -138,7 +136,7 @@ public class Keyboard { debugKey(Key.SINE, released); } break; - case KeyEvent.VK_C: + case KeyboardAWTValues.VK_C: if (Keyboard.shift) { debugKey(Key.ARCCOSINE, released); } else if (Keyboard.alpha) { @@ -147,7 +145,7 @@ public class Keyboard { debugKey(Key.COSINE, released); } break; - case KeyEvent.VK_T: + case KeyboardAWTValues.VK_T: if (Keyboard.shift) { debugKey(Key.ARCTANGENT, released); } else if (Keyboard.alpha) { @@ -156,7 +154,7 @@ public class Keyboard { debugKey(Key.TANGENT, released); } break; - case KeyEvent.VK_D: + case KeyboardAWTValues.VK_D: if (!Keyboard.shift && !Keyboard.alpha) { debugKey(Key.debug_DEG, released); } else if (Keyboard.alpha) { @@ -165,7 +163,7 @@ public class Keyboard { debugKey(Key.NONE, released); } break; - case KeyEvent.VK_R: + case KeyboardAWTValues.VK_R: if (!Keyboard.shift && !Keyboard.alpha) { debugKey(Key.debug_RAD, released); } else if (Keyboard.alpha) { @@ -174,7 +172,7 @@ public class Keyboard { debugKey(Key.NONE, released); } break; - case KeyEvent.VK_G: + case KeyboardAWTValues.VK_G: if (!Keyboard.shift && !Keyboard.alpha) { debugKey(Key.debug_GRA, released); } else if (Keyboard.alpha) { @@ -183,14 +181,14 @@ public class Keyboard { debugKey(Key.NONE, released); } break; - case KeyEvent.VK_X: + case KeyboardAWTValues.VK_X: if (Keyboard.alpha) { debugKey(Key.LETTER_X, released); } else { debugKey(Key.NONE, released); } break; - case KeyEvent.VK_P: + case KeyboardAWTValues.VK_P: if (Keyboard.shift) { debugKey(Key.NONE, released); } else if (Keyboard.alpha) { @@ -199,7 +197,7 @@ public class Keyboard { debugKey(Key.PI, released); } break; - case KeyEvent.VK_E: + case KeyboardAWTValues.VK_E: if (Keyboard.shift) { debugKey(Key.NONE, released); } else if (Keyboard.alpha) { @@ -208,14 +206,14 @@ public class Keyboard { debugKey(Key.EULER_NUMBER, released); } break; - case KeyEvent.VK_Y: + case KeyboardAWTValues.VK_Y: if (Keyboard.alpha) { debugKey(Key.LETTER_Y, released); } else { debugKey(Key.NONE, released); } break; - case KeyEvent.VK_B: + case KeyboardAWTValues.VK_B: if (Keyboard.shift) { debugKey(Key.BRIGHTNESS_CYCLE_REVERSE, released); } else if (!Keyboard.shift && !Keyboard.alpha) { @@ -224,7 +222,7 @@ public class Keyboard { debugKey(Key.LETTER_B, released); } break; - case KeyEvent.VK_L: + case KeyboardAWTValues.VK_L: if (Keyboard.shift) { debugKey(Key.LOGARITHM, released); } else if (Keyboard.alpha) { @@ -234,7 +232,7 @@ public class Keyboard { } break; case KeyboardJogampValues.VK_ENTER: - case KeyEvent.VK_ENTER: + case KeyboardAWTValues.VK_ENTER: if (Keyboard.shift) { debugKey(Key.STEP, released); } else if (!Keyboard.shift && !Keyboard.alpha) { @@ -246,49 +244,49 @@ public class Keyboard { int col = 1; Keyboard.debugKeysDown[row - 1][col - 1] = true; break; - case KeyEvent.VK_1: + case KeyboardAWTValues.VK_1: if (!Keyboard.shift && !Keyboard.alpha) { debugKey(Key.NUM1, released); } else { debugKey(Key.NONE, released); } break; - case KeyEvent.VK_2: + case KeyboardAWTValues.VK_2: if (!Keyboard.shift && !Keyboard.alpha) { debugKey(Key.NUM2, released); } else { debugKey(Key.NONE, released); } break; - case KeyEvent.VK_3: + case KeyboardAWTValues.VK_3: if (!Keyboard.shift && !Keyboard.alpha) { debugKey(Key.NUM3, released); } else { debugKey(Key.NONE, released); } break; - case KeyEvent.VK_4: + case KeyboardAWTValues.VK_4: if (!Keyboard.shift && !Keyboard.alpha) { debugKey(Key.NUM4, released); } else { debugKey(Key.NONE, released); } break; - case KeyEvent.VK_5: + case KeyboardAWTValues.VK_5: if (!Keyboard.shift && !Keyboard.alpha) { debugKey(Key.NUM5, released); } else { debugKey(Key.NONE, released); } break; - case KeyEvent.VK_6: + case KeyboardAWTValues.VK_6: if (!Keyboard.shift && !Keyboard.alpha) { debugKey(Key.NUM6, released); } else { debugKey(Key.NONE, released); } break; - case KeyEvent.VK_7: + case KeyboardAWTValues.VK_7: if (!Keyboard.shift && !Keyboard.alpha) { debugKey(Key.NUM7, released); } else if (Keyboard.shift) { @@ -297,7 +295,7 @@ public class Keyboard { } } break; - case KeyEvent.VK_8: + case KeyboardAWTValues.VK_8: if (!Keyboard.shift && !Keyboard.alpha) { debugKey(Key.NUM8, released); } else if (Keyboard.shift) { @@ -306,7 +304,7 @@ public class Keyboard { debugKey(Key.NONE, released); } break; - case KeyEvent.VK_9: + case KeyboardAWTValues.VK_9: if (!Keyboard.shift && !Keyboard.alpha) { debugKey(Key.NUM9, released); } else if (Keyboard.shift) { @@ -315,7 +313,7 @@ public class Keyboard { debugKey(Key.NONE, released); } break; - case KeyEvent.VK_0: + case KeyboardAWTValues.VK_0: if (!Keyboard.shift && !Keyboard.alpha) { debugKey(Key.NUM0, released); } else if (Keyboard.shift) { @@ -324,7 +322,7 @@ public class Keyboard { debugKey(Key.NONE, released); } break; - case KeyEvent.VK_M: + case KeyboardAWTValues.VK_M: if (!Keyboard.shift && !Keyboard.alpha) { debugKey(Key.SURD_MODE, released); } else if (Keyboard.shift) { @@ -334,7 +332,7 @@ public class Keyboard { } break; case KeyboardJogampValues.VK_ADD: - case KeyEvent.VK_ADD: + case KeyboardAWTValues.VK_ADD: if (!Keyboard.shift && !Keyboard.alpha) { debugKey(Key.PLUS, released); } else if (Keyboard.shift) { @@ -344,7 +342,7 @@ public class Keyboard { } break; case KeyboardJogampValues.VK_SUBTRACT: - case KeyEvent.VK_SUBTRACT: + case KeyboardAWTValues.VK_SUBTRACT: if (!Keyboard.shift && !Keyboard.alpha) { debugKey(Key.MINUS, released); } else { @@ -352,7 +350,7 @@ public class Keyboard { } break; case KeyboardJogampValues.VK_MULTIPLY: - case KeyEvent.VK_MULTIPLY: + case KeyboardAWTValues.VK_MULTIPLY: if (!Keyboard.shift && !Keyboard.alpha) { debugKey(Key.MULTIPLY, released); } else { @@ -360,18 +358,18 @@ public class Keyboard { } break; case KeyboardJogampValues.VK_DIVIDE: - case KeyEvent.VK_DIVIDE: + case KeyboardAWTValues.VK_DIVIDE: if (!Keyboard.shift && !Keyboard.alpha) { debugKey(Key.DIVIDE, released); } else { debugKey(Key.NONE, released); } break; - case KeyEvent.VK_BACK_SPACE: + case KeyboardAWTValues.VK_BACK_SPACE: debugKey(Key.DELETE, released); break; case KeyboardJogampValues.VK_DELETE: - case KeyEvent.VK_DELETE: + case KeyboardAWTValues.VK_DELETE: if (!Keyboard.shift && !Keyboard.alpha) { debugKey(Key.RESET, released); } else { @@ -379,7 +377,7 @@ public class Keyboard { } break; case KeyboardJogampValues.VK_LEFT: - case KeyEvent.VK_LEFT: + case KeyboardAWTValues.VK_LEFT: //LEFT row = 2; col = 3; @@ -391,7 +389,7 @@ public class Keyboard { } break; case KeyboardJogampValues.VK_RIGHT: - case KeyEvent.VK_RIGHT: + case KeyboardAWTValues.VK_RIGHT: //RIGHT row = 2; col = 5; @@ -403,7 +401,7 @@ public class Keyboard { } break; case KeyboardJogampValues.VK_UP: - case KeyEvent.VK_UP: + case KeyboardAWTValues.VK_UP: //UP row = 1; col = 4; @@ -415,7 +413,7 @@ public class Keyboard { } break; case KeyboardJogampValues.VK_DOWN: - case KeyEvent.VK_DOWN: + case KeyboardAWTValues.VK_DOWN: //DOWN row = 3; col = 4; @@ -438,7 +436,7 @@ public class Keyboard { } break; case KeyboardJogampValues.VK_NUMPAD4: - case KeyEvent.VK_NUMPAD4: + case KeyboardAWTValues.VK_NUMPAD4: if (!Keyboard.shift && !Keyboard.alpha) { debugKey(Key.HISTORY_BACK, released); } else { @@ -446,21 +444,21 @@ public class Keyboard { } break; case KeyboardJogampValues.VK_NUMPAD6: - case KeyEvent.VK_NUMPAD6: + case KeyboardAWTValues.VK_NUMPAD6: if (!Keyboard.shift && !Keyboard.alpha) { debugKey(Key.HISTORY_FORWARD, released); } else { debugKey(Key.NONE, released); } break; - case KeyEvent.VK_PERIOD: + case KeyboardAWTValues.VK_PERIOD: if (!Keyboard.shift && !Keyboard.alpha) { debugKey(Key.DOT, released); } else { debugKey(Key.NONE, released); } break; - case KeyEvent.VK_A: + case KeyboardAWTValues.VK_A: if (!Keyboard.shift && !Keyboard.alpha) { debugKey(Key.NONE, released); } else if (Keyboard.alpha && !Keyboard.shift) { @@ -471,7 +469,7 @@ public class Keyboard { debugKey(Key.NONE, released); } break; - case KeyEvent.VK_F: + case KeyboardAWTValues.VK_F: if (!Keyboard.shift && !Keyboard.alpha) { debugKey(Key.NONE, released); } else if (Keyboard.alpha && !Keyboard.shift) { @@ -482,7 +480,7 @@ public class Keyboard { debugKey(Key.NONE, released); } break; - case KeyEvent.VK_H: + case KeyboardAWTValues.VK_H: if (!Keyboard.shift && !Keyboard.alpha) { debugKey(Key.NONE, released); } else if (Keyboard.alpha && !Keyboard.shift) { @@ -493,7 +491,7 @@ public class Keyboard { debugKey(Key.NONE, released); } break; - case KeyEvent.VK_I: + case KeyboardAWTValues.VK_I: if (!Keyboard.shift && !Keyboard.alpha) { debugKey(Key.NONE, released); } else if (Keyboard.alpha && !Keyboard.shift) { @@ -504,7 +502,7 @@ public class Keyboard { debugKey(Key.NONE, released); } break; - case KeyEvent.VK_J: + case KeyboardAWTValues.VK_J: if (!Keyboard.shift && !Keyboard.alpha) { debugKey(Key.NONE, released); } else if (Keyboard.alpha && !Keyboard.shift) { @@ -515,7 +513,7 @@ public class Keyboard { debugKey(Key.NONE, released); } break; - case KeyEvent.VK_K: + case KeyboardAWTValues.VK_K: if (!Keyboard.shift && !Keyboard.alpha) { debugKey(Key.NONE, released); } else if (Keyboard.alpha && !Keyboard.shift) { @@ -526,7 +524,7 @@ public class Keyboard { debugKey(Key.NONE, released); } break; - case KeyEvent.VK_N: + case KeyboardAWTValues.VK_N: if (!Keyboard.shift && !Keyboard.alpha) { debugKey(Key.NONE, released); } else if (Keyboard.alpha && !Keyboard.shift) { @@ -537,7 +535,7 @@ public class Keyboard { debugKey(Key.NONE, released); } break; - case KeyEvent.VK_O: + case KeyboardAWTValues.VK_O: if (!Keyboard.shift && !Keyboard.alpha) { debugKey(Key.NONE, released); } else if (Keyboard.alpha && !Keyboard.shift) { @@ -548,7 +546,7 @@ public class Keyboard { debugKey(Key.NONE, released); } break; - case KeyEvent.VK_Q: + case KeyboardAWTValues.VK_Q: if (!Keyboard.shift && !Keyboard.alpha) { debugKey(Key.NONE, released); } else if (Keyboard.alpha && !Keyboard.shift) { @@ -559,7 +557,7 @@ public class Keyboard { debugKey(Key.NONE, released); } break; - case KeyEvent.VK_U: + case KeyboardAWTValues.VK_U: if (!Keyboard.shift && !Keyboard.alpha) { debugKey(Key.NONE, released); } else if (Keyboard.alpha && !Keyboard.shift) { @@ -570,7 +568,7 @@ public class Keyboard { debugKey(Key.NONE, released); } break; - case KeyEvent.VK_V: + case KeyboardAWTValues.VK_V: if (!Keyboard.shift && !Keyboard.alpha) { debugKey(Key.NONE, released); } else if (Keyboard.alpha && !Keyboard.shift) { @@ -581,7 +579,7 @@ public class Keyboard { debugKey(Key.NONE, released); } break; - case KeyEvent.VK_W: + case KeyboardAWTValues.VK_W: if (!Keyboard.shift && !Keyboard.alpha) { debugKey(Key.NONE, released); } else if (Keyboard.alpha && !Keyboard.shift) { @@ -592,7 +590,7 @@ public class Keyboard { debugKey(Key.NONE, released); } break; - case KeyEvent.VK_Z: + case KeyboardAWTValues.VK_Z: if (!Keyboard.shift && !Keyboard.alpha) { debugKey(Key.NONE, released); } else if (Keyboard.alpha && !Keyboard.shift) { @@ -604,26 +602,26 @@ public class Keyboard { } break; case KeyboardJogampValues.VK_SHIFT: - case KeyEvent.VK_SHIFT: + case KeyboardAWTValues.VK_SHIFT: debugKey(Key.SHIFT, released); break; - case KeyEvent.VK_CONTROL: + case KeyboardAWTValues.VK_CONTROL: debugKey(Key.ALPHA, released); break; case KeyboardJogampValues.VK_NUMPAD1: - case KeyEvent.VK_NUMPAD1: + case KeyboardAWTValues.VK_NUMPAD1: debugKey(Key.SQRT, released); break; case KeyboardJogampValues.VK_NUMPAD2: - case KeyEvent.VK_NUMPAD2: + case KeyboardAWTValues.VK_NUMPAD2: debugKey(Key.ROOT, released); break; case KeyboardJogampValues.VK_NUMPAD3: - case KeyEvent.VK_NUMPAD3: + case KeyboardAWTValues.VK_NUMPAD3: debugKey(Key.POWER_OF_2, released); break; case KeyboardJogampValues.VK_NUMPAD5: - case KeyEvent.VK_NUMPAD5: + case KeyboardAWTValues.VK_NUMPAD5: debugKey(Key.POWER_OF_x, released); break; } @@ -631,20 +629,20 @@ public class Keyboard { private synchronized static void debugKeyReleased(final int keyCode) { switch (keyCode) { - case KeyEvent.VK_ENTER: + case KeyboardAWTValues.VK_ENTER: int row = 2; int col = 1; Keyboard.debugKeysDown[row - 1][col - 1] = false; break; case KeyboardJogampValues.VK_LEFT: - case KeyEvent.VK_LEFT: + case KeyboardAWTValues.VK_LEFT: //LEFT row = 2; col = 3; Keyboard.debugKeysDown[row - 1][col - 1] = false; break; case KeyboardJogampValues.VK_RIGHT: - case KeyEvent.VK_RIGHT: + case KeyboardAWTValues.VK_RIGHT: //RIGHT row = 2; col = 5; @@ -652,14 +650,14 @@ public class Keyboard { System.out.println("RELEASE"); break; case KeyboardJogampValues.VK_UP: - case KeyEvent.VK_UP: + case KeyboardAWTValues.VK_UP: //UP row = 1; col = 4; Keyboard.debugKeysDown[row - 1][col - 1] = false; break; case KeyboardJogampValues.VK_DOWN: - case KeyEvent.VK_DOWN: + case KeyboardAWTValues.VK_DOWN: //DOWN row = 3; col = 4; diff --git a/core/src/main/java/it/cavallium/warppi/device/KeyboardAWTValues.java b/core/src/main/java/it/cavallium/warppi/device/KeyboardAWTValues.java new file mode 100644 index 00000000..083d0531 --- /dev/null +++ b/core/src/main/java/it/cavallium/warppi/device/KeyboardAWTValues.java @@ -0,0 +1,700 @@ +package it.cavallium.warppi.device; + +public interface KeyboardAWTValues { + + /** + * The first number in the range of ids used for key events. + */ + int KEY_FIRST = 400; + + /** + * The last number in the range of ids used for key events. + */ + int KEY_LAST = 402; + + /** + * The "key typed" event. This event is generated when a character is + * entered. In the simplest case, it is produced by a single key press. + * Often, however, characters are produced by series of key presses, and + * the mapping from key pressed events to key typed events may be + * many-to-one or many-to-many. + */ + int KEY_TYPED = KEY_FIRST; + + /** + * The "key pressed" event. This event is generated when a key + * is pushed down. + */ + int KEY_PRESSED = 1 + KEY_FIRST; //Event.KEY_PRESS + + /** + * The "key released" event. This event is generated when a key + * is let up. + */ + int KEY_RELEASED = 2 + KEY_FIRST; //Event.KEY_RELEASE + + /* Virtual key codes. */ + + int VK_ENTER = '\n'; + int VK_BACK_SPACE = '\b'; + int VK_TAB = '\t'; + int VK_CANCEL = 0x03; + int VK_CLEAR = 0x0C; + int VK_SHIFT = 0x10; + int VK_CONTROL = 0x11; + int VK_ALT = 0x12; + int VK_PAUSE = 0x13; + int VK_CAPS_LOCK = 0x14; + int VK_ESCAPE = 0x1B; + int VK_SPACE = 0x20; + int VK_PAGE_UP = 0x21; + int VK_PAGE_DOWN = 0x22; + int VK_END = 0x23; + int VK_HOME = 0x24; + + /** + * Constant for the non-numpad left arrow key. + * @see #VK_KP_LEFT + */ + int VK_LEFT = 0x25; + + /** + * Constant for the non-numpad up arrow key. + * @see #VK_KP_UP + */ + int VK_UP = 0x26; + + /** + * Constant for the non-numpad right arrow key. + * @see #VK_KP_RIGHT + */ + int VK_RIGHT = 0x27; + + /** + * Constant for the non-numpad down arrow key. + * @see #VK_KP_DOWN + */ + int VK_DOWN = 0x28; + + /** + * Constant for the comma key, "," + */ + int VK_COMMA = 0x2C; + + /** + * Constant for the minus key, "-" + * @since 1.2 + */ + int VK_MINUS = 0x2D; + + /** + * Constant for the period key, "." + */ + int VK_PERIOD = 0x2E; + + /** + * Constant for the forward slash key, "/" + */ + int VK_SLASH = 0x2F; + + /** VK_0 thru VK_9 are the same as ASCII '0' thru '9' (0x30 - 0x39) */ + int VK_0 = 0x30; + int VK_1 = 0x31; + int VK_2 = 0x32; + int VK_3 = 0x33; + int VK_4 = 0x34; + int VK_5 = 0x35; + int VK_6 = 0x36; + int VK_7 = 0x37; + int VK_8 = 0x38; + int VK_9 = 0x39; + + /** + * Constant for the semicolon key, ";" + */ + int VK_SEMICOLON = 0x3B; + + /** + * Constant for the equals key, "=" + */ + int VK_EQUALS = 0x3D; + + /** VK_A thru VK_Z are the same as ASCII 'A' thru 'Z' (0x41 - 0x5A) */ + int VK_A = 0x41; + int VK_B = 0x42; + int VK_C = 0x43; + int VK_D = 0x44; + int VK_E = 0x45; + int VK_F = 0x46; + int VK_G = 0x47; + int VK_H = 0x48; + int VK_I = 0x49; + int VK_J = 0x4A; + int VK_K = 0x4B; + int VK_L = 0x4C; + int VK_M = 0x4D; + int VK_N = 0x4E; + int VK_O = 0x4F; + int VK_P = 0x50; + int VK_Q = 0x51; + int VK_R = 0x52; + int VK_S = 0x53; + int VK_T = 0x54; + int VK_U = 0x55; + int VK_V = 0x56; + int VK_W = 0x57; + int VK_X = 0x58; + int VK_Y = 0x59; + int VK_Z = 0x5A; + + /** + * Constant for the open bracket key, "[" + */ + int VK_OPEN_BRACKET = 0x5B; + + /** + * Constant for the back slash key, "\" + */ + int VK_BACK_SLASH = 0x5C; + + /** + * Constant for the close bracket key, "]" + */ + int VK_CLOSE_BRACKET = 0x5D; + + int VK_NUMPAD0 = 0x60; + int VK_NUMPAD1 = 0x61; + int VK_NUMPAD2 = 0x62; + int VK_NUMPAD3 = 0x63; + int VK_NUMPAD4 = 0x64; + int VK_NUMPAD5 = 0x65; + int VK_NUMPAD6 = 0x66; + int VK_NUMPAD7 = 0x67; + int VK_NUMPAD8 = 0x68; + int VK_NUMPAD9 = 0x69; + int VK_MULTIPLY = 0x6A; + int VK_ADD = 0x6B; + + /** + * This constant is obsolete, and is included only for backwards + * compatibility. + * @see #VK_SEPARATOR + */ + int VK_SEPARATER = 0x6C; + + /** + * Constant for the Numpad Separator key. + * @since 1.4 + */ + int VK_SEPARATOR = VK_SEPARATER; + + int VK_SUBTRACT = 0x6D; + int VK_DECIMAL = 0x6E; + int VK_DIVIDE = 0x6F; + int VK_DELETE = 0x7F; /* ASCII DEL */ + int VK_NUM_LOCK = 0x90; + int VK_SCROLL_LOCK = 0x91; + + /** Constant for the F1 function key. */ + int VK_F1 = 0x70; + + /** Constant for the F2 function key. */ + int VK_F2 = 0x71; + + /** Constant for the F3 function key. */ + int VK_F3 = 0x72; + + /** Constant for the F4 function key. */ + int VK_F4 = 0x73; + + /** Constant for the F5 function key. */ + int VK_F5 = 0x74; + + /** Constant for the F6 function key. */ + int VK_F6 = 0x75; + + /** Constant for the F7 function key. */ + int VK_F7 = 0x76; + + /** Constant for the F8 function key. */ + int VK_F8 = 0x77; + + /** Constant for the F9 function key. */ + int VK_F9 = 0x78; + + /** Constant for the F10 function key. */ + int VK_F10 = 0x79; + + /** Constant for the F11 function key. */ + int VK_F11 = 0x7A; + + /** Constant for the F12 function key. */ + int VK_F12 = 0x7B; + + /** + * Constant for the F13 function key. + * @since 1.2 + */ + /* F13 - F24 are used on IBM 3270 keyboard; use random range for constants. */ + int VK_F13 = 0xF000; + + /** + * Constant for the F14 function key. + * @since 1.2 + */ + int VK_F14 = 0xF001; + + /** + * Constant for the F15 function key. + * @since 1.2 + */ + int VK_F15 = 0xF002; + + /** + * Constant for the F16 function key. + * @since 1.2 + */ + int VK_F16 = 0xF003; + + /** + * Constant for the F17 function key. + * @since 1.2 + */ + int VK_F17 = 0xF004; + + /** + * Constant for the F18 function key. + * @since 1.2 + */ + int VK_F18 = 0xF005; + + /** + * Constant for the F19 function key. + * @since 1.2 + */ + int VK_F19 = 0xF006; + + /** + * Constant for the F20 function key. + * @since 1.2 + */ + int VK_F20 = 0xF007; + + /** + * Constant for the F21 function key. + * @since 1.2 + */ + int VK_F21 = 0xF008; + + /** + * Constant for the F22 function key. + * @since 1.2 + */ + int VK_F22 = 0xF009; + + /** + * Constant for the F23 function key. + * @since 1.2 + */ + int VK_F23 = 0xF00A; + + /** + * Constant for the F24 function key. + * @since 1.2 + */ + int VK_F24 = 0xF00B; + + int VK_PRINTSCREEN = 0x9A; + int VK_INSERT = 0x9B; + int VK_HELP = 0x9C; + int VK_META = 0x9D; + + int VK_BACK_QUOTE = 0xC0; + int VK_QUOTE = 0xDE; + + /** + * Constant for the numeric keypad up arrow key. + * @see #VK_UP + * @since 1.2 + */ + int VK_KP_UP = 0xE0; + + /** + * Constant for the numeric keypad down arrow key. + * @see #VK_DOWN + * @since 1.2 + */ + int VK_KP_DOWN = 0xE1; + + /** + * Constant for the numeric keypad left arrow key. + * @see #VK_LEFT + * @since 1.2 + */ + int VK_KP_LEFT = 0xE2; + + /** + * Constant for the numeric keypad right arrow key. + * @see #VK_RIGHT + * @since 1.2 + */ + int VK_KP_RIGHT = 0xE3; + + /* For European keyboards */ + /** @since 1.2 */ + int VK_DEAD_GRAVE = 0x80; + /** @since 1.2 */ + int VK_DEAD_ACUTE = 0x81; + /** @since 1.2 */ + int VK_DEAD_CIRCUMFLEX = 0x82; + /** @since 1.2 */ + int VK_DEAD_TILDE = 0x83; + /** @since 1.2 */ + int VK_DEAD_MACRON = 0x84; + /** @since 1.2 */ + int VK_DEAD_BREVE = 0x85; + /** @since 1.2 */ + int VK_DEAD_ABOVEDOT = 0x86; + /** @since 1.2 */ + int VK_DEAD_DIAERESIS = 0x87; + /** @since 1.2 */ + int VK_DEAD_ABOVERING = 0x88; + /** @since 1.2 */ + int VK_DEAD_DOUBLEACUTE = 0x89; + /** @since 1.2 */ + int VK_DEAD_CARON = 0x8a; + /** @since 1.2 */ + int VK_DEAD_CEDILLA = 0x8b; + /** @since 1.2 */ + int VK_DEAD_OGONEK = 0x8c; + /** @since 1.2 */ + int VK_DEAD_IOTA = 0x8d; + /** @since 1.2 */ + int VK_DEAD_VOICED_SOUND = 0x8e; + /** @since 1.2 */ + int VK_DEAD_SEMIVOICED_SOUND = 0x8f; + + /** @since 1.2 */ + int VK_AMPERSAND = 0x96; + /** @since 1.2 */ + int VK_ASTERISK = 0x97; + /** @since 1.2 */ + int VK_QUOTEDBL = 0x98; + /** @since 1.2 */ + int VK_LESS = 0x99; + + /** @since 1.2 */ + int VK_GREATER = 0xa0; + /** @since 1.2 */ + int VK_BRACELEFT = 0xa1; + /** @since 1.2 */ + int VK_BRACERIGHT = 0xa2; + + /** + * Constant for the "@" key. + * @since 1.2 + */ + int VK_AT = 0x0200; + + /** + * Constant for the ":" key. + * @since 1.2 + */ + int VK_COLON = 0x0201; + + /** + * Constant for the "^" key. + * @since 1.2 + */ + int VK_CIRCUMFLEX = 0x0202; + + /** + * Constant for the "$" key. + * @since 1.2 + */ + int VK_DOLLAR = 0x0203; + + /** + * Constant for the Euro currency sign key. + * @since 1.2 + */ + int VK_EURO_SIGN = 0x0204; + + /** + * Constant for the "!" key. + * @since 1.2 + */ + int VK_EXCLAMATION_MARK = 0x0205; + + /** + * Constant for the inverted exclamation mark key. + * @since 1.2 + */ + int VK_INVERTED_EXCLAMATION_MARK = 0x0206; + + /** + * Constant for the "(" key. + * @since 1.2 + */ + int VK_LEFT_PARENTHESIS = 0x0207; + + /** + * Constant for the "#" key. + * @since 1.2 + */ + int VK_NUMBER_SIGN = 0x0208; + + /** + * Constant for the "+" key. + * @since 1.2 + */ + int VK_PLUS = 0x0209; + + /** + * Constant for the ")" key. + * @since 1.2 + */ + int VK_RIGHT_PARENTHESIS = 0x020A; + + /** + * Constant for the "_" key. + * @since 1.2 + */ + int VK_UNDERSCORE = 0x020B; + + /** + * Constant for the Microsoft Windows "Windows" key. + * It is used for both the left and right version of the key. + * @see #getKeyLocation() + * @since 1.5 + */ + int VK_WINDOWS = 0x020C; + + /** + * Constant for the Microsoft Windows Context Menu key. + * @since 1.5 + */ + int VK_CONTEXT_MENU = 0x020D; + + /* for input method support on Asian Keyboards */ + + /* not clear what this means - listed in Microsoft Windows API */ + int VK_FINAL = 0x0018; + + /** Constant for the Convert function key. */ + /* Japanese PC 106 keyboard, Japanese Solaris keyboard: henkan */ + int VK_CONVERT = 0x001C; + + /** Constant for the Don't Convert function key. */ + /* Japanese PC 106 keyboard: muhenkan */ + int VK_NONCONVERT = 0x001D; + + /** Constant for the Accept or Commit function key. */ + /* Japanese Solaris keyboard: kakutei */ + int VK_ACCEPT = 0x001E; + + /* not clear what this means - listed in Microsoft Windows API */ + int VK_MODECHANGE = 0x001F; + + /* replaced by VK_KANA_LOCK for Microsoft Windows and Solaris; + might still be used on other platforms */ + int VK_KANA = 0x0015; + + /* replaced by VK_INPUT_METHOD_ON_OFF for Microsoft Windows and Solaris; + might still be used for other platforms */ + int VK_KANJI = 0x0019; + + /** + * Constant for the Alphanumeric function key. + * @since 1.2 + */ + /* Japanese PC 106 keyboard: eisuu */ + int VK_ALPHANUMERIC = 0x00F0; + + /** + * Constant for the Katakana function key. + * @since 1.2 + */ + /* Japanese PC 106 keyboard: katakana */ + int VK_KATAKANA = 0x00F1; + + /** + * Constant for the Hiragana function key. + * @since 1.2 + */ + /* Japanese PC 106 keyboard: hiragana */ + int VK_HIRAGANA = 0x00F2; + + /** + * Constant for the Full-Width Characters function key. + * @since 1.2 + */ + /* Japanese PC 106 keyboard: zenkaku */ + int VK_FULL_WIDTH = 0x00F3; + + /** + * Constant for the Half-Width Characters function key. + * @since 1.2 + */ + /* Japanese PC 106 keyboard: hankaku */ + int VK_HALF_WIDTH = 0x00F4; + + /** + * Constant for the Roman Characters function key. + * @since 1.2 + */ + /* Japanese PC 106 keyboard: roumaji */ + int VK_ROMAN_CHARACTERS = 0x00F5; + + /** + * Constant for the All Candidates function key. + * @since 1.2 + */ + /* Japanese PC 106 keyboard - VK_CONVERT + ALT: zenkouho */ + int VK_ALL_CANDIDATES = 0x0100; + + /** + * Constant for the Previous Candidate function key. + * @since 1.2 + */ + /* Japanese PC 106 keyboard - VK_CONVERT + SHIFT: maekouho */ + int VK_PREVIOUS_CANDIDATE = 0x0101; + + /** + * Constant for the Code Input function key. + * @since 1.2 + */ + /* Japanese PC 106 keyboard - VK_ALPHANUMERIC + ALT: kanji bangou */ + int VK_CODE_INPUT = 0x0102; + + /** + * Constant for the Japanese-Katakana function key. + * This key switches to a Japanese input method and selects its Katakana input mode. + * @since 1.2 + */ + /* Japanese Macintosh keyboard - VK_JAPANESE_HIRAGANA + SHIFT */ + int VK_JAPANESE_KATAKANA = 0x0103; + + /** + * Constant for the Japanese-Hiragana function key. + * This key switches to a Japanese input method and selects its Hiragana input mode. + * @since 1.2 + */ + /* Japanese Macintosh keyboard */ + int VK_JAPANESE_HIRAGANA = 0x0104; + + /** + * Constant for the Japanese-Roman function key. + * This key switches to a Japanese input method and selects its Roman-Direct input mode. + * @since 1.2 + */ + /* Japanese Macintosh keyboard */ + int VK_JAPANESE_ROMAN = 0x0105; + + /** + * Constant for the locking Kana function key. + * This key locks the keyboard into a Kana layout. + * @since 1.3 + */ + /* Japanese PC 106 keyboard with special Windows driver - eisuu + Control; Japanese Solaris keyboard: kana */ + int VK_KANA_LOCK = 0x0106; + + /** + * Constant for the input method on/off key. + * @since 1.3 + */ + /* Japanese PC 106 keyboard: kanji. Japanese Solaris keyboard: nihongo */ + int VK_INPUT_METHOD_ON_OFF = 0x0107; + + /* for Sun keyboards */ + /** @since 1.2 */ + int VK_CUT = 0xFFD1; + /** @since 1.2 */ + int VK_COPY = 0xFFCD; + /** @since 1.2 */ + int VK_PASTE = 0xFFCF; + /** @since 1.2 */ + int VK_UNDO = 0xFFCB; + /** @since 1.2 */ + int VK_AGAIN = 0xFFC9; + /** @since 1.2 */ + int VK_FIND = 0xFFD0; + /** @since 1.2 */ + int VK_PROPS = 0xFFCA; + /** @since 1.2 */ + int VK_STOP = 0xFFC8; + + /** + * Constant for the Compose function key. + * @since 1.2 + */ + int VK_COMPOSE = 0xFF20; + + /** + * Constant for the AltGraph function key. + * @since 1.2 + */ + int VK_ALT_GRAPH = 0xFF7E; + + /** + * Constant for the Begin key. + * @since 1.5 + */ + int VK_BEGIN = 0xFF58; + + /** + * This value is used to indicate that the keyCode is unknown. + * KEY_TYPED events do not have a keyCode value; this value + * is used instead. + */ + int VK_UNDEFINED = 0x0; + + /** + * KEY_PRESSED and KEY_RELEASED events which do not map to a + * valid Unicode character use this for the keyChar value. + */ + char CHAR_UNDEFINED = 0xFFFF; + + /** + * A constant indicating that the keyLocation is indeterminate + * or not relevant. + * KEY_TYPED events do not have a keyLocation; this value + * is used instead. + * @since 1.4 + */ + int KEY_LOCATION_UNKNOWN = 0; + + /** + * A constant indicating that the key pressed or released + * is not distinguished as the left or right version of a key, + * and did not originate on the numeric keypad (or did not + * originate with a virtual key corresponding to the numeric + * keypad). + * @since 1.4 + */ + int KEY_LOCATION_STANDARD = 1; + + /** + * A constant indicating that the key pressed or released is in + * the left key location (there is more than one possible location + * for this key). Example: the left shift key. + * @since 1.4 + */ + int KEY_LOCATION_LEFT = 2; + + /** + * A constant indicating that the key pressed or released is in + * the right key location (there is more than one possible location + * for this key). Example: the right shift key. + * @since 1.4 + */ + int KEY_LOCATION_RIGHT = 3; + + /** + * A constant indicating that the key event originated on the + * numeric keypad or with a virtual key corresponding to the + * numeric keypad. + * @since 1.4 + */ + int KEY_LOCATION_NUMPAD = 4; +} diff --git a/core/src/main/java/it/cavallium/warppi/device/graphicengine/RAWFont.java b/core/src/main/java/it/cavallium/warppi/device/graphicengine/RAWFont.java index 1be75b02..d344d79a 100644 --- a/core/src/main/java/it/cavallium/warppi/device/graphicengine/RAWFont.java +++ b/core/src/main/java/it/cavallium/warppi/device/graphicengine/RAWFont.java @@ -1,7 +1,5 @@ package it.cavallium.warppi.device.graphicengine; -import java.awt.image.BufferedImage; -import java.awt.image.DataBufferInt; import java.io.File; import java.io.IOException; import java.lang.ref.WeakReference; @@ -9,8 +7,6 @@ import java.net.URL; import java.util.logging.Level; import java.util.logging.Logger; -import javax.imageio.ImageIO; - import it.cavallium.warppi.Engine; import it.cavallium.warppi.util.ClassUtils; import it.cavallium.warppi.util.Utils; @@ -135,18 +131,6 @@ public class RAWFont { return indexes; } - @SuppressWarnings("unused") - private void saveArray(final int[] screen, final String coutputpng) { - final BufferedImage bi = new BufferedImage(300, 200, BufferedImage.TYPE_INT_RGB); - final int[] a = ((DataBufferInt) bi.getRaster().getDataBuffer()).getData(); - System.arraycopy(screen, 0, a, 0, screen.length); - try { - ImageIO.write(bi, "PNG", new File(coutputpng)); - } catch (final IOException ex) { - Logger.getLogger(RAWFont.class.getName()).log(Level.SEVERE, null, ex); - } - } - public void drawText(final int[] screen, final int[] screenSize, final int x, final int y, final int[] text, final int color) { final int screenLength = screen.length; diff --git a/core/src/main/java/it/cavallium/warppi/gui/DisplayManager.java b/core/src/main/java/it/cavallium/warppi/gui/DisplayManager.java index faa26d33..55647aa6 100644 --- a/core/src/main/java/it/cavallium/warppi/gui/DisplayManager.java +++ b/core/src/main/java/it/cavallium/warppi/gui/DisplayManager.java @@ -5,14 +5,13 @@ import java.util.Arrays; import java.util.LinkedList; import java.util.List; -import org.apache.commons.lang3.tuple.Pair; - import it.cavallium.warppi.Engine; import it.cavallium.warppi.Platform.ConsoleUtils; import it.cavallium.warppi.Platform.Semaphore; import it.cavallium.warppi.StaticVars; import it.cavallium.warppi.device.Keyboard; import it.cavallium.warppi.flow.Observable; +import it.cavallium.warppi.flow.Pair; import it.cavallium.warppi.gui.graphicengine.BinaryFont; import it.cavallium.warppi.gui.graphicengine.GraphicEngine; import it.cavallium.warppi.gui.graphicengine.Renderer; diff --git a/core/src/main/java/it/cavallium/warppi/gui/graphicengine/impl/common/PngSkin.java b/core/src/main/java/it/cavallium/warppi/gui/graphicengine/impl/common/PngSkin.java index c90c8504..78a4fba0 100644 --- a/core/src/main/java/it/cavallium/warppi/gui/graphicengine/impl/common/PngSkin.java +++ b/core/src/main/java/it/cavallium/warppi/gui/graphicengine/impl/common/PngSkin.java @@ -1,14 +1,10 @@ package it.cavallium.warppi.gui.graphicengine.impl.common; -import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; -import java.net.URISyntaxException; - -import javax.imageio.ImageIO; import it.cavallium.warppi.Engine; -import it.cavallium.warppi.Platform.PngUtils.PngReader; +import it.cavallium.warppi.Platform.ImageUtils.ImageReader; import it.cavallium.warppi.gui.graphicengine.GraphicEngine; import it.cavallium.warppi.gui.graphicengine.Skin; @@ -29,14 +25,7 @@ public abstract class PngSkin implements Skin { if (!file.startsWith("/")) { file = "/" + file; } - if (!file.endsWith(".png")) { - final File f = File.createTempFile("picalculator-png", ".png"); - f.deleteOnExit(); - final BufferedImage img = ImageIO.read(Engine.getPlatform().getStorageUtils().getResourceStream(file)); - ImageIO.write(img, "PNG", f); - file = f.toString(); - } - final PngReader r = Engine.getPlatform().getPngUtils().load(Engine.getPlatform().getStorageUtils().getResourceStream(file)); + final ImageReader r = Engine.getPlatform().getImageUtils().load(Engine.getPlatform().getStorageUtils().getResourceStream(file)); if (r == null) { skinData = new int[0]; skinSize = new int[] { 0, 0 }; diff --git a/core/src/main/java/it/cavallium/warppi/gui/graphicengine/impl/common/RFTFont.java b/core/src/main/java/it/cavallium/warppi/gui/graphicengine/impl/common/RFTFont.java index af68f9cd..a2baae3a 100644 --- a/core/src/main/java/it/cavallium/warppi/gui/graphicengine/impl/common/RFTFont.java +++ b/core/src/main/java/it/cavallium/warppi/gui/graphicengine/impl/common/RFTFont.java @@ -1,7 +1,5 @@ package it.cavallium.warppi.gui.graphicengine.impl.common; -import java.awt.image.BufferedImage; -import java.awt.image.DataBufferInt; import java.io.File; import java.io.IOException; import java.io.InputStream; @@ -10,8 +8,6 @@ import java.util.LinkedList; import java.util.logging.Level; import java.util.logging.Logger; -import javax.imageio.ImageIO; - import it.cavallium.warppi.Engine; import it.cavallium.warppi.Platform.ConsoleUtils; import it.cavallium.warppi.gui.graphicengine.BinaryFont; @@ -223,18 +219,6 @@ public abstract class RFTFont implements BinaryFont { } } - @SuppressWarnings("unused") - private void saveArray(final int[] screen, final int w, final int h, final String coutputpng) { - final BufferedImage bi = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB); - final int[] a = ((DataBufferInt) bi.getRaster().getDataBuffer()).getData(); - System.arraycopy(screen, 0, a, 0, screen.length); - try { - ImageIO.write(bi, "PNG", new File(coutputpng)); - } catch (final IOException ex) { - Logger.getLogger(BinaryFont.class.getName()).log(Level.SEVERE, null, ex); - } - } - public int[] getCharIndexes(final String txt) { final int l = txt.length(); final int[] indexes = new int[l]; diff --git a/core/src/main/java/it/cavallium/warppi/util/Utils.java b/core/src/main/java/it/cavallium/warppi/util/Utils.java index c1d195a6..0336f252 100644 --- a/core/src/main/java/it/cavallium/warppi/util/Utils.java +++ b/core/src/main/java/it/cavallium/warppi/util/Utils.java @@ -4,15 +4,10 @@ import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; -import java.lang.management.ManagementFactory; -import java.lang.management.OperatingSystemMXBean; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; import java.math.BigDecimal; import java.math.BigInteger; import java.math.RoundingMode; import java.net.URISyntaxException; -import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; @@ -571,55 +566,6 @@ public class Utils { return sdata; } - public static void printSystemResourcesUsage() { - System.out.println("============"); - final OperatingSystemMXBean operatingSystemMXBean = ManagementFactory.getOperatingSystemMXBean(); - for (final Method method : operatingSystemMXBean.getClass().getDeclaredMethods()) { - method.setAccessible(true); - if (method.getName().startsWith("get") && Modifier.isPublic(method.getModifiers())) { - Object value; - try { - value = method.invoke(operatingSystemMXBean); - } catch (final Exception e) { - value = e; - } // try - boolean percent = false; - boolean mb = false; - final String displayName = method.getName(); - final String displayValue = value.toString(); - if (displayName.endsWith("CpuLoad")) { - percent = true; - } - if (displayName.endsWith("MemorySize")) { - mb = true; - } - final List arr = new ArrayList<>(); - arr.add("getFreePhysicalMemorySize"); - arr.add("getProcessCpuLoad"); - arr.add("getSystemCpuLoad"); - arr.add("getTotalPhysicalMemorySize"); - if (arr.contains(displayName)) { - if (percent) { - try { - System.out.println(displayName + " = " + (int) (Float.parseFloat(displayValue) * 10000f) / 100f + "%"); - } catch (final Exception ex) { - System.out.println(displayName + " = " + displayValue); - } - } else if (mb) { - try { - System.out.println(displayName + " = " + Long.parseLong(displayValue) / 1024L / 1024L + " MB"); - } catch (final Exception ex) { - System.out.println(displayName + " = " + displayValue); - } - } else { - System.out.println(displayName + " = " + displayValue); - } - } - } // if - } // for - System.out.println("============"); - } - public static boolean isWindows() { return Engine.getPlatform().getOsName().indexOf("win") >= 0; } diff --git a/desktop/pom.xml b/desktop/pom.xml index c16aa18e..c54f0525 100644 --- a/desktop/pom.xml +++ b/desktop/pom.xml @@ -6,7 +6,7 @@ it.cavallium warppi - 0.9.0a2 + 0.9.0a3 warppi-desktop @@ -16,12 +16,12 @@ it.cavallium warppi-core - 0.9.0a2 + 0.9.0a3 it.cavallium warppi-engine-jogl - 0.9.0a2 + 0.9.0a3 org.fusesource.jansi diff --git a/desktop/src/main/java/it/cavallium/warppi/desktop/DesktopPngReader.java b/desktop/src/main/java/it/cavallium/warppi/desktop/DesktopImageReader.java similarity index 68% rename from desktop/src/main/java/it/cavallium/warppi/desktop/DesktopPngReader.java rename to desktop/src/main/java/it/cavallium/warppi/desktop/DesktopImageReader.java index 26596ca5..feb4e75a 100644 --- a/desktop/src/main/java/it/cavallium/warppi/desktop/DesktopPngReader.java +++ b/desktop/src/main/java/it/cavallium/warppi/desktop/DesktopImageReader.java @@ -1,16 +1,30 @@ package it.cavallium.warppi.desktop; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; import java.io.InputStream; +import javax.imageio.ImageIO; + import ar.com.hjg.pngj.ImageLineInt; -import it.cavallium.warppi.Platform.PngUtils.PngReader; +import it.cavallium.warppi.Engine; +import it.cavallium.warppi.Platform.ImageUtils.ImageReader; -public class DesktopPngReader implements PngReader { +public class DesktopImageReader implements ImageReader { - private final ar.com.hjg.pngj.PngReader r; + private ar.com.hjg.pngj.PngReader r; - public DesktopPngReader(final InputStream resourceStream) { + public DesktopImageReader(final InputStream resourceStream) throws IOException { r = new ar.com.hjg.pngj.PngReader(resourceStream); + // Try to read image converting it to png + if (r == null) { + final File f = File.createTempFile("picalculator-png", ".png"); + f.deleteOnExit(); + final BufferedImage img = ImageIO.read(resourceStream); + ImageIO.write(img, "PNG", f); + r = new ar.com.hjg.pngj.PngReader(f); + } } @Override diff --git a/desktop/src/main/java/it/cavallium/warppi/desktop/DesktopImageUtils.java b/desktop/src/main/java/it/cavallium/warppi/desktop/DesktopImageUtils.java new file mode 100644 index 00000000..7caac9d0 --- /dev/null +++ b/desktop/src/main/java/it/cavallium/warppi/desktop/DesktopImageUtils.java @@ -0,0 +1,16 @@ +package it.cavallium.warppi.desktop; + +import java.io.IOException; +import java.io.InputStream; + +import it.cavallium.warppi.Platform.ImageUtils.ImageReader; +import it.cavallium.warppi.Platform.ImageUtils; + +public class DesktopImageUtils implements ImageUtils { + + @Override + public ImageReader load(final InputStream resourceStream) throws IOException { + return new DesktopImageReader(resourceStream); + } + +} diff --git a/desktop/src/main/java/it/cavallium/warppi/desktop/DesktopPlatform.java b/desktop/src/main/java/it/cavallium/warppi/desktop/DesktopPlatform.java index c162f425..c08ec638 100644 --- a/desktop/src/main/java/it/cavallium/warppi/desktop/DesktopPlatform.java +++ b/desktop/src/main/java/it/cavallium/warppi/desktop/DesktopPlatform.java @@ -26,7 +26,7 @@ public class DesktopPlatform implements Platform { private final DesktopConsoleUtils cu; private final DesktopGpio gi; private final DesktopStorageUtils su; - private final PngUtils pu; + private final ImageUtils pu; private final String on; private final Map el; private final DesktopSettings settings; @@ -36,7 +36,7 @@ public class DesktopPlatform implements Platform { cu = new DesktopConsoleUtils(); gi = new DesktopGpio(); su = new DesktopStorageUtils(); - pu = new DesktopPngUtils(); + pu = new DesktopImageUtils(); on = System.getProperty("os.name").toLowerCase(); el = new HashMap<>(); el.put("CPU engine", new SwingEngine()); @@ -60,7 +60,7 @@ public class DesktopPlatform implements Platform { } @Override - public PngUtils getPngUtils() { + public ImageUtils getImageUtils() { return pu; } diff --git a/desktop/src/main/java/it/cavallium/warppi/desktop/DesktopPngUtils.java b/desktop/src/main/java/it/cavallium/warppi/desktop/DesktopPngUtils.java deleted file mode 100644 index a7ea6da4..00000000 --- a/desktop/src/main/java/it/cavallium/warppi/desktop/DesktopPngUtils.java +++ /dev/null @@ -1,14 +0,0 @@ -package it.cavallium.warppi.desktop; - -import java.io.InputStream; - -import it.cavallium.warppi.Platform.PngUtils; - -public class DesktopPngUtils implements PngUtils { - - @Override - public PngReader load(final InputStream resourceStream) { - return new DesktopPngReader(resourceStream); - } - -} diff --git a/engine-jogl/pom.xml b/engine-jogl/pom.xml index aa7a3f36..aeb6cab0 100644 --- a/engine-jogl/pom.xml +++ b/engine-jogl/pom.xml @@ -6,7 +6,7 @@ it.cavallium warppi - 0.9.0a2 + 0.9.0a3 warppi-engine-jogl @@ -16,7 +16,7 @@ it.cavallium warppi-core - 0.9.0a2 + 0.9.0a3 org.jogamp.jogl diff --git a/hardware/pom.xml b/hardware/pom.xml index e51cb1f7..26ab8ed6 100644 --- a/hardware/pom.xml +++ b/hardware/pom.xml @@ -6,7 +6,7 @@ it.cavallium warppi - 0.9.0a2 + 0.9.0a3 warppi-hardware @@ -18,12 +18,12 @@ it.cavallium warppi-core - 0.9.0a2 + 0.9.0a3 it.cavallium warppi-engine-jogl - 0.9.0a2 + 0.9.0a3 com.pi4j diff --git a/hardware/src/main/java/it/cavallium/warppi/hardware/HardwarePngReader.java b/hardware/src/main/java/it/cavallium/warppi/hardware/HardwareImageReader.java similarity index 87% rename from hardware/src/main/java/it/cavallium/warppi/hardware/HardwarePngReader.java rename to hardware/src/main/java/it/cavallium/warppi/hardware/HardwareImageReader.java index 30043b0b..61e944d7 100644 --- a/hardware/src/main/java/it/cavallium/warppi/hardware/HardwarePngReader.java +++ b/hardware/src/main/java/it/cavallium/warppi/hardware/HardwareImageReader.java @@ -3,13 +3,13 @@ package it.cavallium.warppi.hardware; import java.io.InputStream; import ar.com.hjg.pngj.ImageLineInt; -import it.cavallium.warppi.Platform.PngUtils.PngReader; +import it.cavallium.warppi.Platform.ImageUtils.ImageReader; -public class HardwarePngReader implements PngReader { +public class HardwareImageReader implements ImageReader { private final ar.com.hjg.pngj.PngReader r; - public HardwarePngReader(final InputStream resourceStream) { + public HardwareImageReader(final InputStream resourceStream) { r = new ar.com.hjg.pngj.PngReader(resourceStream); } diff --git a/hardware/src/main/java/it/cavallium/warppi/hardware/HardwareImageUtils.java b/hardware/src/main/java/it/cavallium/warppi/hardware/HardwareImageUtils.java new file mode 100644 index 00000000..586a870a --- /dev/null +++ b/hardware/src/main/java/it/cavallium/warppi/hardware/HardwareImageUtils.java @@ -0,0 +1,14 @@ +package it.cavallium.warppi.hardware; + +import java.io.InputStream; + +import it.cavallium.warppi.Platform.ImageUtils; + +public class HardwareImageUtils implements ImageUtils { + + @Override + public ImageReader load(final InputStream resourceStream) { + return new HardwareImageReader(resourceStream); + } + +} diff --git a/hardware/src/main/java/it/cavallium/warppi/hardware/HardwarePlatform.java b/hardware/src/main/java/it/cavallium/warppi/hardware/HardwarePlatform.java index 75aa321c..cbbf986d 100644 --- a/hardware/src/main/java/it/cavallium/warppi/hardware/HardwarePlatform.java +++ b/hardware/src/main/java/it/cavallium/warppi/hardware/HardwarePlatform.java @@ -21,7 +21,7 @@ public class HardwarePlatform implements Platform { private final HardwareConsoleUtils cu; private final HardwareGpio gi; private final HardwareStorageUtils su; - private final PngUtils pu; + private final ImageUtils pu; private final String on; private final Map el; private final HardwareSettings settings; @@ -31,7 +31,7 @@ public class HardwarePlatform implements Platform { cu = new HardwareConsoleUtils(); gi = new HardwareGpio(); su = new HardwareStorageUtils(); - pu = new HardwarePngUtils(); + pu = new HardwareImageUtils(); on = System.getProperty("os.name").toLowerCase(); el = new HashMap<>(); el.put("GPU engine", new JOGLEngine()); @@ -55,7 +55,7 @@ public class HardwarePlatform implements Platform { } @Override - public PngUtils getPngUtils() { + public ImageUtils getImageUtils() { return pu; } diff --git a/hardware/src/main/java/it/cavallium/warppi/hardware/HardwarePngUtils.java b/hardware/src/main/java/it/cavallium/warppi/hardware/HardwarePngUtils.java deleted file mode 100644 index c5943f17..00000000 --- a/hardware/src/main/java/it/cavallium/warppi/hardware/HardwarePngUtils.java +++ /dev/null @@ -1,14 +0,0 @@ -package it.cavallium.warppi.hardware; - -import java.io.InputStream; - -import it.cavallium.warppi.Platform.PngUtils; - -public class HardwarePngUtils implements PngUtils { - - @Override - public PngReader load(final InputStream resourceStream) { - return new HardwarePngReader(resourceStream); - } - -} diff --git a/pom.xml b/pom.xml index daf4c2cb..c1f1acbf 100755 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ it.cavallium warppi - 0.9.0a2 + 0.9.0a3 pom WarpPI Calculator diff --git a/rules/pom.xml b/rules/pom.xml index 52156d1d..437da7d6 100644 --- a/rules/pom.xml +++ b/rules/pom.xml @@ -6,7 +6,7 @@ it.cavallium warppi - 0.9.0a2 + 0.9.0a3 warppi-rules @@ -17,7 +17,7 @@ it.cavallium warppi-core - 0.9.0a2 + 0.9.0a3 diff --git a/teavm/pom.xml b/teavm/pom.xml index fc8c9325..d3212794 100644 --- a/teavm/pom.xml +++ b/teavm/pom.xml @@ -6,7 +6,7 @@ it.cavallium warppi - 0.9.0a2 + 0.9.0a3 warppi-teavm jar @@ -17,12 +17,12 @@ it.cavallium warppi-core - 0.9.0a2 + 0.9.0a3 it.cavallium warppi-rules - 0.9.0a2 + 0.9.0a3 org.teavm diff --git a/teavm/src/main/java/it/cavallium/warppi/teavm/TeaVMPngReader.java b/teavm/src/main/java/it/cavallium/warppi/teavm/TeaVMImageReader.java similarity index 87% rename from teavm/src/main/java/it/cavallium/warppi/teavm/TeaVMPngReader.java rename to teavm/src/main/java/it/cavallium/warppi/teavm/TeaVMImageReader.java index 2c21fce7..f0c1cff4 100644 --- a/teavm/src/main/java/it/cavallium/warppi/teavm/TeaVMPngReader.java +++ b/teavm/src/main/java/it/cavallium/warppi/teavm/TeaVMImageReader.java @@ -3,13 +3,13 @@ package it.cavallium.warppi.teavm; import java.io.InputStream; import ar.com.hjg.pngj.ImageLineInt; -import it.cavallium.warppi.Platform.PngUtils.PngReader; +import it.cavallium.warppi.Platform.ImageUtils.ImageReader; -public class TeaVMPngReader implements PngReader { +public class TeaVMImageReader implements ImageReader { private final ar.com.hjg.pngj.PngReader r; - public TeaVMPngReader(final InputStream resourceStream) { + public TeaVMImageReader(final InputStream resourceStream) { r = new ar.com.hjg.pngj.PngReader(resourceStream); } diff --git a/teavm/src/main/java/it/cavallium/warppi/teavm/TeaVMImageUtils.java b/teavm/src/main/java/it/cavallium/warppi/teavm/TeaVMImageUtils.java new file mode 100644 index 00000000..9b0b493b --- /dev/null +++ b/teavm/src/main/java/it/cavallium/warppi/teavm/TeaVMImageUtils.java @@ -0,0 +1,14 @@ +package it.cavallium.warppi.teavm; + +import java.io.InputStream; + +import it.cavallium.warppi.Platform.ImageUtils; + +public class TeaVMImageUtils implements ImageUtils { + + @Override + public ImageReader load(final InputStream resourceStream) { + return new TeaVMImageReader(resourceStream); + } + +} diff --git a/teavm/src/main/java/it/cavallium/warppi/teavm/TeaVMPlatform.java b/teavm/src/main/java/it/cavallium/warppi/teavm/TeaVMPlatform.java index d63899ae..b60fc0ff 100644 --- a/teavm/src/main/java/it/cavallium/warppi/teavm/TeaVMPlatform.java +++ b/teavm/src/main/java/it/cavallium/warppi/teavm/TeaVMPlatform.java @@ -21,7 +21,7 @@ public class TeaVMPlatform implements Platform { private final TeaVMStorageUtils su; private final String on; private final Map el; - private final TeaVMPngUtils pu; + private final TeaVMImageUtils pu; private final TeaVMSettings settings; private Boolean runningOnRaspberryOverride = null; @@ -29,7 +29,7 @@ public class TeaVMPlatform implements Platform { cu = new TeaVMConsoleUtils(); gi = new TeaVMGpio(); su = new TeaVMStorageUtils(); - pu = new TeaVMPngUtils(); + pu = new TeaVMImageUtils(); on = "JavaScript"; el = new HashMap<>(); el.put("HTML5 engine", new HtmlEngine()); @@ -52,7 +52,7 @@ public class TeaVMPlatform implements Platform { } @Override - public PngUtils getPngUtils() { + public ImageUtils getImageUtils() { return pu; } diff --git a/teavm/src/main/java/it/cavallium/warppi/teavm/TeaVMPngUtils.java b/teavm/src/main/java/it/cavallium/warppi/teavm/TeaVMPngUtils.java deleted file mode 100644 index 6378dc2d..00000000 --- a/teavm/src/main/java/it/cavallium/warppi/teavm/TeaVMPngUtils.java +++ /dev/null @@ -1,14 +0,0 @@ -package it.cavallium.warppi.teavm; - -import java.io.InputStream; - -import it.cavallium.warppi.Platform.PngUtils; - -public class TeaVMPngUtils implements PngUtils { - - @Override - public PngReader load(final InputStream resourceStream) { - return new TeaVMPngReader(resourceStream); - } - -} diff --git a/util/pom.xml b/util/pom.xml index 84e1c6a6..42cdcab3 100644 --- a/util/pom.xml +++ b/util/pom.xml @@ -7,7 +7,7 @@ it.cavallium warppi - 0.9.0a2 + 0.9.0a3 warppi-util From 1a51ae5698e46f4d25bf8a843816b1599e6ac6ab Mon Sep 17 00:00:00 2001 From: Cavallium Date: Tue, 16 Oct 2018 15:56:27 +0200 Subject: [PATCH 12/18] Extra --- .../warppi/extra/tetris/TetrisGame.java | 84 ++++++++++++++++++- .../warppi/extra/tetris/TetrisScreen.java | 35 +++++++- .../warppi/extra/tetris/Tetromino.java | 6 +- .../warppi/extra/tetris/TetrominoICyan.java | 4 +- .../warppi/extra/tetris/TetrominoJBlue.java | 4 +- .../warppi/extra/tetris/TetrominoLOrange.java | 4 +- .../warppi/extra/tetris/TetrominoOYellow.java | 2 +- .../warppi/extra/tetris/TetrominoSGreen.java | 4 +- .../warppi/extra/tetris/TetrominoTPurple.java | 4 +- .../warppi/extra/tetris/TetrominoZRed.java | 4 +- 10 files changed, 133 insertions(+), 18 deletions(-) diff --git a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrisGame.java b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrisGame.java index 410f718c..8d4ea909 100644 --- a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrisGame.java +++ b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrisGame.java @@ -39,9 +39,22 @@ public class TetrisGame { nextTetromino.fixInitialPosition(); } - public void update(float dt, boolean leftPressed, boolean rightPressed, boolean downPressed, boolean okPressed, boolean backPressed) { + public void update(float dt, boolean leftPressed, boolean rightPressed, boolean downPressed, boolean upPressed, boolean okPressed, boolean backPressed) { currentTime += dt; tickTimer += dt; + if (!(leftPressed && rightPressed)) { + if (leftPressed) { + move(this.currentTetromino, -1, 0, 0); + } else if (rightPressed) { + move(this.currentTetromino, 1, 0, 0); + } + } + if (downPressed) { + move(this.currentTetromino, 0, -1, 0); + } + if (upPressed) { + move(this.currentTetromino, 0, 0, 1); + } while (tickTimer >= TICK_TIME) { tickTimer -= TICK_TIME; gameTick(leftPressed, rightPressed, downPressed, okPressed, backPressed); @@ -55,9 +68,72 @@ public class TetrisGame { } public void gameTick(boolean leftPressed, boolean rightPressed, boolean downPressed, boolean okPressed, boolean backPressed) { - this.currentTetromino.setY((byte) (this.currentTetromino.getY() - 1)); + if (move(this.currentTetromino, 0, -1, 0)) { + + } else { + // Spawn new tetromino and write the old to the permanent grid + drawCurrentTetromino(grid); + checkLines(); + placeNextTetromino(); + if (move(this.currentTetromino, 0, 0, 0) == false) { + // Lose + this.gameStatus = GameStatus.LOST; + } + } } + private void checkLines() { + for(int i = 0; i < HEIGHT; i++) { + boolean scored = true; + for (int x = 0; x < WIDTH; x++) { + if (this.grid[x + i * WIDTH] == null) { + scored = false; + break; + } + } + if (scored) { + this.score += WIDTH; + for (int x = 0; x < WIDTH; x++) { + int y = 1; + while (i + y < HEIGHT) { + this.grid[x + (i + y - 1) * WIDTH] = this.grid[x + (i + y) * WIDTH]; + y++; + } + } + } + } + } + + private boolean move(Tetromino t, int dX, int dY, int dRotation) { + byte rot = (byte) ((t.getRotation() + dRotation) % 4); + boolean[] block = t.getRenderedBlock(rot); + int blockSize = t.getTetrominoGridSize(); + int half1 = (int) Math.floor(((double)t.getTetrominoGridSize())/2d); + int half2 = blockSize - half1; + byte aX = (byte)(t.getX()+dX), aY = (byte)(t.getY()+dY); + int blockX = 0, blockY = 0; + for (int x = aX - half1; x < aX + half2; x++) { + for (int y = aY - half1; y < aY + half2; y++) { + if (block[blockX + blockY * blockSize] == true) { + if (x >= 0 & y >= 0 & x < WIDTH & (x + y * WIDTH) < this.grid.length) { + if (this.grid[x + y * WIDTH] != null) { + return false; + } + } else { + return false; + } + } + blockY++; + } + blockY = 0; + blockX++; + } + t.setRotation(rot); + t.setX(aX); + t.setY(aY); + return true; + } + public void renderGrid() { this.renderedGrid = Arrays.copyOf(grid, grid.length); drawCurrentTetromino(this.renderedGrid); @@ -95,6 +171,10 @@ public class TetrisGame { return new TetrominoZRed(middleX, middleY, rotation); } } + + public Tetromino getNextTetromino() { + return this.nextTetromino; + } private void drawCurrentTetromino(BlockColor[] grid) { currentTetromino.draw(grid, WIDTH); diff --git a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrisScreen.java b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrisScreen.java index 30c5c402..82312a1f 100644 --- a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrisScreen.java +++ b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrisScreen.java @@ -22,6 +22,8 @@ public class TetrisScreen extends Screen { private boolean rightPressed; + private boolean upPressed; + private boolean downPressed; private boolean okPressed; @@ -61,7 +63,8 @@ public class TetrisScreen extends Screen { @Override public void beforeRender(final float dt) { Engine.INSTANCE.getHardwareDevice().getDisplayManager().renderer.glClearColor(0xff000000); - g.update(dt, leftPressed, rightPressed, downPressed, okPressed, backPressed); + g.update(dt, leftPressed, rightPressed, downPressed, upPressed, okPressed, backPressed); + upPressed = false; } @Override @@ -82,7 +85,27 @@ public class TetrisScreen extends Screen { if (type != null) { r.glFillRect(leftOffset + x * 5, topOffset + (TetrisGame.HEIGHT+3-y) * 5, 5, 5, renderedGrid[offset].ordinal() * 5, 0, 5, 5); } else { -// r.glFillRect(leftOffset + x * 5, topOffset + y * 5, 5, 5, 1 * 5, 0, 5, 5); + //r.glFillRect(leftOffset + x * 5, topOffset + (TetrisGame.HEIGHT+3-y) * 5, 5, 5, 1 * 5, 0, 2, 2); + } + } + } + + + Tetromino nextTetromino = g.getNextTetromino(); + if (nextTetromino != null) { + boolean[] renderedNextTetromino = nextTetromino.getRenderedBlock(); + final BlockColor type = nextTetromino.getColor(); + int nextTetrominoGridSize = nextTetromino.getTetrominoGridSize(); + for (int y = 0; y < nextTetrominoGridSize; y++) { + for (int x = 0; x < nextTetrominoGridSize; x++) { + final int offset = x+y*nextTetrominoGridSize; + if (renderedNextTetromino[offset]) { + if (type != null) { + r.glFillRect(leftOffset + (TetrisGame.WIDTH + 3 + x) * 5, topOffset + (3 - y) * 5, 5, 5, type.ordinal() * 5, 0, 5, 5); + } else { + //r.glFillRect(leftOffset + x * 5, topOffset + (TetrisGame.HEIGHT+3-y) * 5, 5, 5, 1 * 5, 0, 2, 2); + } + } } } } @@ -99,6 +122,10 @@ public class TetrisScreen extends Screen { rightPressed = true; return true; } + case UP: { + upPressed = true; + return true; + } case DOWN: { downPressed = true; return true; @@ -126,6 +153,10 @@ public class TetrisScreen extends Screen { rightPressed = false; return true; } + case UP: { + upPressed = false; + return true; + } case DOWN: { downPressed = false; return true; diff --git a/core/src/main/java/it/cavallium/warppi/extra/tetris/Tetromino.java b/core/src/main/java/it/cavallium/warppi/extra/tetris/Tetromino.java index 0ca620c4..cfc1f3cd 100644 --- a/core/src/main/java/it/cavallium/warppi/extra/tetris/Tetromino.java +++ b/core/src/main/java/it/cavallium/warppi/extra/tetris/Tetromino.java @@ -84,7 +84,11 @@ public abstract class Tetromino { } public abstract int getTetrominoGridSize(); - protected abstract boolean[] getRenderedBlock(); + protected abstract boolean[] getRenderedBlock(byte dRotation); + protected boolean[] getRenderedBlock() { + return getRenderedBlock(this.rotation); + } + @Override public int hashCode() { diff --git a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoICyan.java b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoICyan.java index 4e908b6f..d15a2d7e 100644 --- a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoICyan.java +++ b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoICyan.java @@ -11,8 +11,8 @@ public class TetrominoICyan extends Tetromino { } @Override - public boolean[] getRenderedBlock() { - switch(getRotation()) { + public boolean[] getRenderedBlock(final byte rotation) { + switch(rotation) { case 0: return new boolean[] { o,o,o,o, diff --git a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoJBlue.java b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoJBlue.java index be8049e3..eae5698d 100644 --- a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoJBlue.java +++ b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoJBlue.java @@ -11,8 +11,8 @@ public class TetrominoJBlue extends Tetromino { } @Override - public boolean[] getRenderedBlock() { - switch(getRotation()) { + public boolean[] getRenderedBlock(final byte rotation) { + switch(rotation) { case 0: return new boolean[] { w,o,o, diff --git a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoLOrange.java b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoLOrange.java index 2d8911f0..09d5d384 100644 --- a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoLOrange.java +++ b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoLOrange.java @@ -11,8 +11,8 @@ public class TetrominoLOrange extends Tetromino { } @Override - public boolean[] getRenderedBlock() { - switch(getRotation()) { + public boolean[] getRenderedBlock(final byte rotation) { + switch(rotation) { case 0: return new boolean[] { o,o,w, diff --git a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoOYellow.java b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoOYellow.java index aa2d0602..7425a75d 100644 --- a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoOYellow.java +++ b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoOYellow.java @@ -10,7 +10,7 @@ public class TetrominoOYellow extends Tetromino { } @Override - public boolean[] getRenderedBlock() { + public boolean[] getRenderedBlock(byte rotation) { return new boolean[] { w,w, w,w, diff --git a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoSGreen.java b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoSGreen.java index 10e59ba4..1bb35053 100644 --- a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoSGreen.java +++ b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoSGreen.java @@ -11,8 +11,8 @@ public class TetrominoSGreen extends Tetromino { } @Override - public boolean[] getRenderedBlock() { - switch(getRotation()) { + public boolean[] getRenderedBlock(final byte rotation) { + switch(rotation) { case 0: return new boolean[] { o,w,w, diff --git a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoTPurple.java b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoTPurple.java index bd30e433..d656d6fc 100644 --- a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoTPurple.java +++ b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoTPurple.java @@ -11,8 +11,8 @@ public class TetrominoTPurple extends Tetromino { } @Override - public boolean[] getRenderedBlock() { - switch(getRotation()) { + public boolean[] getRenderedBlock(byte rotation) { + switch(rotation) { case 0: return new boolean[] { o,w,o, diff --git a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoZRed.java b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoZRed.java index 0b7a70d5..5f99f83d 100644 --- a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoZRed.java +++ b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoZRed.java @@ -11,8 +11,8 @@ public class TetrominoZRed extends Tetromino { } @Override - public boolean[] getRenderedBlock() { - switch(getRotation()) { + public boolean[] getRenderedBlock(final byte rotation) { + switch(rotation) { case 0: return new boolean[] { w,w,o, From 38c87109295f11d5437ffce13cc26d00dfaf3b8e Mon Sep 17 00:00:00 2001 From: Cavallium Date: Tue, 16 Oct 2018 17:37:21 +0200 Subject: [PATCH 13/18] Extra --- .../warppi/extra/tetris/TetrisGame.java | 34 ++++++++++--------- .../warppi/extra/tetris/TetrisScreen.java | 4 +-- .../warppi/extra/tetris/Tetromino.java | 2 +- 3 files changed, 21 insertions(+), 19 deletions(-) diff --git a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrisGame.java b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrisGame.java index 8d4ea909..41eccbea 100644 --- a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrisGame.java +++ b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrisGame.java @@ -50,7 +50,7 @@ public class TetrisGame { } } if (downPressed) { - move(this.currentTetromino, 0, -1, 0); + move(this.currentTetromino, 0, 1, 0); } if (upPressed) { move(this.currentTetromino, 0, 0, 1); @@ -68,7 +68,7 @@ public class TetrisGame { } public void gameTick(boolean leftPressed, boolean rightPressed, boolean downPressed, boolean okPressed, boolean backPressed) { - if (move(this.currentTetromino, 0, -1, 0)) { + if (move(this.currentTetromino, 0, 1, 0)) { } else { // Spawn new tetromino and write the old to the permanent grid @@ -83,21 +83,23 @@ public class TetrisGame { } private void checkLines() { - for(int i = 0; i < HEIGHT; i++) { + for(int i = HEIGHT - 1; i >= 0; i--) { boolean scored = true; - for (int x = 0; x < WIDTH; x++) { - if (this.grid[x + i * WIDTH] == null) { - scored = false; - break; - } - } - if (scored) { - this.score += WIDTH; + while (scored) { for (int x = 0; x < WIDTH; x++) { - int y = 1; - while (i + y < HEIGHT) { - this.grid[x + (i + y - 1) * WIDTH] = this.grid[x + (i + y) * WIDTH]; - y++; + if (this.grid[x + i * WIDTH] == null) { + scored = false; + break; + } + } + if (scored) { + this.score += WIDTH; + for (int x = 0; x < WIDTH; x++) { + int y = HEIGHT - i - 2; + while (i + y > 0) { + this.grid[x + (i + y + 1) * WIDTH] = this.grid[x + (i + y) * WIDTH]; + y--; + } } } } @@ -153,7 +155,7 @@ public class TetrisGame { private Tetromino generateRandomTetromino() { int s = (int) (Math.random() * 7); - final byte middleX = (byte)((WIDTH - 1)/2), middleY = (byte)(HEIGHT - 1), rotation = (byte) (Math.random() * 4); + final byte middleX = (byte)((WIDTH - 1)/2), middleY = 0, rotation = (byte) (Math.random() * 4); switch (s) { case 0: return new TetrominoICyan(middleX, middleY, rotation); diff --git a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrisScreen.java b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrisScreen.java index 82312a1f..3b491ee2 100644 --- a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrisScreen.java +++ b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrisScreen.java @@ -83,9 +83,9 @@ public class TetrisScreen extends Screen { final int offset = x+y*TetrisGame.WIDTH; final BlockColor type = renderedGrid[offset]; if (type != null) { - r.glFillRect(leftOffset + x * 5, topOffset + (TetrisGame.HEIGHT+3-y) * 5, 5, 5, renderedGrid[offset].ordinal() * 5, 0, 5, 5); + r.glFillRect(leftOffset + x * 5, topOffset + (y+3) * 5, 5, 5, renderedGrid[offset].ordinal() * 5, 0, 5, 5); } else { - //r.glFillRect(leftOffset + x * 5, topOffset + (TetrisGame.HEIGHT+3-y) * 5, 5, 5, 1 * 5, 0, 2, 2); + r.glFillRect(leftOffset + x * 5, topOffset + (y+3) * 5, 5, 5, 1 * 5, 0, 2, 2); } } } diff --git a/core/src/main/java/it/cavallium/warppi/extra/tetris/Tetromino.java b/core/src/main/java/it/cavallium/warppi/extra/tetris/Tetromino.java index cfc1f3cd..ebadf3b2 100644 --- a/core/src/main/java/it/cavallium/warppi/extra/tetris/Tetromino.java +++ b/core/src/main/java/it/cavallium/warppi/extra/tetris/Tetromino.java @@ -80,7 +80,7 @@ public abstract class Tetromino { } public void fixInitialPosition() { - this.y -= (byte) (this.getTetrominoGridSize()/2); + this.y += (byte) (this.getTetrominoGridSize()/2); } public abstract int getTetrominoGridSize(); From f7a7f6d200d0c42a8a6d028acecfd290ca3849a3 Mon Sep 17 00:00:00 2001 From: Cavallium Date: Tue, 16 Oct 2018 19:28:43 +0200 Subject: [PATCH 14/18] Extra --- .../warppi/extra/tetris/TetrisGame.java | 11 +++++--- .../warppi/extra/tetris/TetrisScreen.java | 15 +++++++---- .../warppi/extra/tetris/TetrominoTPurple.java | 2 +- core/src/main/resources/tetrisskin.png | Bin 2289 -> 2379 bytes core/src/main/resources/tetrisskin.xcf | Bin 1600 -> 1670 bytes .../gui/graphicengine/html/HtmlEngine.java | 24 +++++++++++++++--- 6 files changed, 39 insertions(+), 13 deletions(-) diff --git a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrisGame.java b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrisGame.java index 41eccbea..319bfc90 100644 --- a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrisGame.java +++ b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrisGame.java @@ -78,6 +78,7 @@ public class TetrisGame { if (move(this.currentTetromino, 0, 0, 0) == false) { // Lose this.gameStatus = GameStatus.LOST; + playAgain(); } } } @@ -95,9 +96,9 @@ public class TetrisGame { if (scored) { this.score += WIDTH; for (int x = 0; x < WIDTH; x++) { - int y = HEIGHT - i - 2; - while (i + y > 0) { - this.grid[x + (i + y + 1) * WIDTH] = this.grid[x + (i + y) * WIDTH]; + int y = i; + while (y > 0) { + this.grid[x + (y) * WIDTH] = this.grid[x + (y-1) * WIDTH]; y--; } } @@ -185,4 +186,8 @@ public class TetrisGame { public BlockColor[] getRenderedGrid() { return renderedGrid; } + + public int getScore() { + return this.score; + } } diff --git a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrisScreen.java b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrisScreen.java index 3b491ee2..94b140c4 100644 --- a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrisScreen.java +++ b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrisScreen.java @@ -85,7 +85,7 @@ public class TetrisScreen extends Screen { if (type != null) { r.glFillRect(leftOffset + x * 5, topOffset + (y+3) * 5, 5, 5, renderedGrid[offset].ordinal() * 5, 0, 5, 5); } else { - r.glFillRect(leftOffset + x * 5, topOffset + (y+3) * 5, 5, 5, 1 * 5, 0, 2, 2); + r.glFillRect(leftOffset + x * 5, topOffset + (y+3) * 5, 5, 5, 7 * 5, 0, 5, 5); } } } @@ -93,22 +93,26 @@ public class TetrisScreen extends Screen { Tetromino nextTetromino = g.getNextTetromino(); if (nextTetromino != null) { + r.glColor3f(0.25f, 0.25f, 0.25f); + r.glFillColor(leftOffset + (TetrisGame.WIDTH + 3) * 5, topOffset + 3 * 5, 5*4, 5*4); + r.glColor3f(1,1,1); boolean[] renderedNextTetromino = nextTetromino.getRenderedBlock(); final BlockColor type = nextTetromino.getColor(); int nextTetrominoGridSize = nextTetromino.getTetrominoGridSize(); + int nextGridOffset = 4*5/2 - nextTetrominoGridSize*5/2; for (int y = 0; y < nextTetrominoGridSize; y++) { for (int x = 0; x < nextTetrominoGridSize; x++) { final int offset = x+y*nextTetrominoGridSize; if (renderedNextTetromino[offset]) { if (type != null) { - r.glFillRect(leftOffset + (TetrisGame.WIDTH + 3 + x) * 5, topOffset + (3 - y) * 5, 5, 5, type.ordinal() * 5, 0, 5, 5); - } else { - //r.glFillRect(leftOffset + x * 5, topOffset + (TetrisGame.HEIGHT+3-y) * 5, 5, 5, 1 * 5, 0, 2, 2); + r.glFillRect(leftOffset + nextGridOffset + (TetrisGame.WIDTH + 3 + x) * 5, topOffset + nextGridOffset + (3 + y) * 5, 5, 5, type.ordinal() * 5, 0, 5, 5); } } } } } + r.glColor3f(1,1,1); + r.glDrawStringLeft(leftOffset + (TetrisGame.WIDTH + 3) * 5, topOffset + (3+5) * 5, "SCORE:"+g.getScore()); } @Override @@ -132,6 +136,7 @@ public class TetrisScreen extends Screen { } case OK: { okPressed = true; + g.playAgain(); return true; } case BACK: { @@ -179,6 +184,6 @@ public class TetrisScreen extends Screen { @Override public String getSessionTitle() { - return "Absolutely Not Tetris"; + return "Tetris"; } } diff --git a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoTPurple.java b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoTPurple.java index d656d6fc..4b187f3d 100644 --- a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoTPurple.java +++ b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoTPurple.java @@ -2,7 +2,7 @@ package it.cavallium.warppi.extra.tetris; public class TetrominoTPurple extends Tetromino { public TetrominoTPurple(byte x, byte y, byte rotation) { - super(x, y, rotation, TetrominoType.I_CYAN); + super(x, y, rotation, TetrominoType.T_PURPLE); } @Override diff --git a/core/src/main/resources/tetrisskin.png b/core/src/main/resources/tetrisskin.png index f73890af931f6f15c31fc9bf4f50622dac8c6acc..07420c4a2bf0bdbe7b45a33c21da4187866e2d43 100644 GIT binary patch delta 2264 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 AcmMzZ diff --git a/core/src/main/resources/tetrisskin.xcf b/core/src/main/resources/tetrisskin.xcf index e49ca87b1b873bc5b558c05d5bb1dcedf7809fb1..11cba233e806b6b53d819843048af588a27104bc 100644 GIT binary patch delta 324 zcmX@W)5g1@iiMShfkjMway5%=EDOVbF(&o@V&V+{3z_EpFXRN%{|YxU9sjqnglYEw z5>iyK0QIvJvTS4l>H^ttlf{Y|M2oO0#{7d=Bm=bwXe`jee}&OZY5${v9H3qQO2AGb zMg`CkkVQa^K&yZjG_ab1E!xg1?*g?*5#b}C*^CJD@koH&&j~h=lTA@f{l7Q^Dz4|d G!2kf<5N|O6 delta 212 zcmZqUJ;1Y}iiJ^eay^TzqawrqLZ&(Y3pv5`zru}7$Nz0CVVeEF1fL8GP(4c_%SM(G z#mPTetQch`o3Sb~|NpNzxsg?h8K@UT3xdo5ssiC?AXy?ac{8gSQ0*I5dE@{8)etrV e?PCPniCqk28z)e=KbxYM(tjBSR9w$@mjM7=4NQ6f diff --git a/teavm/src/main/java/it/cavallium/warppi/gui/graphicengine/html/HtmlEngine.java b/teavm/src/main/java/it/cavallium/warppi/gui/graphicengine/html/HtmlEngine.java index 8843fddc..55db8dca 100644 --- a/teavm/src/main/java/it/cavallium/warppi/gui/graphicengine/html/HtmlEngine.java +++ b/teavm/src/main/java/it/cavallium/warppi/gui/graphicengine/html/HtmlEngine.java @@ -24,6 +24,7 @@ import it.cavallium.warppi.flow.BehaviorSubject; import it.cavallium.warppi.flow.Observable; import it.cavallium.warppi.gui.graphicengine.GraphicEngine; import it.cavallium.warppi.gui.graphicengine.RenderingLoop; +import it.unimi.dsi.fastutil.objects.Object2IntArrayMap; public class HtmlEngine implements GraphicEngine { @@ -70,6 +71,15 @@ public class HtmlEngine implements GraphicEngine { } private String previousValue = ""; + private static final Object2IntArrayMap keyNames = new Object2IntArrayMap<>(); + + static { + keyNames.put(" ", 32); + keyNames.put("ArrowUp", 38); + keyNames.put("ArrowDown", 40); + keyNames.put("ArrowLeft", 37); + keyNames.put("ArrowRight", 39); + } @JSBody(params = { "ctx", "enabled" }, script = "" + "ctx.mozImageSmoothingEnabled = enabled;" + "ctx.oImageSmoothingEnabled = enabled;" + "ctx.webkitImageSmoothingEnabled = enabled;" + "ctx.msImageSmoothingEnabled = enabled;" + "ctx.imageSmoothingEnabled = enabled;") public static native void setImageSmoothingEnabled(CanvasRenderingContext2D ctx, boolean enabled); @@ -110,12 +120,18 @@ public class HtmlEngine implements GraphicEngine { HtmlEngine.document.getBody().appendChild(keyInput); keyInput.setTabIndex(0); keyInput.setValue("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); - keyInput.addEventListener("keydown", (final KeyboardEvent evt) -> { + HtmlEngine.document.addEventListener("keydown", (final KeyboardEvent evt) -> { evt.preventDefault(); new Thread(() -> { - previousValue = keyInput.getValue(); - Keyboard.debugKey(evt.getKeyCode(), false); - Keyboard.debugKey(evt.getKeyCode(), true); + Keyboard.debugKey(keyNames .getOrDefault(evt.getKey(), evt.getKeyCode()), false); + System.out.println(evt.getKeyCode()); + System.out.println("" + (int) evt.getKey().charAt(0)); + }).start(); + }); + HtmlEngine.document.addEventListener("keyup", (final KeyboardEvent evt) -> { + evt.preventDefault(); + new Thread(() -> { + Keyboard.debugKey(keyNames .getOrDefault(evt.getKey(), evt.getKeyCode()), true); System.out.println(evt.getKeyCode()); System.out.println("" + (int) evt.getKey().charAt(0)); }).start(); From 6e5ed8ef9481c10525ce7e7b3956fb3f5e103906 Mon Sep 17 00:00:00 2001 From: Andrea Cavalli Date: Tue, 16 Oct 2018 20:03:12 +0200 Subject: [PATCH 15/18] Fixed buggy keyboard behavior --- .../it/cavallium/warppi/device/Keyboard.java | 2 +- .../gui/graphicengine/html/HtmlEngine.java | 118 +++++++++--------- 2 files changed, 61 insertions(+), 59 deletions(-) diff --git a/core/src/main/java/it/cavallium/warppi/device/Keyboard.java b/core/src/main/java/it/cavallium/warppi/device/Keyboard.java index 3e91d26c..a949d5d1 100644 --- a/core/src/main/java/it/cavallium/warppi/device/Keyboard.java +++ b/core/src/main/java/it/cavallium/warppi/device/Keyboard.java @@ -767,7 +767,7 @@ public class Keyboard { { Key.SHIFT, Key.SHIFT, Key.SHIFT }, /* 0,0 */ { Key.ALPHA, Key.ALPHA, Key.ALPHA }, /* 0,1 */ { Key.NONE, Key.NONE, Key.NONE }, /* 0,2 */ - { Key.NONE, Key.NONE, Key.NONE }, /* 0,3 */ + { Key.UP, Key.NONE, Key.NONE }, /* 0,3 */ { Key.NONE, Key.NONE, Key.NONE }, /* 0,4 */ { Key.SETTINGS, Key.NONE, Key.NONE }, /* 0,5 */ { Key.BRIGHTNESS_CYCLE, Key.BRIGHTNESS_CYCLE_REVERSE, Key.ZOOM_MODE }, /* 0,6 */ diff --git a/teavm/src/main/java/it/cavallium/warppi/gui/graphicengine/html/HtmlEngine.java b/teavm/src/main/java/it/cavallium/warppi/gui/graphicengine/html/HtmlEngine.java index 55db8dca..ffe0592a 100644 --- a/teavm/src/main/java/it/cavallium/warppi/gui/graphicengine/html/HtmlEngine.java +++ b/teavm/src/main/java/it/cavallium/warppi/gui/graphicengine/html/HtmlEngine.java @@ -167,71 +167,73 @@ public class HtmlEngine implements GraphicEngine { }); final NodeList buttons = HtmlEngine.document.getBody().getElementsByTagName("button"); for (int i = 0; i < buttons.getLength(); i++) - if (buttons.item(i).hasAttribute("keycode")) - buttons.item(i).addEventListener("click", (final Event evt) -> { - evt.preventDefault(); - final EventTarget target = evt.getCurrentTarget(); - final HTMLButtonElement button = target.cast(); - new Thread(() -> { - try { - if (button.hasAttribute("keycode") && button.getAttribute("keycode").contains(",")) { - final String code = button.getAttribute("keycode"); - final String[] coordinates = code.split(",", 2); - final boolean removeshift = Keyboard.shift && Integer.parseInt(coordinates[0]) != 0 && Integer.parseInt(coordinates[1]) != 0; - final boolean removealpha = Keyboard.alpha && Integer.parseInt(coordinates[0]) != 0 && Integer.parseInt(coordinates[1]) != 1; - Keyboard.keyRaw(Integer.parseInt(coordinates[0]), Integer.parseInt(coordinates[1]), false); - if (removeshift) - Keyboard.keyRaw(0, 0, false); - if (removealpha) - Keyboard.keyRaw(0, 1, false); - Thread.sleep(100); - Keyboard.keyRaw(Integer.parseInt(coordinates[0]), Integer.parseInt(coordinates[1]), true); - if (removeshift) - Keyboard.keyRaw(0, 0, true); - if (removealpha) - Keyboard.keyRaw(0, 1, true); - } else if (Keyboard.alpha && !Keyboard.shift) { - if (button.hasAttribute("keycodea")) { - Keyboard.debugKey(Integer.parseInt(button.getAttribute("keycodea")), false); - Keyboard.debugKey(Integer.parseInt(button.getAttribute("keycodea")), true); - } else { - Keyboard.debugKey(Integer.parseInt(button.getAttribute("keycode")), false); - Keyboard.debugKey(Integer.parseInt(button.getAttribute("keycode")), true); - } - } else if (!Keyboard.alpha && Keyboard.shift) { - if (button.hasAttribute("keycodes")) { - Keyboard.debugKey(Integer.parseInt(button.getAttribute("keycodes")), false); - Keyboard.debugKey(Integer.parseInt(button.getAttribute("keycodes")), true); - } else { - Keyboard.debugKey(Integer.parseInt(button.getAttribute("keycode")), false); - Keyboard.debugKey(Integer.parseInt(button.getAttribute("keycode")), true); - } - } else if (Keyboard.alpha && Keyboard.shift) { - if (button.hasAttribute("keycodesa")) { - Keyboard.debugKey(Integer.parseInt(button.getAttribute("keycodesa")), false); - Keyboard.debugKey(Integer.parseInt(button.getAttribute("keycodesa")), true); - } else if (button.hasAttribute("keycodes")) { - Keyboard.debugKey(Integer.parseInt(button.getAttribute("keycodes")), false); - Keyboard.debugKey(Integer.parseInt(button.getAttribute("keycodes")), true); - } else { - Keyboard.debugKey(Integer.parseInt(button.getAttribute("keycode")), false); - Keyboard.debugKey(Integer.parseInt(button.getAttribute("keycode")), true); - } - } else { - Keyboard.debugKey(Integer.parseInt(button.getAttribute("keycode")), false); - Keyboard.debugKey(Integer.parseInt(button.getAttribute("keycode")), true); - } - } catch (final Exception ex) { - ex.printStackTrace(); - } - }).start(); + if (buttons.item(i).hasAttribute("keycode")) { + buttons.item(i).addEventListener("touchstart", (final Event evt) -> { + buttonEvent(evt, false); }); + buttons.item(i).addEventListener("touchend", (final Event evt) -> { + buttonEvent(evt, true); + }); + buttons.item(i).addEventListener("mousedown", (final Event evt) -> { + buttonEvent(evt, false); + }); + buttons.item(i).addEventListener("mouseup", (final Event evt) -> { + buttonEvent(evt, true); + }); + } renderer = new HtmlRenderer(this, g); initialized = true; if (onInitialized != null) onInitialized.run(); } + private void buttonEvent(Event evt, boolean released) { + evt.preventDefault(); + final EventTarget target = evt.getCurrentTarget(); + final HTMLButtonElement button = target.cast(); + new Thread(() -> { + try { + if (button.hasAttribute("keycode") && button.getAttribute("keycode").contains(",")) { + final String code = button.getAttribute("keycode"); + final String[] coordinates = code.split(",", 2); + final boolean removeshift = Keyboard.shift && Integer.parseInt(coordinates[0]) != 0 && Integer.parseInt(coordinates[1]) != 0; + final boolean removealpha = Keyboard.alpha && Integer.parseInt(coordinates[0]) != 0 && Integer.parseInt(coordinates[1]) != 1; + Keyboard.keyRaw(Integer.parseInt(coordinates[0]), Integer.parseInt(coordinates[1]), released); + if (released) { + if (removeshift) + Keyboard.keyRaw(0, 0, false); + if (removealpha) + Keyboard.keyRaw(0, 1, false); + } + } else if (Keyboard.alpha && !Keyboard.shift) { + if (button.hasAttribute("keycodea")) { + Keyboard.debugKey(Integer.parseInt(button.getAttribute("keycodea")), released); + } else { + Keyboard.debugKey(Integer.parseInt(button.getAttribute("keycode")), released); + } + } else if (!Keyboard.alpha && Keyboard.shift) { + if (button.hasAttribute("keycodes")) { + Keyboard.debugKey(Integer.parseInt(button.getAttribute("keycodes")), released); + } else { + Keyboard.debugKey(Integer.parseInt(button.getAttribute("keycode")), released); + } + } else if (Keyboard.alpha && Keyboard.shift) { + if (button.hasAttribute("keycodesa")) { + Keyboard.debugKey(Integer.parseInt(button.getAttribute("keycodesa")), released); + } else if (button.hasAttribute("keycodes")) { + Keyboard.debugKey(Integer.parseInt(button.getAttribute("keycodes")), released); + } else { + Keyboard.debugKey(Integer.parseInt(button.getAttribute("keycode")), released); + } + } else { + Keyboard.debugKey(Integer.parseInt(button.getAttribute("keycode")), released); + } + } catch (final Exception ex) { + ex.printStackTrace(); + } + }).start(); + } + @JSBody(params = {}, script = "return CSS.supports(\"zoom:2\")") private static native boolean suppportsZoom(); From cd6768d6083f79fd240a41deef9d25e4c51fc169 Mon Sep 17 00:00:00 2001 From: Andrea Cavalli Date: Tue, 16 Oct 2018 21:42:36 +0200 Subject: [PATCH 16/18] Extra --- .../warppi/extra/tetris/ButtonInfo.java | 40 +++++ .../warppi/extra/tetris/MarioBlock.java | 33 ----- .../warppi/extra/tetris/MarioEnemy.java | 9 -- .../warppi/extra/tetris/MarioEntity.java | 85 ----------- .../warppi/extra/tetris/MarioEvent.java | 5 - .../warppi/extra/tetris/MarioWorld.java | 69 --------- .../warppi/extra/tetris/PlayerEntity.java | 138 ------------------ .../warppi/extra/tetris/PositionEvent.java | 5 - .../warppi/extra/tetris/TetrisGame.java | 67 +++++++-- .../warppi/extra/tetris/TetrisScreen.java | 37 +++-- 10 files changed, 113 insertions(+), 375 deletions(-) create mode 100644 core/src/main/java/it/cavallium/warppi/extra/tetris/ButtonInfo.java delete mode 100644 core/src/main/java/it/cavallium/warppi/extra/tetris/MarioBlock.java delete mode 100644 core/src/main/java/it/cavallium/warppi/extra/tetris/MarioEnemy.java delete mode 100644 core/src/main/java/it/cavallium/warppi/extra/tetris/MarioEntity.java delete mode 100644 core/src/main/java/it/cavallium/warppi/extra/tetris/MarioEvent.java delete mode 100644 core/src/main/java/it/cavallium/warppi/extra/tetris/MarioWorld.java delete mode 100644 core/src/main/java/it/cavallium/warppi/extra/tetris/PlayerEntity.java delete mode 100644 core/src/main/java/it/cavallium/warppi/extra/tetris/PositionEvent.java diff --git a/core/src/main/java/it/cavallium/warppi/extra/tetris/ButtonInfo.java b/core/src/main/java/it/cavallium/warppi/extra/tetris/ButtonInfo.java new file mode 100644 index 00000000..680c71e3 --- /dev/null +++ b/core/src/main/java/it/cavallium/warppi/extra/tetris/ButtonInfo.java @@ -0,0 +1,40 @@ +package it.cavallium.warppi.extra.tetris; + +public class ButtonInfo { + + public volatile int pressedCount = 0; + public volatile int releasedCount = 0; + public volatile int unreadCount = 0; + + public ButtonInfo() { + + } + + public void press() { + if (pressedCount <= releasedCount) { + System.out.println("press"); + pressedCount = releasedCount + 1; + unreadCount++; + } + } + + public void release() { + releasedCount++; + pressedCount = releasedCount; + System.out.println("release" + releasedCount); + } + + public int readPressed() { + int val = unreadCount; + unreadCount = 0; + return val; + } + + public boolean hasUnreadData() { + return unreadCount > 0; + } + + public boolean isPressedNow() { + return pressedCount > releasedCount; + } +} diff --git a/core/src/main/java/it/cavallium/warppi/extra/tetris/MarioBlock.java b/core/src/main/java/it/cavallium/warppi/extra/tetris/MarioBlock.java deleted file mode 100644 index d463e236..00000000 --- a/core/src/main/java/it/cavallium/warppi/extra/tetris/MarioBlock.java +++ /dev/null @@ -1,33 +0,0 @@ -package it.cavallium.warppi.extra.tetris; - -public class MarioBlock { - private final int x; - private final int y; - private final byte id; - - public MarioBlock(final int x, final int y, final byte b) { - this.x = x; - this.y = y; - id = b; - } - - public boolean isSolid() { - return MarioBlock.isSolid(id); - } - - public byte getID() { - return id; - } - - public int getX() { - return x; - } - - public int getY() { - return y; - } - - public static boolean isSolid(final byte id) { - return id != 0b0; - } -} diff --git a/core/src/main/java/it/cavallium/warppi/extra/tetris/MarioEnemy.java b/core/src/main/java/it/cavallium/warppi/extra/tetris/MarioEnemy.java deleted file mode 100644 index e6b5c120..00000000 --- a/core/src/main/java/it/cavallium/warppi/extra/tetris/MarioEnemy.java +++ /dev/null @@ -1,9 +0,0 @@ -package it.cavallium.warppi.extra.tetris; - -public class MarioEnemy extends MarioEntity { - - public MarioEnemy(final double x, final double y, final double forceX, final double forceY, final boolean onGround, final boolean subjectToGravity) { - super(x, y, forceX, forceY, onGround, subjectToGravity); - } - -} diff --git a/core/src/main/java/it/cavallium/warppi/extra/tetris/MarioEntity.java b/core/src/main/java/it/cavallium/warppi/extra/tetris/MarioEntity.java deleted file mode 100644 index c563449f..00000000 --- a/core/src/main/java/it/cavallium/warppi/extra/tetris/MarioEntity.java +++ /dev/null @@ -1,85 +0,0 @@ -package it.cavallium.warppi.extra.tetris; - -public class MarioEntity { - protected double x; - protected double y; - public double forceX; - public double forceY; - public boolean collisionUp; - public boolean collisionDown; - public boolean collisionLeft; - public boolean collisionRight; - public boolean subjectToGravity; - - public MarioEntity(final double x, final double y, final double forceX, final double forceY, final boolean onGround, final boolean subjectToGravity) { - this.x = x; - this.y = y; - this.forceX = forceX; - this.forceY = forceY; - collisionDown = onGround; - this.subjectToGravity = subjectToGravity; - } - - public void setPosition(final double x, final double y) { - this.x = x; - this.y = y; - } - - public void setPosition(final double x, final double y, final boolean onGround) { - this.x = x; - this.y = y; - collisionDown = onGround; - } - - public double getX() { - return x; - } - - public double getY() { - return y; - } - - public boolean isOnGround() { - return collisionDown; - } - - public void setOnGround(final boolean onGround) { - collisionDown = onGround; - } - - public void gameTick(final double dt) { - x = computeFutureDX(dt); - y = computeFutureDY(dt); - forceX = computeFutureForceDX(dt); - forceY = computeFutureForceDY(dt); - } - - public double computeFutureDX(final double dt) { - return x + dt * forceX - x; - } - - public double computeFutureDY(final double dt) { - final double forceY = this.forceY; - double y = this.y; - if (!collisionDown) { - y += dt * forceY; - } - return y - this.y; - } - - public double computeFutureForceDX(final double dt) { - double forceX = this.forceX; - forceX *= 0.75; - return forceX - this.forceX; - } - - public double computeFutureForceDY(final double dt) { - double forceY = this.forceY; - if (subjectToGravity && !collisionDown) { - forceY -= dt * 1569.6 / 16f; - } else { - forceY *= 0.75; - } - return forceY - this.forceY; - } -} diff --git a/core/src/main/java/it/cavallium/warppi/extra/tetris/MarioEvent.java b/core/src/main/java/it/cavallium/warppi/extra/tetris/MarioEvent.java deleted file mode 100644 index 9a2023c5..00000000 --- a/core/src/main/java/it/cavallium/warppi/extra/tetris/MarioEvent.java +++ /dev/null @@ -1,5 +0,0 @@ -package it.cavallium.warppi.extra.tetris; - -public class MarioEvent { - -} diff --git a/core/src/main/java/it/cavallium/warppi/extra/tetris/MarioWorld.java b/core/src/main/java/it/cavallium/warppi/extra/tetris/MarioWorld.java deleted file mode 100644 index 5166e236..00000000 --- a/core/src/main/java/it/cavallium/warppi/extra/tetris/MarioWorld.java +++ /dev/null @@ -1,69 +0,0 @@ -package it.cavallium.warppi.extra.tetris; - -public class MarioWorld { - - private final int[] spawnPoint; - private final int width; - private final int height; - private final byte[][] data; - @SuppressWarnings("unused") - private final MarioEvent[] events; - private final MarioEntity[] entities; - - /** - * @param width - * @param height - * @param data - * @param events - * @param marioEnemies - */ - public MarioWorld(final int[] spawnPoint, final int width, final int height, final byte[][] data, final MarioEvent[] events, final MarioEntity[] entities) { - this.spawnPoint = spawnPoint; - this.width = width; - this.height = height; - this.data = data; - this.events = events; - this.entities = entities; - } - - public byte getBlockIdAt(final int x, final int y) { - final int idy = height - 1 - y; - if (idy < 0 || idy >= data.length) { - return 0b0; - } - final int idx = x; - if (idx < 0 || idx >= data[0].length) { - return 0b0; - } - return data[idy][idx]; - } - - public MarioBlock getBlockAt(final int x, final int y) { - return new MarioBlock(x, y, getBlockIdAt(x, y)); - } - - public int getWidth() { - return width; - } - - public int getHeight() { - return height; - } - - public void reset() { - - } - - public double getSpawnPointX() { - return spawnPoint[0]; - } - - public double getSpawnPointY() { - return spawnPoint[1]; - } - - public MarioEntity[] getEntities() { - return entities; - } - -} diff --git a/core/src/main/java/it/cavallium/warppi/extra/tetris/PlayerEntity.java b/core/src/main/java/it/cavallium/warppi/extra/tetris/PlayerEntity.java deleted file mode 100644 index e74655b1..00000000 --- a/core/src/main/java/it/cavallium/warppi/extra/tetris/PlayerEntity.java +++ /dev/null @@ -1,138 +0,0 @@ -package it.cavallium.warppi.extra.tetris; - -public class PlayerEntity extends MarioEntity { - - @SuppressWarnings("unused") - private final int life; - public float walkAnimation = 0; - public float jumptime = 0; - public boolean walking = false; - public boolean running = false; - public boolean jumping = false; - public boolean flipped = false; - public int[] marioSkinPos = new int[] { 0, 0 }; - private double controllerDX; - private double controllerDY; - - public PlayerEntity(final double x, final double y, final int life) { - super(x, y, 0, 0, true, true); - this.life = life; - } - - @Override - public void gameTick(final double dt) { - walkAnimation += dt; - x += computeFutureDX(dt); - y += computeFutureDY(dt); - forceX += computeFutureForceDX(dt); - forceY += computeFutureForceDY(dt); - if (controllerDX == 0) { - walking = false; - walkAnimation = 0; - } else { - if (controllerDX > 0) { //RIGHT - walking = true; - flipped = false; - } - if (controllerDX < 0) { //LEFT - walking = true; - flipped = true; - } - } - if (controllerDY > 0) { //JUMP - if (collisionUp) { - jumptime = Float.MAX_VALUE; - jumping = false; - } - jumptime += dt; - if (jumptime <= 0.5f && !jumping && collisionDown) { - jumping = true; - collisionDown = false; - } else if (jumptime <= 0.5f) {} else { - jumping = false; - } - } else { - jumping = false; - if (collisionDown) { - jumptime = 0; - } else { - jumptime = Float.MAX_VALUE; - } - } - if (!walking & !running & !jumping) { - marioSkinPos[0] = 0; - marioSkinPos[1] = 0; - } else if (collisionDown & walking & !running & !jumping && walkAnimation >= 0.08) { - while (walkAnimation > 0.08) { - walkAnimation -= 0.08; - if (marioSkinPos[0] == 1 & marioSkinPos[1] == 0) { - marioSkinPos[0] += 2; - } else if (marioSkinPos[0] == 3 & marioSkinPos[1] == 0) { - marioSkinPos[0] -= 1; - } else if (marioSkinPos[0] == 2 & marioSkinPos[1] == 0) { - marioSkinPos[0] -= 1; - } else { - marioSkinPos[0] = 1; - marioSkinPos[1] = 0; - } - } - } else if (jumping) { - marioSkinPos[0] = 5; - marioSkinPos[1] = 1; - } - } - - @Override - public double computeFutureDX(final double dt) { - return super.computeFutureDX(dt); - } - - public double computeFuturedDY(final double dt) { - return super.computeFutureDY(dt); - } - - @Override - public double computeFutureForceDX(final double dt) { - double forceX = this.forceX; - if (controllerDX == 0) {} else { - if (controllerDX > 0) { - if (forceX < 500f / 16f) { - forceX += dt * 500f / 16f; - } - } - if (controllerDX < 0) { - if (forceX > -500f / 16f) { - forceX -= dt * 500f / 16f; - } - } - } - - return forceX + super.computeFutureForceDX(dt) - this.forceX; - } - - @Override - public double computeFutureForceDY(final double dt) { - float jumptime = this.jumptime; - double forceY = this.forceY; - if (controllerDY > 0) { //JUMP - if (collisionUp) { - jumptime = Float.MAX_VALUE; - } - jumptime += dt; - if (jumptime <= 0.5f && !jumping && collisionDown) { - forceY = dt * (4 * 1569.6f) / 16f; - } else if (jumptime <= 0.5f) { - forceY = dt * (4 * 1569.6f) / 16f; - } - } - return forceY + super.computeFutureForceDY(dt) - this.forceY; - } - - public void move(final float dt, final double dX, final double dY) { - walkAnimation += dt; - - controllerDX = dX; - controllerDY = dY; - } - -} diff --git a/core/src/main/java/it/cavallium/warppi/extra/tetris/PositionEvent.java b/core/src/main/java/it/cavallium/warppi/extra/tetris/PositionEvent.java deleted file mode 100644 index ad8ebd03..00000000 --- a/core/src/main/java/it/cavallium/warppi/extra/tetris/PositionEvent.java +++ /dev/null @@ -1,5 +0,0 @@ -package it.cavallium.warppi.extra.tetris; - -public class PositionEvent extends MarioEvent { - -} diff --git a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrisGame.java b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrisGame.java index 319bfc90..e9d93f35 100644 --- a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrisGame.java +++ b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrisGame.java @@ -5,14 +5,14 @@ import java.util.Arrays; public class TetrisGame { public static final int WIDTH = 10, HEIGHT = 22; - public static final double TICK_TIME = 0.25; + public static final double TICK_TIME = 0.25, DOWN_TIME = 0.10, MOVE_TIMER = 0.125; + private double tickTimer, downTimer, leftTimer, rightTimer, upTimer; private BlockColor[] grid; private BlockColor[] hovergrid; private volatile BlockColor[] renderedGrid; private GameStatus gameStatus; private int score; private double currentTime; - private double tickTimer; private Tetromino currentTetromino; private Tetromino nextTetromino; @@ -39,21 +39,64 @@ public class TetrisGame { nextTetromino.fixInitialPosition(); } - public void update(float dt, boolean leftPressed, boolean rightPressed, boolean downPressed, boolean upPressed, boolean okPressed, boolean backPressed) { + public void update(float dt, ButtonInfo leftPressed, ButtonInfo rightPressed, ButtonInfo downPressed, ButtonInfo upPressed, ButtonInfo okPressed, ButtonInfo backPressed) { currentTime += dt; tickTimer += dt; - if (!(leftPressed && rightPressed)) { - if (leftPressed) { + leftTimer += dt; + rightTimer += dt; + downTimer += dt; + upTimer += dt; + if (leftPressed.hasUnreadData()) { + for (int i = leftPressed.readPressed(); i > 0; i--) { move(this.currentTetromino, -1, 0, 0); - } else if (rightPressed) { - move(this.currentTetromino, 1, 0, 0); } + leftTimer = -MOVE_TIMER; + } else if (leftPressed.isPressedNow()) { + while (leftTimer >= MOVE_TIMER) { + leftTimer -= MOVE_TIMER; + move(this.currentTetromino, -1, 0, 0); + } + } else { + leftTimer = 0; } - if (downPressed) { - move(this.currentTetromino, 0, 1, 0); + if (rightPressed.isPressedNow()) { + if (rightPressed.hasUnreadData()) { + for (int i = rightPressed.readPressed(); i > 0; i--) { + move(this.currentTetromino, 1, 0, 0); + } + rightTimer = -MOVE_TIMER; + } else { + while (rightTimer >= MOVE_TIMER) { + rightTimer -= MOVE_TIMER; + move(this.currentTetromino, 1, 0, 0); + } + } + } else { + rightTimer = 0; } - if (upPressed) { - move(this.currentTetromino, 0, 0, 1); + if (upPressed.isPressedNow()) { + if (upPressed.hasUnreadData()) { + for (int i = upPressed.readPressed(); i > 0; i--) { + move(this.currentTetromino, 0, 0, 1); + } + upTimer = -MOVE_TIMER; + } else { + while (upTimer >= MOVE_TIMER) { + upTimer -= MOVE_TIMER; + move(this.currentTetromino, 0, 0, 1); + } + } + } else { + rightTimer = 0; + } + if (downPressed.isPressedNow()) { + downPressed.readPressed(); + while (downTimer >= DOWN_TIME) { + downTimer -= DOWN_TIME; + move(this.currentTetromino, 0, 1, 0); + } + } else { + downTimer = 0; } while (tickTimer >= TICK_TIME) { tickTimer -= TICK_TIME; @@ -67,7 +110,7 @@ public class TetrisGame { renderGrid(); } - public void gameTick(boolean leftPressed, boolean rightPressed, boolean downPressed, boolean okPressed, boolean backPressed) { + public void gameTick(ButtonInfo leftPressed, ButtonInfo rightPressed, ButtonInfo downPressed, ButtonInfo okPressed, ButtonInfo backPressed) { if (move(this.currentTetromino, 0, 1, 0)) { } else { diff --git a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrisScreen.java b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrisScreen.java index 94b140c4..ef54d9fc 100644 --- a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrisScreen.java +++ b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrisScreen.java @@ -18,17 +18,17 @@ public class TetrisScreen extends Screen { private TetrisGame g; - private boolean leftPressed; + private ButtonInfo leftPressed = new ButtonInfo(); - private boolean rightPressed; + private ButtonInfo rightPressed = new ButtonInfo(); - private boolean upPressed; + private ButtonInfo upPressed = new ButtonInfo(); - private boolean downPressed; + private ButtonInfo downPressed = new ButtonInfo(); - private boolean okPressed; + private ButtonInfo okPressed = new ButtonInfo(); - private boolean backPressed; + private ButtonInfo backPressed = new ButtonInfo(); private GraphicEngine e; @@ -64,7 +64,6 @@ public class TetrisScreen extends Screen { public void beforeRender(final float dt) { Engine.INSTANCE.getHardwareDevice().getDisplayManager().renderer.glClearColor(0xff000000); g.update(dt, leftPressed, rightPressed, downPressed, upPressed, okPressed, backPressed); - upPressed = false; } @Override @@ -119,28 +118,28 @@ public class TetrisScreen extends Screen { public boolean onKeyPressed(KeyPressedEvent k) { switch (k.getKey()) { case LEFT: { - leftPressed = true; + leftPressed.press(); return true; } case RIGHT: { - rightPressed = true; + rightPressed.press(); return true; } case UP: { - upPressed = true; + upPressed.press(); return true; } case DOWN: { - downPressed = true; + downPressed.press(); return true; } case OK: { - okPressed = true; + okPressed.press(); g.playAgain(); return true; } case BACK: { - backPressed = true; + backPressed.press(); return true; } default: return false; @@ -151,27 +150,27 @@ public class TetrisScreen extends Screen { public boolean onKeyReleased(KeyReleasedEvent k) { switch (k.getKey()) { case LEFT: { - leftPressed = false; + leftPressed.release(); return true; } case RIGHT: { - rightPressed = false; + rightPressed.release(); return true; } case UP: { - upPressed = false; + upPressed.release(); return true; } case DOWN: { - downPressed = false; + downPressed.release(); return true; } case OK: { - okPressed = false; + okPressed.release(); return true; } case BACK: { - backPressed = false; + backPressed.release(); return true; } default: return false; From 76d9d77e13d901e4b13815fd1c4be205f4c34f61 Mon Sep 17 00:00:00 2001 From: Andrea Cavalli Date: Tue, 16 Oct 2018 22:26:05 +0200 Subject: [PATCH 17/18] Extra --- .../warppi/extra/tetris/ButtonInfo.java | 2 - .../warppi/extra/tetris/TetrisGame.java | 78 +++++++++---------- .../gui/graphicengine/html/HtmlEngine.java | 4 - 3 files changed, 38 insertions(+), 46 deletions(-) diff --git a/core/src/main/java/it/cavallium/warppi/extra/tetris/ButtonInfo.java b/core/src/main/java/it/cavallium/warppi/extra/tetris/ButtonInfo.java index 680c71e3..33ec0ddc 100644 --- a/core/src/main/java/it/cavallium/warppi/extra/tetris/ButtonInfo.java +++ b/core/src/main/java/it/cavallium/warppi/extra/tetris/ButtonInfo.java @@ -12,7 +12,6 @@ public class ButtonInfo { public void press() { if (pressedCount <= releasedCount) { - System.out.println("press"); pressedCount = releasedCount + 1; unreadCount++; } @@ -21,7 +20,6 @@ public class ButtonInfo { public void release() { releasedCount++; pressedCount = releasedCount; - System.out.println("release" + releasedCount); } public int readPressed() { diff --git a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrisGame.java b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrisGame.java index e9d93f35..505d71c2 100644 --- a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrisGame.java +++ b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrisGame.java @@ -15,7 +15,7 @@ public class TetrisGame { private double currentTime; private Tetromino currentTetromino; private Tetromino nextTetromino; - + public TetrisGame() { resetVariables(); } @@ -25,7 +25,7 @@ public class TetrisGame { gameStatus = GameStatus.PLAYING; placeNextTetromino(); } - + private void resetVariables() { grid = new BlockColor[WIDTH * HEIGHT]; hovergrid = new BlockColor[WIDTH * HEIGHT]; @@ -39,7 +39,8 @@ public class TetrisGame { nextTetromino.fixInitialPosition(); } - public void update(float dt, ButtonInfo leftPressed, ButtonInfo rightPressed, ButtonInfo downPressed, ButtonInfo upPressed, ButtonInfo okPressed, ButtonInfo backPressed) { + public void update(float dt, ButtonInfo leftPressed, ButtonInfo rightPressed, ButtonInfo downPressed, + ButtonInfo upPressed, ButtonInfo okPressed, ButtonInfo backPressed) { currentTime += dt; tickTimer += dt; leftTimer += dt; @@ -59,35 +60,31 @@ public class TetrisGame { } else { leftTimer = 0; } - if (rightPressed.isPressedNow()) { - if (rightPressed.hasUnreadData()) { - for (int i = rightPressed.readPressed(); i > 0; i--) { - move(this.currentTetromino, 1, 0, 0); - } - rightTimer = -MOVE_TIMER; - } else { - while (rightTimer >= MOVE_TIMER) { - rightTimer -= MOVE_TIMER; - move(this.currentTetromino, 1, 0, 0); - } + if (rightPressed.hasUnreadData()) { + for (int i = rightPressed.readPressed(); i > 0; i--) { + move(this.currentTetromino, 1, 0, 0); + } + rightTimer = -MOVE_TIMER; + } else if (rightPressed.isPressedNow()) { + while (rightTimer >= MOVE_TIMER) { + rightTimer -= MOVE_TIMER; + move(this.currentTetromino, 1, 0, 0); } } else { rightTimer = 0; } - if (upPressed.isPressedNow()) { - if (upPressed.hasUnreadData()) { - for (int i = upPressed.readPressed(); i > 0; i--) { - move(this.currentTetromino, 0, 0, 1); - } - upTimer = -MOVE_TIMER; - } else { - while (upTimer >= MOVE_TIMER) { - upTimer -= MOVE_TIMER; - move(this.currentTetromino, 0, 0, 1); - } + if (upPressed.hasUnreadData()) { + for (int i = upPressed.readPressed(); i > 0; i--) { + move(this.currentTetromino, 0, 0, 1); + } + upTimer = -MOVE_TIMER; + } else if (upPressed.isPressedNow()) { + while (upTimer >= MOVE_TIMER) { + upTimer -= MOVE_TIMER; + move(this.currentTetromino, 0, 0, 1); } } else { - rightTimer = 0; + upTimer = 0; } if (downPressed.isPressedNow()) { downPressed.readPressed(); @@ -105,14 +102,15 @@ public class TetrisGame { if (gameStatus == GameStatus.INITIAL) { playAgain(); } else { - + } renderGrid(); } - public void gameTick(ButtonInfo leftPressed, ButtonInfo rightPressed, ButtonInfo downPressed, ButtonInfo okPressed, ButtonInfo backPressed) { + public void gameTick(ButtonInfo leftPressed, ButtonInfo rightPressed, ButtonInfo downPressed, ButtonInfo okPressed, + ButtonInfo backPressed) { if (move(this.currentTetromino, 0, 1, 0)) { - + } else { // Spawn new tetromino and write the old to the permanent grid drawCurrentTetromino(grid); @@ -125,9 +123,9 @@ public class TetrisGame { } } } - + private void checkLines() { - for(int i = HEIGHT - 1; i >= 0; i--) { + for (int i = HEIGHT - 1; i >= 0; i--) { boolean scored = true; while (scored) { for (int x = 0; x < WIDTH; x++) { @@ -141,7 +139,7 @@ public class TetrisGame { for (int x = 0; x < WIDTH; x++) { int y = i; while (y > 0) { - this.grid[x + (y) * WIDTH] = this.grid[x + (y-1) * WIDTH]; + this.grid[x + (y) * WIDTH] = this.grid[x + (y - 1) * WIDTH]; y--; } } @@ -154,9 +152,9 @@ public class TetrisGame { byte rot = (byte) ((t.getRotation() + dRotation) % 4); boolean[] block = t.getRenderedBlock(rot); int blockSize = t.getTetrominoGridSize(); - int half1 = (int) Math.floor(((double)t.getTetrominoGridSize())/2d); + int half1 = (int) Math.floor(((double) t.getTetrominoGridSize()) / 2d); int half2 = blockSize - half1; - byte aX = (byte)(t.getX()+dX), aY = (byte)(t.getY()+dY); + byte aX = (byte) (t.getX() + dX), aY = (byte) (t.getY() + dY); int blockX = 0, blockY = 0; for (int x = aX - half1; x < aX + half2; x++) { for (int y = aY - half1; y < aY + half2; y++) { @@ -170,7 +168,7 @@ public class TetrisGame { } } blockY++; - } + } blockY = 0; blockX++; } @@ -185,21 +183,21 @@ public class TetrisGame { drawCurrentTetromino(this.renderedGrid); for (int y = 0; y < HEIGHT; y++) { for (int x = 0; x < WIDTH; x++) { - final int offset = x+y*WIDTH; + final int offset = x + y * WIDTH; renderedGrid[offset] = hovergrid[offset] != null ? hovergrid[offset] : renderedGrid[offset]; } } } - + private void placeNextTetromino() { currentTetromino = nextTetromino; nextTetromino = generateRandomTetromino(); nextTetromino.fixInitialPosition(); } - + private Tetromino generateRandomTetromino() { int s = (int) (Math.random() * 7); - final byte middleX = (byte)((WIDTH - 1)/2), middleY = 0, rotation = (byte) (Math.random() * 4); + final byte middleX = (byte) ((WIDTH - 1) / 2), middleY = 0, rotation = (byte) (Math.random() * 4); switch (s) { case 0: return new TetrominoICyan(middleX, middleY, rotation); @@ -217,7 +215,7 @@ public class TetrisGame { return new TetrominoZRed(middleX, middleY, rotation); } } - + public Tetromino getNextTetromino() { return this.nextTetromino; } diff --git a/teavm/src/main/java/it/cavallium/warppi/gui/graphicengine/html/HtmlEngine.java b/teavm/src/main/java/it/cavallium/warppi/gui/graphicengine/html/HtmlEngine.java index ffe0592a..28e4bc6f 100644 --- a/teavm/src/main/java/it/cavallium/warppi/gui/graphicengine/html/HtmlEngine.java +++ b/teavm/src/main/java/it/cavallium/warppi/gui/graphicengine/html/HtmlEngine.java @@ -124,16 +124,12 @@ public class HtmlEngine implements GraphicEngine { evt.preventDefault(); new Thread(() -> { Keyboard.debugKey(keyNames .getOrDefault(evt.getKey(), evt.getKeyCode()), false); - System.out.println(evt.getKeyCode()); - System.out.println("" + (int) evt.getKey().charAt(0)); }).start(); }); HtmlEngine.document.addEventListener("keyup", (final KeyboardEvent evt) -> { evt.preventDefault(); new Thread(() -> { Keyboard.debugKey(keyNames .getOrDefault(evt.getKey(), evt.getKeyCode()), true); - System.out.println(evt.getKeyCode()); - System.out.println("" + (int) evt.getKey().charAt(0)); }).start(); }); keyInput.addEventListener("input", (final Event evt) -> { From 87151ed60672de3ada958af22035296db70a7ce8 Mon Sep 17 00:00:00 2001 From: Cavallium Date: Tue, 23 Oct 2018 17:28:05 +0200 Subject: [PATCH 18/18] Fixed graphic glitches when changing screen --- Flow | 2 +- .../warppi/extra/mario/MarioScreen.java | 37 + .../warppi/extra/tetris/TetrisScreen.java | 6 +- .../cavallium/warppi/gui/CalculatorHUD.java | 6 + .../cavallium/warppi/gui/DisplayManager.java | 12 +- .../warppi/gui/GraphicalInterface.java | 4 +- .../java/it/cavallium/warppi/gui/HUD.java | 11 + .../warppi/gui/graphicengine/Skin.java | 8 + .../screens/ChooseVariableValueScreen.java | 3 + .../warppi/gui/screens/EmptyScreen.java | 3 + .../gui/screens/KeyboardDebugScreen.java | 3 + .../warppi/gui/screens/LoadingScreen.java | 3 + .../warppi/gui/screens/MathInputScreen.java | 5 + .../cavallium/warppi/gui/screens/Screen.java | 29 + .../warppi/gui/screens/SolveForXScreen.java | 3 + .../graphicengine/impl/swing/SwingWindow.java | 1 + .../graphicengine/impl/jogl/JOGLRenderer.java | 2 +- .../graphicengine/impl/jogl/NEWTWindow.java | 1 - .../ar/com/hjg/pngj/BufferedStreamFeeder.java | 208 ------ .../java/ar/com/hjg/pngj/ChunkReader.java | 231 ------- .../ar/com/hjg/pngj/ChunkSeqBuffering.java | 30 - .../java/ar/com/hjg/pngj/ChunkSeqReader.java | 432 ------------ .../ar/com/hjg/pngj/ChunkSeqReaderPng.java | 310 --------- .../ar/com/hjg/pngj/ChunkSeqSkipping.java | 76 -- .../ar/com/hjg/pngj/DeflatedChunkReader.java | 78 --- .../ar/com/hjg/pngj/DeflatedChunksSet.java | 422 ------------ .../java/ar/com/hjg/pngj/Deinterlacer.java | 197 ------ .../main/java/ar/com/hjg/pngj/FilterType.java | 125 ---- .../java/ar/com/hjg/pngj/IBytesConsumer.java | 16 - .../java/ar/com/hjg/pngj/IChunkFactory.java | 23 - .../java/ar/com/hjg/pngj/IDatChunkWriter.java | 132 ---- .../main/java/ar/com/hjg/pngj/IImageLine.java | 50 -- .../java/ar/com/hjg/pngj/IImageLineArray.java | 26 - .../ar/com/hjg/pngj/IImageLineFactory.java | 8 - .../java/ar/com/hjg/pngj/IImageLineSet.java | 64 -- .../ar/com/hjg/pngj/IImageLineSetFactory.java | 32 - .../ar/com/hjg/pngj/IPngWriterFactory.java | 7 - .../main/java/ar/com/hjg/pngj/IdatSet.java | 246 ------- .../main/java/ar/com/hjg/pngj/ImageInfo.java | 265 ------- .../java/ar/com/hjg/pngj/ImageLineByte.java | 191 ------ .../java/ar/com/hjg/pngj/ImageLineHelper.java | 469 ------------- .../java/ar/com/hjg/pngj/ImageLineInt.java | 203 ------ .../ar/com/hjg/pngj/ImageLineSetDefault.java | 159 ----- .../ar/com/hjg/pngj/PngHelperInternal.java | 332 --------- .../ar/com/hjg/pngj/PngHelperInternal2.java | 35 - .../main/java/ar/com/hjg/pngj/PngReader.java | 647 ------------------ .../java/ar/com/hjg/pngj/PngReaderApng.java | 211 ------ .../java/ar/com/hjg/pngj/PngReaderByte.java | 32 - .../java/ar/com/hjg/pngj/PngReaderFilter.java | 101 --- .../java/ar/com/hjg/pngj/PngReaderInt.java | 39 -- .../main/java/ar/com/hjg/pngj/PngWriter.java | 453 ------------ .../java/ar/com/hjg/pngj/PngWriterHc.java | 35 - .../ar/com/hjg/pngj/PngjBadCrcException.java | 20 - .../java/ar/com/hjg/pngj/PngjException.java | 20 - .../com/hjg/pngj/PngjExceptionInternal.java | 24 - .../ar/com/hjg/pngj/PngjInputException.java | 20 - .../ar/com/hjg/pngj/PngjOutputException.java | 20 - .../hjg/pngj/PngjUnsupportedException.java | 25 - .../main/java/ar/com/hjg/pngj/RowInfo.java | 55 -- .../hjg/pngj/chunks/ChunkCopyBehaviour.java | 97 --- .../ar/com/hjg/pngj/chunks/ChunkFactory.java | 112 --- .../ar/com/hjg/pngj/chunks/ChunkHelper.java | 291 -------- .../hjg/pngj/chunks/ChunkLoadBehaviour.java | 27 - .../com/hjg/pngj/chunks/ChunkPredicate.java | 14 - .../java/ar/com/hjg/pngj/chunks/ChunkRaw.java | 175 ----- .../ar/com/hjg/pngj/chunks/ChunksList.java | 160 ----- .../hjg/pngj/chunks/ChunksListForWrite.java | 185 ----- .../pngj/chunks/PngBadCharsetException.java | 20 - .../java/ar/com/hjg/pngj/chunks/PngChunk.java | 227 ------ .../ar/com/hjg/pngj/chunks/PngChunkACTL.java | 57 -- .../ar/com/hjg/pngj/chunks/PngChunkBKGD.java | 112 --- .../ar/com/hjg/pngj/chunks/PngChunkCHRM.java | 75 -- .../ar/com/hjg/pngj/chunks/PngChunkFCTL.java | 158 ----- .../ar/com/hjg/pngj/chunks/PngChunkFDAT.java | 72 -- .../ar/com/hjg/pngj/chunks/PngChunkGAMA.java | 51 -- .../ar/com/hjg/pngj/chunks/PngChunkHIST.java | 56 -- .../ar/com/hjg/pngj/chunks/PngChunkICCP.java | 76 -- .../ar/com/hjg/pngj/chunks/PngChunkIDAT.java | 35 - .../ar/com/hjg/pngj/chunks/PngChunkIEND.java | 35 - .../ar/com/hjg/pngj/chunks/PngChunkIHDR.java | 184 ----- .../ar/com/hjg/pngj/chunks/PngChunkITXT.java | 108 --- .../com/hjg/pngj/chunks/PngChunkMultiple.java | 28 - .../ar/com/hjg/pngj/chunks/PngChunkOFFS.java | 81 --- .../ar/com/hjg/pngj/chunks/PngChunkPHYS.java | 107 --- .../ar/com/hjg/pngj/chunks/PngChunkPLTE.java | 95 --- .../ar/com/hjg/pngj/chunks/PngChunkSBIT.java | 114 --- .../ar/com/hjg/pngj/chunks/PngChunkSPLT.java | 129 ---- .../ar/com/hjg/pngj/chunks/PngChunkSRGB.java | 55 -- .../ar/com/hjg/pngj/chunks/PngChunkSTER.java | 54 -- .../com/hjg/pngj/chunks/PngChunkSingle.java | 45 -- .../ar/com/hjg/pngj/chunks/PngChunkTEXT.java | 44 -- .../ar/com/hjg/pngj/chunks/PngChunkTIME.java | 83 --- .../ar/com/hjg/pngj/chunks/PngChunkTRNS.java | 150 ---- .../com/hjg/pngj/chunks/PngChunkTextVar.java | 60 -- .../com/hjg/pngj/chunks/PngChunkUNKNOWN.java | 40 -- .../ar/com/hjg/pngj/chunks/PngChunkZTXT.java | 61 -- .../ar/com/hjg/pngj/chunks/PngMetadata.java | 235 ------- .../java/ar/com/hjg/pngj/chunks/package.html | 9 - .../main/java/ar/com/hjg/pngj/package.html | 49 -- .../com/hjg/pngj/pixels/CompressorStream.java | 168 ----- .../pngj/pixels/CompressorStreamDeflater.java | 105 --- .../hjg/pngj/pixels/CompressorStreamLz4.java | 94 --- .../hjg/pngj/pixels/DeflaterEstimatorHjg.java | 248 ------- .../hjg/pngj/pixels/DeflaterEstimatorLz4.java | 265 ------- .../hjg/pngj/pixels/FiltersPerformance.java | 208 ------ .../ar/com/hjg/pngj/pixels/PixelsWriter.java | 272 -------- .../hjg/pngj/pixels/PixelsWriterDefault.java | 158 ----- .../hjg/pngj/pixels/PixelsWriterMultiple.java | 230 ------- .../java/ar/com/hjg/pngj/pixels/package.html | 14 - .../gui/graphicengine/html/HtmlEngine.java | 2 +- .../gui/graphicengine/html/HtmlSkin.java | 27 +- .../warppi/teavm/TeaVMImageReader.java | 45 +- .../warppi/teavm/TeaVMSemaphore.java | 4 +- 113 files changed, 154 insertions(+), 11660 deletions(-) delete mode 100644 teavm/src/main/java/ar/com/hjg/pngj/BufferedStreamFeeder.java delete mode 100644 teavm/src/main/java/ar/com/hjg/pngj/ChunkReader.java delete mode 100644 teavm/src/main/java/ar/com/hjg/pngj/ChunkSeqBuffering.java delete mode 100644 teavm/src/main/java/ar/com/hjg/pngj/ChunkSeqReader.java delete mode 100644 teavm/src/main/java/ar/com/hjg/pngj/ChunkSeqReaderPng.java delete mode 100644 teavm/src/main/java/ar/com/hjg/pngj/ChunkSeqSkipping.java delete mode 100644 teavm/src/main/java/ar/com/hjg/pngj/DeflatedChunkReader.java delete mode 100644 teavm/src/main/java/ar/com/hjg/pngj/DeflatedChunksSet.java delete mode 100644 teavm/src/main/java/ar/com/hjg/pngj/Deinterlacer.java delete mode 100644 teavm/src/main/java/ar/com/hjg/pngj/FilterType.java delete mode 100644 teavm/src/main/java/ar/com/hjg/pngj/IBytesConsumer.java delete mode 100644 teavm/src/main/java/ar/com/hjg/pngj/IChunkFactory.java delete mode 100644 teavm/src/main/java/ar/com/hjg/pngj/IDatChunkWriter.java delete mode 100644 teavm/src/main/java/ar/com/hjg/pngj/IImageLine.java delete mode 100644 teavm/src/main/java/ar/com/hjg/pngj/IImageLineArray.java delete mode 100644 teavm/src/main/java/ar/com/hjg/pngj/IImageLineFactory.java delete mode 100644 teavm/src/main/java/ar/com/hjg/pngj/IImageLineSet.java delete mode 100644 teavm/src/main/java/ar/com/hjg/pngj/IImageLineSetFactory.java delete mode 100644 teavm/src/main/java/ar/com/hjg/pngj/IPngWriterFactory.java delete mode 100644 teavm/src/main/java/ar/com/hjg/pngj/IdatSet.java delete mode 100644 teavm/src/main/java/ar/com/hjg/pngj/ImageInfo.java delete mode 100644 teavm/src/main/java/ar/com/hjg/pngj/ImageLineByte.java delete mode 100644 teavm/src/main/java/ar/com/hjg/pngj/ImageLineHelper.java delete mode 100644 teavm/src/main/java/ar/com/hjg/pngj/ImageLineInt.java delete mode 100644 teavm/src/main/java/ar/com/hjg/pngj/ImageLineSetDefault.java delete mode 100644 teavm/src/main/java/ar/com/hjg/pngj/PngHelperInternal.java delete mode 100644 teavm/src/main/java/ar/com/hjg/pngj/PngHelperInternal2.java delete mode 100644 teavm/src/main/java/ar/com/hjg/pngj/PngReader.java delete mode 100644 teavm/src/main/java/ar/com/hjg/pngj/PngReaderApng.java delete mode 100644 teavm/src/main/java/ar/com/hjg/pngj/PngReaderByte.java delete mode 100644 teavm/src/main/java/ar/com/hjg/pngj/PngReaderFilter.java delete mode 100644 teavm/src/main/java/ar/com/hjg/pngj/PngReaderInt.java delete mode 100644 teavm/src/main/java/ar/com/hjg/pngj/PngWriter.java delete mode 100644 teavm/src/main/java/ar/com/hjg/pngj/PngWriterHc.java delete mode 100644 teavm/src/main/java/ar/com/hjg/pngj/PngjBadCrcException.java delete mode 100644 teavm/src/main/java/ar/com/hjg/pngj/PngjException.java delete mode 100644 teavm/src/main/java/ar/com/hjg/pngj/PngjExceptionInternal.java delete mode 100644 teavm/src/main/java/ar/com/hjg/pngj/PngjInputException.java delete mode 100644 teavm/src/main/java/ar/com/hjg/pngj/PngjOutputException.java delete mode 100644 teavm/src/main/java/ar/com/hjg/pngj/PngjUnsupportedException.java delete mode 100644 teavm/src/main/java/ar/com/hjg/pngj/RowInfo.java delete mode 100644 teavm/src/main/java/ar/com/hjg/pngj/chunks/ChunkCopyBehaviour.java delete mode 100644 teavm/src/main/java/ar/com/hjg/pngj/chunks/ChunkFactory.java delete mode 100644 teavm/src/main/java/ar/com/hjg/pngj/chunks/ChunkHelper.java delete mode 100644 teavm/src/main/java/ar/com/hjg/pngj/chunks/ChunkLoadBehaviour.java delete mode 100644 teavm/src/main/java/ar/com/hjg/pngj/chunks/ChunkPredicate.java delete mode 100644 teavm/src/main/java/ar/com/hjg/pngj/chunks/ChunkRaw.java delete mode 100644 teavm/src/main/java/ar/com/hjg/pngj/chunks/ChunksList.java delete mode 100644 teavm/src/main/java/ar/com/hjg/pngj/chunks/ChunksListForWrite.java delete mode 100644 teavm/src/main/java/ar/com/hjg/pngj/chunks/PngBadCharsetException.java delete mode 100644 teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunk.java delete mode 100644 teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkACTL.java delete mode 100644 teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkBKGD.java delete mode 100644 teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkCHRM.java delete mode 100644 teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkFCTL.java delete mode 100644 teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkFDAT.java delete mode 100644 teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkGAMA.java delete mode 100644 teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkHIST.java delete mode 100644 teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkICCP.java delete mode 100644 teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkIDAT.java delete mode 100644 teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkIEND.java delete mode 100644 teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkIHDR.java delete mode 100644 teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkITXT.java delete mode 100644 teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkMultiple.java delete mode 100644 teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkOFFS.java delete mode 100644 teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkPHYS.java delete mode 100644 teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkPLTE.java delete mode 100644 teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkSBIT.java delete mode 100644 teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkSPLT.java delete mode 100644 teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkSRGB.java delete mode 100644 teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkSTER.java delete mode 100644 teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkSingle.java delete mode 100644 teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkTEXT.java delete mode 100644 teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkTIME.java delete mode 100644 teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkTRNS.java delete mode 100644 teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkTextVar.java delete mode 100644 teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkUNKNOWN.java delete mode 100644 teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkZTXT.java delete mode 100644 teavm/src/main/java/ar/com/hjg/pngj/chunks/PngMetadata.java delete mode 100644 teavm/src/main/java/ar/com/hjg/pngj/chunks/package.html delete mode 100644 teavm/src/main/java/ar/com/hjg/pngj/package.html delete mode 100644 teavm/src/main/java/ar/com/hjg/pngj/pixels/CompressorStream.java delete mode 100644 teavm/src/main/java/ar/com/hjg/pngj/pixels/CompressorStreamDeflater.java delete mode 100644 teavm/src/main/java/ar/com/hjg/pngj/pixels/CompressorStreamLz4.java delete mode 100644 teavm/src/main/java/ar/com/hjg/pngj/pixels/DeflaterEstimatorHjg.java delete mode 100644 teavm/src/main/java/ar/com/hjg/pngj/pixels/DeflaterEstimatorLz4.java delete mode 100644 teavm/src/main/java/ar/com/hjg/pngj/pixels/FiltersPerformance.java delete mode 100644 teavm/src/main/java/ar/com/hjg/pngj/pixels/PixelsWriter.java delete mode 100644 teavm/src/main/java/ar/com/hjg/pngj/pixels/PixelsWriterDefault.java delete mode 100644 teavm/src/main/java/ar/com/hjg/pngj/pixels/PixelsWriterMultiple.java delete mode 100644 teavm/src/main/java/ar/com/hjg/pngj/pixels/package.html diff --git a/Flow b/Flow index fc24680f..59b89e9d 160000 --- a/Flow +++ b/Flow @@ -1 +1 @@ -Subproject commit fc24680f8f2178fa550174c75d7dbacb6065358c +Subproject commit 59b89e9d165c3fc615ca44aa742b531caf13fcb6 diff --git a/core/src/main/java/it/cavallium/warppi/extra/mario/MarioScreen.java b/core/src/main/java/it/cavallium/warppi/extra/mario/MarioScreen.java index 8b5e00c2..a7a310f1 100644 --- a/core/src/main/java/it/cavallium/warppi/extra/mario/MarioScreen.java +++ b/core/src/main/java/it/cavallium/warppi/extra/mario/MarioScreen.java @@ -44,6 +44,43 @@ public class MarioScreen extends Screen { historyBehavior = HistoryBehavior.ALWAYS_KEEP_IN_HISTORY; } + @Override + public void graphicInitialized() { + try { + if (MarioScreen.skin == null) { + MarioScreen.skin = Engine.INSTANCE.getHardwareDevice().getDisplayManager().engine.loadSkin("/marioskin.png"); + } + if (MarioScreen.groundskin == null) { + MarioScreen.groundskin = Engine.INSTANCE.getHardwareDevice().getDisplayManager().engine.loadSkin("/marioground.png"); + } + if (MarioScreen.gpuTest2 == null) { + try { + MarioScreen.gpuTest2 = Engine.INSTANCE.getHardwareDevice().getDisplayManager().engine.loadFont("N:\\gputest\\gputest2"); + } catch (final Exception ex) {} + } + if (MarioScreen.gpuTest1 == null) { + try { + MarioScreen.gpuTest1 = Engine.INSTANCE.getHardwareDevice().getDisplayManager().engine.loadFont("N:\\gputest\\gputest12"); + MarioScreen.gpuTest12 = true; + } catch (final Exception ex) { + MarioScreen.gpuTest12 = false; + try { + MarioScreen.gpuTest1 = Engine.INSTANCE.getHardwareDevice().getDisplayManager().engine.loadFont("N:\\gputest\\gputest1"); + } catch (final Exception ex2) {} + } + } + if (MarioScreen.gpuTest3 == null) { + try { + MarioScreen.gpuTest3 = Engine.INSTANCE.getHardwareDevice().getDisplayManager().engine.loadSkin("N:\\gputest\\font_gputest3.png"); + } catch (final Exception ex) { + ex.printStackTrace(); + } + } + } catch (final IOException e) { + e.printStackTrace(); + } + } + @Override public void initialized() { try { diff --git a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrisScreen.java b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrisScreen.java index ef54d9fc..64e954d2 100644 --- a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrisScreen.java +++ b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrisScreen.java @@ -43,13 +43,17 @@ public class TetrisScreen extends Screen { @Override public void initialized() { + StaticVars.windowZoom.onNext(2f); + } + + @Override + public void graphicInitialized() { try { e = d.engine; r = d.renderer; if (TetrisScreen.skin == null) { TetrisScreen.skin = Engine.INSTANCE.getHardwareDevice().getDisplayManager().engine.loadSkin("/tetrisskin.png"); } - StaticVars.windowZoom.onNext(2f); } catch (final IOException e) { e.printStackTrace(); } diff --git a/core/src/main/java/it/cavallium/warppi/gui/CalculatorHUD.java b/core/src/main/java/it/cavallium/warppi/gui/CalculatorHUD.java index a86fac78..921c32ce 100644 --- a/core/src/main/java/it/cavallium/warppi/gui/CalculatorHUD.java +++ b/core/src/main/java/it/cavallium/warppi/gui/CalculatorHUD.java @@ -24,6 +24,12 @@ public class CalculatorHUD extends HUD { } + @Override + public void graphicInitialized() throws InterruptedException { + // TODO Auto-generated method stub + + } + @Override public void render() { // TODO Auto-generated method stub diff --git a/core/src/main/java/it/cavallium/warppi/gui/DisplayManager.java b/core/src/main/java/it/cavallium/warppi/gui/DisplayManager.java index 55647aa6..cf913787 100644 --- a/core/src/main/java/it/cavallium/warppi/gui/DisplayManager.java +++ b/core/src/main/java/it/cavallium/warppi/gui/DisplayManager.java @@ -242,10 +242,10 @@ public final class DisplayManager implements RenderingLoop { screen.create(); } this.screen = screen; - screenChange.release(); if (screen.initialized == false) { screen.initialize(); } + screenChange.release(); } catch (final Exception e) { e.printStackTrace(); Engine.getPlatform().exit(0); @@ -267,10 +267,10 @@ public final class DisplayManager implements RenderingLoop { try { screen.create(); this.screen = screen; - screenChange.release(); if (screen.initialized == false) { screen.initialize(); } + screenChange.release(); } catch (final Exception e) { e.printStackTrace(); Engine.getPlatform().exit(0); @@ -372,6 +372,13 @@ public final class DisplayManager implements RenderingLoop { } } } + if (!screen.graphicInitialized) { + try { + screen.initializeGraphic(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } renderer.glClear(engine.getWidth(), engine.getHeight()); } @@ -439,7 +446,6 @@ public final class DisplayManager implements RenderingLoop { setScreen(initialScreen); initialScreen = null; } - screen.initialize(); } catch (final Exception e) { e.printStackTrace(); Engine.getPlatform().exit(0); diff --git a/core/src/main/java/it/cavallium/warppi/gui/GraphicalInterface.java b/core/src/main/java/it/cavallium/warppi/gui/GraphicalInterface.java index 9617df37..f1ee5694 100644 --- a/core/src/main/java/it/cavallium/warppi/gui/GraphicalInterface.java +++ b/core/src/main/java/it/cavallium/warppi/gui/GraphicalInterface.java @@ -2,8 +2,10 @@ package it.cavallium.warppi.gui; public interface GraphicalInterface { void create() throws InterruptedException; - + void initialize() throws InterruptedException; + + void initializeGraphic() throws InterruptedException; void render(); diff --git a/core/src/main/java/it/cavallium/warppi/gui/HUD.java b/core/src/main/java/it/cavallium/warppi/gui/HUD.java index 06e13a26..79436fe9 100644 --- a/core/src/main/java/it/cavallium/warppi/gui/HUD.java +++ b/core/src/main/java/it/cavallium/warppi/gui/HUD.java @@ -3,6 +3,7 @@ package it.cavallium.warppi.gui; public abstract class HUD implements GraphicalInterface { public DisplayManager d; public boolean created = false; + public boolean graphicInitialized = false; public boolean initialized = false; public boolean visible = true; @@ -16,6 +17,14 @@ public abstract class HUD implements GraphicalInterface { } } + @Override + public void initializeGraphic() throws InterruptedException { + if (!graphicInitialized) { + graphicInitialized = true; + graphicInitialized(); + } + } + @Override public void create() throws InterruptedException { if (!created) { @@ -26,6 +35,8 @@ public abstract class HUD implements GraphicalInterface { public abstract void created() throws InterruptedException; + public abstract void graphicInitialized() throws InterruptedException; + public abstract void initialized() throws InterruptedException; public abstract void renderBackground(); diff --git a/core/src/main/java/it/cavallium/warppi/gui/graphicengine/Skin.java b/core/src/main/java/it/cavallium/warppi/gui/graphicengine/Skin.java index 5469dbdd..80c24b71 100644 --- a/core/src/main/java/it/cavallium/warppi/gui/graphicengine/Skin.java +++ b/core/src/main/java/it/cavallium/warppi/gui/graphicengine/Skin.java @@ -13,7 +13,15 @@ public interface Skin { void use(GraphicEngine d); + /** + * May not be available before initialization + * @return skin width + */ int getSkinWidth(); + /** + * May not be available before initialization + * @return skin height + */ int getSkinHeight(); } diff --git a/core/src/main/java/it/cavallium/warppi/gui/screens/ChooseVariableValueScreen.java b/core/src/main/java/it/cavallium/warppi/gui/screens/ChooseVariableValueScreen.java index 0a7b92bc..0c8a7ca7 100644 --- a/core/src/main/java/it/cavallium/warppi/gui/screens/ChooseVariableValueScreen.java +++ b/core/src/main/java/it/cavallium/warppi/gui/screens/ChooseVariableValueScreen.java @@ -27,6 +27,9 @@ public class ChooseVariableValueScreen extends Screen { @Override public void initialized() throws InterruptedException {} + @Override + public void graphicInitialized() throws InterruptedException {} + @Override public void render() { Utils.getFont(false, true).use(Engine.INSTANCE.getHardwareDevice().getDisplayManager().engine); diff --git a/core/src/main/java/it/cavallium/warppi/gui/screens/EmptyScreen.java b/core/src/main/java/it/cavallium/warppi/gui/screens/EmptyScreen.java index 7502376e..e60210b0 100644 --- a/core/src/main/java/it/cavallium/warppi/gui/screens/EmptyScreen.java +++ b/core/src/main/java/it/cavallium/warppi/gui/screens/EmptyScreen.java @@ -19,6 +19,9 @@ public class EmptyScreen extends Screen { @Override public void initialized() throws InterruptedException {} + @Override + public void graphicInitialized() throws InterruptedException {} + @Override public void render() { // TODO Auto-generated method stub diff --git a/core/src/main/java/it/cavallium/warppi/gui/screens/KeyboardDebugScreen.java b/core/src/main/java/it/cavallium/warppi/gui/screens/KeyboardDebugScreen.java index 10302b19..412a08e7 100644 --- a/core/src/main/java/it/cavallium/warppi/gui/screens/KeyboardDebugScreen.java +++ b/core/src/main/java/it/cavallium/warppi/gui/screens/KeyboardDebugScreen.java @@ -27,6 +27,9 @@ public class KeyboardDebugScreen extends Screen { @Override public void initialized() throws InterruptedException {} + @Override + public void graphicInitialized() throws InterruptedException {} + @Override public void render() { final Renderer renderer = Engine.INSTANCE.getHardwareDevice().getDisplayManager().renderer; diff --git a/core/src/main/java/it/cavallium/warppi/gui/screens/LoadingScreen.java b/core/src/main/java/it/cavallium/warppi/gui/screens/LoadingScreen.java index ed4af2f2..db078d4e 100644 --- a/core/src/main/java/it/cavallium/warppi/gui/screens/LoadingScreen.java +++ b/core/src/main/java/it/cavallium/warppi/gui/screens/LoadingScreen.java @@ -33,6 +33,9 @@ public class LoadingScreen extends Screen { Engine.INSTANCE.getHardwareDevice().getDisplayManager().getHUD().hide(); StaticVars.windowZoom.onNext(1f); } + + @Override + public void graphicInitialized() throws InterruptedException {} @Override public void beforeRender(final float dt) { diff --git a/core/src/main/java/it/cavallium/warppi/gui/screens/MathInputScreen.java b/core/src/main/java/it/cavallium/warppi/gui/screens/MathInputScreen.java index d783556c..396c4eb3 100644 --- a/core/src/main/java/it/cavallium/warppi/gui/screens/MathInputScreen.java +++ b/core/src/main/java/it/cavallium/warppi/gui/screens/MathInputScreen.java @@ -105,6 +105,11 @@ public class MathInputScreen extends Screen { /* Fine caricamento */ } + @Override + public void graphicInitialized() throws InterruptedException { + /* Fine caricamento */ + } + @Override public void beforeRender(final float dt) { if (Engine.INSTANCE.getHardwareDevice().getDisplayManager().error == null) { diff --git a/core/src/main/java/it/cavallium/warppi/gui/screens/Screen.java b/core/src/main/java/it/cavallium/warppi/gui/screens/Screen.java index ec6dbb27..5c07dd1e 100644 --- a/core/src/main/java/it/cavallium/warppi/gui/screens/Screen.java +++ b/core/src/main/java/it/cavallium/warppi/gui/screens/Screen.java @@ -10,6 +10,7 @@ public abstract class Screen implements KeyboardEventListener, TouchEventListene public DisplayManager d; public boolean created = false; public boolean initialized = false; + public boolean graphicInitialized = false; public HistoryBehavior historyBehavior = HistoryBehavior.NORMAL; public static long lastDebugScreenID = 1; @@ -19,6 +20,14 @@ public abstract class Screen implements KeyboardEventListener, TouchEventListene debugScreenID = lastDebugScreenID++; } + @Override + public void initializeGraphic() throws InterruptedException { + if (!graphicInitialized) { + graphicInitialized = true; + graphicInitialized(); + } + } + @Override public void initialize() throws InterruptedException { if (!initialized) { @@ -35,10 +44,30 @@ public abstract class Screen implements KeyboardEventListener, TouchEventListene } } + /** + * Called when creating the screen + * Called before initialized() + * Called before graphicInitialized() + * @throws InterruptedException + */ public abstract void created() throws InterruptedException; + /** + * Load everything except skins, etc... + * Called after created() + * Called after graphicInitialized() + * @throws InterruptedException + */ public abstract void initialized() throws InterruptedException; + /** + * Load skins, etc... + * Called after created() + * Called before initialized() + * @throws InterruptedException + */ + public abstract void graphicInitialized() throws InterruptedException; + @Override public abstract void render(); diff --git a/core/src/main/java/it/cavallium/warppi/gui/screens/SolveForXScreen.java b/core/src/main/java/it/cavallium/warppi/gui/screens/SolveForXScreen.java index 6890dd18..c9a06249 100644 --- a/core/src/main/java/it/cavallium/warppi/gui/screens/SolveForXScreen.java +++ b/core/src/main/java/it/cavallium/warppi/gui/screens/SolveForXScreen.java @@ -23,6 +23,9 @@ public class SolveForXScreen extends Screen { @Override public void initialized() throws InterruptedException {} + @Override + public void graphicInitialized() throws InterruptedException {} + @Override public void render() { Engine.INSTANCE.getHardwareDevice().getDisplayManager().renderer.glColor4i(0, 0, 0, 64); diff --git a/desktop/src/main/java/it/cavallium/warppi/gui/graphicengine/impl/swing/SwingWindow.java b/desktop/src/main/java/it/cavallium/warppi/gui/graphicengine/impl/swing/SwingWindow.java index a417bf3e..76cbd429 100644 --- a/desktop/src/main/java/it/cavallium/warppi/gui/graphicengine/impl/swing/SwingWindow.java +++ b/desktop/src/main/java/it/cavallium/warppi/gui/graphicengine/impl/swing/SwingWindow.java @@ -372,6 +372,7 @@ public class SwingWindow extends JFrame { if (renderingLoop != null) { renderingLoop.refresh(); + final int[] a = ((DataBufferInt) display.g.getRaster().getDataBuffer()).getData(); SwingRenderer.canvas2d = a; g.clearRect(0, 0, display.r.size[0] * mult, display.r.size[1] * mult); diff --git a/engine-jogl/src/main/java/it/cavallium/warppi/gui/graphicengine/impl/jogl/JOGLRenderer.java b/engine-jogl/src/main/java/it/cavallium/warppi/gui/graphicengine/impl/jogl/JOGLRenderer.java index 6f6ed6e9..6cdea1f2 100644 --- a/engine-jogl/src/main/java/it/cavallium/warppi/gui/graphicengine/impl/jogl/JOGLRenderer.java +++ b/engine-jogl/src/main/java/it/cavallium/warppi/gui/graphicengine/impl/jogl/JOGLRenderer.java @@ -261,7 +261,7 @@ public class JOGLRenderer implements Renderer { } static OpenedTextureData openTexture(final String file, final boolean isResource) throws GLException, IOException { - BufferedImage img = ImageIO.read(isResource ? JOGLRenderer.class.getResource("/" + file) : new File(file).toURI().toURL()); + BufferedImage img = ImageIO.read(isResource ? JOGLRenderer.class.getResource(file) : new File(file).toURI().toURL()); File f; if (isResource) { f = Files.createTempFile("texture-", ".png").toFile(); diff --git a/engine-jogl/src/main/java/it/cavallium/warppi/gui/graphicengine/impl/jogl/NEWTWindow.java b/engine-jogl/src/main/java/it/cavallium/warppi/gui/graphicengine/impl/jogl/NEWTWindow.java index 9c487747..f574617e 100644 --- a/engine-jogl/src/main/java/it/cavallium/warppi/gui/graphicengine/impl/jogl/NEWTWindow.java +++ b/engine-jogl/src/main/java/it/cavallium/warppi/gui/graphicengine/impl/jogl/NEWTWindow.java @@ -483,7 +483,6 @@ class NEWTWindow implements GLEventListener { final boolean linear = windowZoom % (int) windowZoom != 0f; if (refreshViewport) { - System.err.println("[[[REFVP"); refreshViewport = false; gl.glViewport(0, 0, realWindowSize[0], realWindowSize[1]); diff --git a/teavm/src/main/java/ar/com/hjg/pngj/BufferedStreamFeeder.java b/teavm/src/main/java/ar/com/hjg/pngj/BufferedStreamFeeder.java deleted file mode 100644 index 1b2b548b..00000000 --- a/teavm/src/main/java/ar/com/hjg/pngj/BufferedStreamFeeder.java +++ /dev/null @@ -1,208 +0,0 @@ -package ar.com.hjg.pngj; - -import java.io.IOException; -import java.io.InputStream; - -/** - * Reads bytes from an input stream, and feeds a IBytesConsumer. - */ -public class BufferedStreamFeeder { - - private InputStream stream; - private byte[] buf; - private int pendinglen; // bytes read and stored in buf that have not yet still been fed to - // IBytesConsumer - private int offset; - private boolean eof = false; - private boolean closeStream = true; - private boolean failIfNoFeed = false; - - private static final int DEFAULTSIZE = 8192; - - /** By default, the stream will be closed on close() */ - public BufferedStreamFeeder(final InputStream is) { - this(is, BufferedStreamFeeder.DEFAULTSIZE); - } - - public BufferedStreamFeeder(final InputStream is, final int bufsize) { - stream = is; - buf = new byte[bufsize < 1 ? BufferedStreamFeeder.DEFAULTSIZE : bufsize]; - } - - /** - * Returns inputstream - * - * @return Input Stream from which bytes are read - */ - public InputStream getStream() { - return stream; - } - - /** - * Feeds bytes to the consumer
- * Returns bytes actually consumed
- * This should return 0 only if the stream is EOF or the consumer is done - */ - public int feed(final IBytesConsumer consumer) { - return feed(consumer, Integer.MAX_VALUE); - } - - /** - * Feeds the consumer (with at most maxbytes)
- * Returns 0 only if the stream is EOF (or maxbytes=0). Returns negative is - * the consumer is done.
- * It can return less than maxbytes (that doesn't mean that the consumer or - * the input stream is done) - */ - public int feed(final IBytesConsumer consumer, final int maxbytes) { - if (pendinglen == 0) - refillBuffer(); - final int tofeed = maxbytes >= 0 && maxbytes < pendinglen ? maxbytes : pendinglen; - int n = 0; - if (tofeed > 0) { - n = consumer.consume(buf, offset, tofeed); - if (n > 0) { - offset += n; - pendinglen -= n; - } - } - if (n < 1 && failIfNoFeed) - throw new PngjInputException("Failed to feed bytes (premature ending?)"); - return n; - } - - /** - * Feeds as much bytes as it can to the consumer, in a loop.
- * Returns bytes actually consumed
- * This will stop when either the input stream is eof, or when the consumer - * refuses to eat more bytes. The caller can - * distinguish both cases by calling {@link #hasMoreToFeed()} - */ - public long feedAll(final IBytesConsumer consumer) { - long n = 0; - while (hasMoreToFeed()) { - final int n1 = feed(consumer); - if (n1 < 1) - break; - n += n1; - } - return n; - } - - /** - * Feeds exactly nbytes, retrying if necessary - * - * @param consumer - * Consumer - * @param nbytes - * Number of bytes - * @return true if success, false otherwise (EOF on stream, or consumer is - * done) - */ - public boolean feedFixed(final IBytesConsumer consumer, final int nbytes) { - int remain = nbytes; - while (remain > 0) { - final int n = feed(consumer, remain); - if (n < 1) - return false; - remain -= n; - } - return true; - } - - /** - * If there are not pending bytes to be consumed tries to fill the buffer - * with bytes from the stream. - */ - protected void refillBuffer() { - if (pendinglen > 0 || eof) - return; // only if not pending data - try { - // try to read - offset = 0; - pendinglen = stream.read(buf); - if (pendinglen < 0) { - close(); - return; - } else - return; - } catch (final IOException e) { - throw new PngjInputException(e); - } - } - - /** - * Returuns true if we have more data to fed the consumer. This internally - * tries to grabs more bytes from the stream - * if necessary - */ - public boolean hasMoreToFeed() { - if (eof) - return pendinglen > 0; - else - refillBuffer(); - return pendinglen > 0; - } - - /** - * @param closeStream - * If true, the underlying stream will be closed on when close() - * is called - */ - public void setCloseStream(final boolean closeStream) { - this.closeStream = closeStream; - } - - /** - * Closes this object. - * - * Sets EOF=true, and closes the stream if closeStream is true - * - * This can be called internally, or from outside. - * - * Idempotent, secure, never throws exception. - **/ - public void close() { - eof = true; - buf = null; - pendinglen = 0; - offset = 0; - if (stream != null && closeStream) - try { - stream.close(); - } catch (final Exception e) { - // PngHelperInternal.LOGGER.log(Level.WARNING, "Exception closing stream", e); - } - stream = null; - } - - /** - * Sets a new underlying inputstream. This allows to reuse this object. The - * old underlying is not closed and the state - * is not reset (you should call close() previously if you want that) - * - * @param is - */ - public void setInputStream(final InputStream is) { // to reuse this object - stream = is; - eof = false; - } - - /** - * @return EOF on stream, or close() was called - */ - public boolean isEof() { - return eof; - } - - /** - * If this flag is set (default: false), any call to feed() that returns - * zero (no byte feed) will throw an exception. - * This is useful to be sure of avoid infinite loops in some scenarios. - * - * @param failIfNoFeed - */ - public void setFailIfNoFeed(final boolean failIfNoFeed) { - this.failIfNoFeed = failIfNoFeed; - } -} diff --git a/teavm/src/main/java/ar/com/hjg/pngj/ChunkReader.java b/teavm/src/main/java/ar/com/hjg/pngj/ChunkReader.java deleted file mode 100644 index a33934bb..00000000 --- a/teavm/src/main/java/ar/com/hjg/pngj/ChunkReader.java +++ /dev/null @@ -1,231 +0,0 @@ -package ar.com.hjg.pngj; - -import ar.com.hjg.pngj.chunks.ChunkRaw; - -/** - * Parses a PNG chunk, consuming bytes in one mode: - * {@link ChunkReaderMode#BUFFER}, {@link ChunkReaderMode#PROCESS}, - * {@link ChunkReaderMode#SKIP}. - *

- * It calls {@link #chunkDone()} when done. Also calls - * {@link #processData(byte[], int, int)} if PROCESS - * mode. Apart from thas, it's totally agnostic (it doesn't know about IDAT - * chunks, or PNG general structure) - *

- * The object wraps a ChunkRaw instance (content filled only if BUFFER mode); it - * should be short lived (one instance - * created for each chunk, and discarded after reading), but the wrapped - * chunkRaw can be (usually is) long lived. - */ -public abstract class ChunkReader { - - /** - * see {@link ChunkReaderMode} - */ - public final ChunkReaderMode mode; - private final ChunkRaw chunkRaw; - - private boolean crcCheck; // by default, this is false for SKIP, true elsewhere - - /** - * How many bytes have been read for this chunk, data only - */ - protected int read = 0; - private int crcn = 0; // how many bytes have been read from crc - - /** - * Modes of ChunkReader chunk processing. - */ - public enum ChunkReaderMode { - /** - * Stores full chunk data in buffer - */ - BUFFER, - /** - * Does not store content, processes on the fly, calling processData() - * for each partial read - */ - PROCESS, - /** - * Does not store nor process - implies crcCheck=false (by default). - */ - SKIP; - } - - /** - * The constructor creates also a chunkRaw, preallocated if mode = - * ChunkReaderMode.BUFFER - * - * @param clen - * @param id - * @param offsetInPng - * Informational, is stored in chunkRaw - * @param mode - */ - public ChunkReader(final int clen, final String id, final long offsetInPng, final ChunkReaderMode mode) { - if (mode == null || id.length() != 4 || clen < 0) - throw new PngjExceptionInternal("Bad chunk paramenters: " + mode); - this.mode = mode; - chunkRaw = new ChunkRaw(clen, id, mode == ChunkReaderMode.BUFFER); - chunkRaw.setOffset(offsetInPng); - crcCheck = mode == ChunkReaderMode.SKIP ? false : true; // can be changed with setter - } - - /** - * Returns raw chunk (data can be empty or not, depending on - * ChunkReaderMode) - * - * @return Raw chunk - never null - */ - public ChunkRaw getChunkRaw() { - return chunkRaw; - } - - /** - * Consumes data for the chunk (data and CRC). This never consumes more - * bytes than for this chunk. - * - * In HOT_PROCESS can call processData() (not more than once) - * - * If this ends the chunk (included CRC) it checks CRC (if checking) and - * calls chunkDone() - * - * @param buf - * @param off - * @param len - * @return How many bytes have been consumed - */ - public final int feedBytes(final byte[] buf, int off, int len) { - if (len == 0) - return 0; - if (len < 0) - throw new PngjException("negative length??"); - if (read == 0 && crcn == 0 && crcCheck) - chunkRaw.updateCrc(chunkRaw.idbytes, 0, 4); // initializes crc calculation with the Chunk ID - int bytesForData = chunkRaw.len - read; // bytesForData : bytes to be actually read from chunk data - if (bytesForData > len) - bytesForData = len; - // we want to call processData even for empty chunks (IEND:bytesForData=0) at least once - if (bytesForData > 0 || crcn == 0) { - // in buffer mode we compute the CRC at the end - if (crcCheck && mode != ChunkReaderMode.BUFFER && bytesForData > 0) - chunkRaw.updateCrc(buf, off, bytesForData); - - if (mode == ChunkReaderMode.BUFFER) { - // just copy the contents to the internal buffer - if (chunkRaw.data != buf && bytesForData > 0) - // if the buffer passed if the same as this one, we don't copy the caller should know what he's doing - System.arraycopy(buf, off, chunkRaw.data, read, bytesForData); - } else if (mode == ChunkReaderMode.PROCESS) - processData(read, buf, off, bytesForData); - else { - // mode == ChunkReaderMode.SKIP; nothing to do - } - read += bytesForData; - off += bytesForData; - len -= bytesForData; - } - int crcRead = 0; - if (read == chunkRaw.len) { // data done - read crc? - crcRead = 4 - crcn; - if (crcRead > len) - crcRead = len; - if (crcRead > 0) { - if (buf != chunkRaw.crcval) - System.arraycopy(buf, off, chunkRaw.crcval, crcn, crcRead); - crcn += crcRead; - if (crcn == 4) { - if (crcCheck) { - if (mode == ChunkReaderMode.BUFFER) - chunkRaw.updateCrc(chunkRaw.data, 0, chunkRaw.len); - chunkRaw.checkCrc(); - } - chunkDone(); - } - } - } - return bytesForData + crcRead; - } - - /** - * Chunks has been read - * - * @return true if we have read all chunk, including trailing CRC - */ - public final boolean isDone() { - return crcn == 4; // has read all 4 bytes from the crc - } - - /** - * Determines if CRC should be checked. This should be called before - * starting reading. - * - * @param crcCheck - */ - public void setCrcCheck(final boolean crcCheck) { - if (read != 0 && crcCheck && !this.crcCheck) - throw new PngjException("too late!"); - this.crcCheck = crcCheck; - } - - /** - * This method will only be called in PROCESS mode, probably several times, - * each time with a new fragment of data - * inside the chunk. For chunks with zero-length data, this will still be - * called once. - * - * It's guaranteed that the data corresponds exclusively to this chunk data - * (no crc, no data from no other chunks, ) - * - * @param offsetInchunk - * data bytes that had already been read/processed for this chunk - * @param buf - * @param off - * @param len - */ - protected abstract void processData(int offsetInchunk, byte[] buf, int off, int len); - - /** - * This method will be called (in all modes) when the full chunk -including - * crc- has been read - */ - protected abstract void chunkDone(); - - public boolean isFromDeflatedSet() { - return false; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + (chunkRaw == null ? 0 : chunkRaw.hashCode()); - return result; - } - - /** - * Equality (and hash) is basically delegated to the ChunkRaw - */ - @Override - public boolean equals(final Object obj) { // delegates to chunkraw - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - final ChunkReader other = (ChunkReader) obj; - if (chunkRaw == null) { - if (other.chunkRaw != null) - return false; - } else if (!chunkRaw.equals(other.chunkRaw)) - return false; - return true; - } - - @Override - public String toString() { - return chunkRaw.toString(); - } - -} diff --git a/teavm/src/main/java/ar/com/hjg/pngj/ChunkSeqBuffering.java b/teavm/src/main/java/ar/com/hjg/pngj/ChunkSeqBuffering.java deleted file mode 100644 index 541eafdc..00000000 --- a/teavm/src/main/java/ar/com/hjg/pngj/ChunkSeqBuffering.java +++ /dev/null @@ -1,30 +0,0 @@ -package ar.com.hjg.pngj; - -/** - * This loads the png as a plain sequence of chunks, buffering all - * - * Useful to do things like insert or delete a ancilllary chunk. This does not - * distinguish IDAT from others - **/ -public class ChunkSeqBuffering extends ChunkSeqReader { - protected boolean checkCrc = true; - - public ChunkSeqBuffering() { - super(); - } - - @Override - protected boolean isIdatKind(final String id) { - return false; - } - - @Override - protected boolean shouldCheckCrc(final int len, final String id) { - return checkCrc; - } - - public void setCheckCrc(final boolean checkCrc) { - this.checkCrc = checkCrc; - } - -} diff --git a/teavm/src/main/java/ar/com/hjg/pngj/ChunkSeqReader.java b/teavm/src/main/java/ar/com/hjg/pngj/ChunkSeqReader.java deleted file mode 100644 index 5516615f..00000000 --- a/teavm/src/main/java/ar/com/hjg/pngj/ChunkSeqReader.java +++ /dev/null @@ -1,432 +0,0 @@ -package ar.com.hjg.pngj; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.InputStream; -import java.util.Arrays; - -import ar.com.hjg.pngj.ChunkReader.ChunkReaderMode; -import ar.com.hjg.pngj.chunks.ChunkHelper; - -/** - * Consumes a stream of bytes that consist of a series of PNG-like chunks. - *

- * This has little intelligence, it's quite low-level and general (it could even - * be used for a MNG stream, for example). - * It supports signature recognition and idat deflate - */ -public class ChunkSeqReader implements IBytesConsumer { - - protected static final int SIGNATURE_LEN = 8; - protected final boolean withSignature; - - private final byte[] buf0 = new byte[8]; // for signature or chunk starts - private int buf0len = 0; - - private boolean signatureDone = false; - private boolean done = false; // ended, normally or not - - private int chunkCount = 0; - - private long bytesCount = 0; - - private DeflatedChunksSet curReaderDeflatedSet; // one instance is created for each - // "idat-like set". Normally one. - - private ChunkReader curChunkReader; - - private long idatBytes; // this is only for the IDAT (not mrerely "idat-like") - - /** - * Creates a ChunkSeqReader (with signature) - */ - public ChunkSeqReader() { - this(true); - } - - /** - * @param withSignature - * If true, the stream is assumed be prepended by 8 bit signature - */ - public ChunkSeqReader(final boolean withSignature) { - this.withSignature = withSignature; - signatureDone = !withSignature; - } - - /** - * Consumes (in general, partially) a number of bytes. A single call never - * involves more than one chunk. - * - * When the signature is read, it calls checkSignature() - * - * When the start of a chunk is detected, it calls - * {@link #startNewChunk(int, String, long)} - * - * When data from a chunk is being read, it delegates to - * {@link ChunkReader#feedBytes(byte[], int, int)} - * - * The caller might want to call this method more than once in succesion - * - * This should rarely be overriden - * - * @param buffer - * @param offset - * Offset in buffer - * @param len - * Valid bytes that can be consumed - * @return processed bytes, in the 1-len range. -1 if done. Only returns 0 - * if len=0. - **/ - @Override - public int consume(final byte[] buffer, final int offset, final int len) { - if (done) - return -1; - if (len == 0) - return 0; // nothing to do - if (len < 0) - throw new PngjInputException("Bad len: " + len); - int processed = 0; - if (signatureDone) { - if (curChunkReader == null || curChunkReader.isDone()) { // new chunk: read first 8 bytes - int read0 = 8 - buf0len; - if (read0 > len) - read0 = len; - System.arraycopy(buffer, offset, buf0, buf0len, read0); - buf0len += read0; - processed += read0; - bytesCount += read0; - // len -= read0; - // offset += read0; - if (buf0len == 8) { // end reading chunk length and id - chunkCount++; - final int clen = PngHelperInternal.readInt4fromBytes(buf0, 0); - final String cid = ChunkHelper.toString(buf0, 4, 4); - startNewChunk(clen, cid, bytesCount - 8); - buf0len = 0; - } - } else { // reading chunk, delegates to curChunkReader - final int read1 = curChunkReader.feedBytes(buffer, offset, len); - processed += read1; - bytesCount += read1; - } - } else { // reading signature - int read = ChunkSeqReader.SIGNATURE_LEN - buf0len; - if (read > len) - read = len; - System.arraycopy(buffer, offset, buf0, buf0len, read); - buf0len += read; - if (buf0len == ChunkSeqReader.SIGNATURE_LEN) { - checkSignature(buf0); - buf0len = 0; - signatureDone = true; - } - processed += read; - bytesCount += read; - } - return processed; - } - - /** - * Trys to feeds exactly len bytes, calling - * {@link #consume(byte[], int, int)} retrying if necessary. - * - * This should only be used in callback mode - * - * @return true if succceded - */ - public boolean feedAll(final byte[] buf, int off, int len) { - while (len > 0) { - final int n = consume(buf, off, len); - if (n < 1) - return false; - len -= n; - off += n; - } - return true; - } - - /** - * Called for all chunks when a chunk start has been read (id and length), - * before the chunk data itself is read. It - * creates a new ChunkReader (field accesible via - * {@link #getCurChunkReader()}) in the corresponding mode, and - * eventually a curReaderDeflatedSet.(field accesible via - * {@link #getCurReaderDeflatedSet()}) - * - * To decide the mode and options, it calls - * {@link #shouldCheckCrc(int, String)}, - * {@link #shouldSkipContent(int, String)}, {@link #isIdatKind(String)}. - * Those methods should be overriden in - * preference to this; if overriden, this should be called first. - * - * The respective {@link ChunkReader#chunkDone()} method is directed to this - * {@link #postProcessChunk(ChunkReader)}. - * - * Instead of overriding this, see also - * {@link #createChunkReaderForNewChunk(String, int, long, boolean)} - */ - protected void startNewChunk(final int len, final String id, final long offset) { - if (id.equals(ChunkHelper.IDAT)) - idatBytes += len; - final boolean checkCrc = shouldCheckCrc(len, id); - final boolean skip = shouldSkipContent(len, id); - final boolean isIdatType = isIdatKind(id); - // PngHelperInternal.debug("start new chunk id=" + id + " off=" + offset + " skip=" + skip + " idat=" + - // isIdatType); - // first see if we should terminate an active curReaderDeflatedSet - boolean forCurrentIdatSet = false; - if (curReaderDeflatedSet != null) - forCurrentIdatSet = curReaderDeflatedSet.ackNextChunkId(id); - if (isIdatType && !skip) { // IDAT non skipped: create a DeflatedChunkReader owned by a idatSet - if (!forCurrentIdatSet) { - if (curReaderDeflatedSet != null && !curReaderDeflatedSet.isDone()) - throw new PngjInputException("new IDAT-like chunk when previous was not done"); - curReaderDeflatedSet = createIdatSet(id); - } - curChunkReader = new DeflatedChunkReader(len, id, checkCrc, offset, curReaderDeflatedSet) { - @Override - protected void chunkDone() { - super.chunkDone(); - postProcessChunk(this); - } - }; - - } else { // for non-idat chunks (or skipped idat like) - curChunkReader = createChunkReaderForNewChunk(id, len, offset, skip); - if (!checkCrc) - curChunkReader.setCrcCheck(false); - } - } - - /** - * This will be called for all chunks (even skipped), except for IDAT-like - * non-skiped chunks - * - * The default behaviour is to create a ChunkReader in BUFFER mode (or SKIP - * if skip==true) that calls - * {@link #postProcessChunk(ChunkReader)} (always) when done. - * - * @param id - * Chunk id - * @param len - * Chunk length - * @param offset - * offset inside PNG stream , merely informative - * @param skip - * flag: is true, the content will not be buffered (nor - * processed) - * @return a newly created ChunkReader that will create the ChunkRaw and - * then discarded - */ - protected ChunkReader createChunkReaderForNewChunk(final String id, final int len, final long offset, - final boolean skip) { - return new ChunkReader(len, id, offset, skip ? ChunkReaderMode.SKIP : ChunkReaderMode.BUFFER) { - @Override - protected void chunkDone() { - postProcessChunk(this); - } - - @Override - protected void processData(final int offsetinChhunk, final byte[] buf, final int off, final int len) { - throw new PngjExceptionInternal("should never happen"); - } - }; - } - - /** - * This is called after a chunk is read, in all modes - * - * This implementation only chenks the id of the first chunk, and process - * the IEND chunk (sets done=true) - ** - * Further processing should be overriden (call this first!) - **/ - protected void postProcessChunk(final ChunkReader chunkR) { // called after chunk is read - if (chunkCount == 1) { - final String cid = firstChunkId(); - if (cid != null && !cid.equals(chunkR.getChunkRaw().id)) - throw new PngjInputException("Bad first chunk: " + chunkR.getChunkRaw().id + " expected: " + firstChunkId()); - } - if (chunkR.getChunkRaw().id.equals(endChunkId())) - done = true; - } - - /** - * DeflatedChunksSet factory. This implementation is quite dummy, it usually - * should be overriden. - */ - protected DeflatedChunksSet createIdatSet(final String id) { - return new DeflatedChunksSet(id, 1024, 1024); // sizes: arbitrary This should normally be - // overriden - } - - /** - * Decides if this Chunk is of "IDAT" kind (in concrete: if it is, and if - * it's not to be skiped, a DeflatedChunksSet - * will be created to deflate it and process+ the deflated data) - * - * This implementation always returns always false - * - * @param id - */ - protected boolean isIdatKind(final String id) { - return false; - } - - /** - * Chunks can be skipped depending on id and/or length. Skipped chunks are - * still processed, but their data will be - * null, and CRC will never checked - * - * @param len - * @param id - */ - protected boolean shouldSkipContent(final int len, final String id) { - return false; - } - - protected boolean shouldCheckCrc(final int len, final String id) { - return true; - } - - /** - * Throws PngjInputException if bad signature - * - * @param buf - * Signature. Should be of length 8 - */ - protected void checkSignature(final byte[] buf) { - if (!Arrays.equals(buf, PngHelperInternal.getPngIdSignature())) - throw new PngjInputException("Bad PNG signature"); - } - - /** - * If false, we are still reading the signature - * - * @return true if signature has been read (or if we don't have signature) - */ - public boolean isSignatureDone() { - return signatureDone; - } - - /** - * If true, we either have processe the IEND chunk, or close() has been - * called, or a fatal error has happened - */ - public boolean isDone() { - return done; - } - - /** - * total of bytes read (buffered or not) - */ - public long getBytesCount() { - return bytesCount; - } - - /** - * @return Chunks already read, including partial reading (currently - * reading) - */ - public int getChunkCount() { - return chunkCount; - } - - /** - * Currently reading chunk, or just ended reading - * - * @return null only if still reading signature - */ - public ChunkReader getCurChunkReader() { - return curChunkReader; - } - - /** - * The latest deflated set (typically IDAT chunks) reader. Notice that there - * could be several idat sets (eg for APNG) - */ - public DeflatedChunksSet getCurReaderDeflatedSet() { - return curReaderDeflatedSet; - } - - /** - * Closes this object and release resources. For normal termination or - * abort. Secure and idempotent. - */ - public void close() { // forced closing - if (curReaderDeflatedSet != null) - curReaderDeflatedSet.close(); - done = true; - } - - /** - * Returns true if we are not in middle of a chunk: we have just ended - * reading past chunk , or we are at the start, or - * end of signature, or we are done - */ - public boolean isAtChunkBoundary() { - return bytesCount == 0 || bytesCount == 8 || done || curChunkReader == null || curChunkReader.isDone(); - } - - /** - * Which should be the id of the first chunk - * - * @return null if you don't want to check it - */ - protected String firstChunkId() { - return "IHDR"; - } - - /** - * Helper method, reports amount of bytes inside IDAT chunks. - * - * @return Bytes in IDAT chunks - */ - public long getIdatBytes() { - return idatBytes; - } - - /** - * Which should be the id of the last chunk - * - * @return "IEND" - */ - protected String endChunkId() { - return "IEND"; - } - - /** - * Reads all content from a file. Helper method, only for callback mode - */ - public void feedFromFile(final File f) { - try { - feedFromInputStream(new FileInputStream(f), true); - } catch (final FileNotFoundException e) { - throw new PngjInputException(e.getMessage()); - } - } - - /** - * Reads all content from an input stream. Helper method, only for callback - * mode - * - * @param is - * @param closeStream - * Closes the input stream when done (or if error) - */ - public void feedFromInputStream(final InputStream is, final boolean closeStream) { - final BufferedStreamFeeder sf = new BufferedStreamFeeder(is); - sf.setCloseStream(closeStream); - try { - sf.feedAll(this); - } finally { - close(); - sf.close(); - } - } - - public void feedFromInputStream(final InputStream is) { - feedFromInputStream(is, true); - } -} diff --git a/teavm/src/main/java/ar/com/hjg/pngj/ChunkSeqReaderPng.java b/teavm/src/main/java/ar/com/hjg/pngj/ChunkSeqReaderPng.java deleted file mode 100644 index 18b4777c..00000000 --- a/teavm/src/main/java/ar/com/hjg/pngj/ChunkSeqReaderPng.java +++ /dev/null @@ -1,310 +0,0 @@ -package ar.com.hjg.pngj; - -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import ar.com.hjg.pngj.ChunkReader.ChunkReaderMode; -import ar.com.hjg.pngj.chunks.ChunkFactory; -import ar.com.hjg.pngj.chunks.ChunkHelper; -import ar.com.hjg.pngj.chunks.ChunkLoadBehaviour; -import ar.com.hjg.pngj.chunks.ChunksList; -import ar.com.hjg.pngj.chunks.PngChunk; -import ar.com.hjg.pngj.chunks.PngChunkIDAT; -import ar.com.hjg.pngj.chunks.PngChunkIEND; -import ar.com.hjg.pngj.chunks.PngChunkIHDR; -import ar.com.hjg.pngj.chunks.PngChunkPLTE; - -/** - * Adds to ChunkSeqReader the storing of PngChunk, with a PngFactory, and - * imageInfo + deinterlacer. - *

- * Most usual PNG reading should use this class, or a {@link PngReader}, which - * is a thin wrapper over this. - */ -public class ChunkSeqReaderPng extends ChunkSeqReader { - - protected ImageInfo imageInfo; // initialized at parsing the IHDR - protected ImageInfo curImageInfo; // can vary, for apng - protected Deinterlacer deinterlacer; - protected int currentChunkGroup = -1; - - /** - * All chunks, but some of them can have the buffer empty (IDAT and skipped) - */ - protected ChunksList chunksList = null; - protected final boolean callbackMode; - private long bytesAncChunksLoaded = 0; // bytes loaded from buffered chunks non-critical chunks (data only) - - private boolean checkCrc = true; - - // --- parameters to be set prior to reading --- - private boolean includeNonBufferedChunks = false; - - private final Set chunksToSkip = new HashSet<>(); - private long maxTotalBytesRead = 0; - private long skipChunkMaxSize = 0; - private long maxBytesMetadata = 0; - private IChunkFactory chunkFactory; - private ChunkLoadBehaviour chunkLoadBehaviour = ChunkLoadBehaviour.LOAD_CHUNK_ALWAYS; - - public ChunkSeqReaderPng(final boolean callbackMode) { - super(); - this.callbackMode = callbackMode; - chunkFactory = new ChunkFactory(); // default factory - } - - private void updateAndCheckChunkGroup(final String id) { - if (id.equals(PngChunkIHDR.ID)) { // IDHR - if (currentChunkGroup < 0) - currentChunkGroup = ChunksList.CHUNK_GROUP_0_IDHR; - else - throw new PngjInputException("unexpected chunk " + id); - } else if (id.equals(PngChunkPLTE.ID)) { // PLTE - if (currentChunkGroup == ChunksList.CHUNK_GROUP_0_IDHR || currentChunkGroup == ChunksList.CHUNK_GROUP_1_AFTERIDHR) - currentChunkGroup = ChunksList.CHUNK_GROUP_2_PLTE; - else - throw new PngjInputException("unexpected chunk " + id); - } else if (id.equals(PngChunkIDAT.ID)) { // IDAT (no necessarily the first) - if (currentChunkGroup >= ChunksList.CHUNK_GROUP_0_IDHR && currentChunkGroup <= ChunksList.CHUNK_GROUP_4_IDAT) - currentChunkGroup = ChunksList.CHUNK_GROUP_4_IDAT; - else - throw new PngjInputException("unexpected chunk " + id); - } else if (id.equals(PngChunkIEND.ID)) { // END - if (currentChunkGroup >= ChunksList.CHUNK_GROUP_4_IDAT) - currentChunkGroup = ChunksList.CHUNK_GROUP_6_END; - else - throw new PngjInputException("unexpected chunk " + id); - } else if (currentChunkGroup <= ChunksList.CHUNK_GROUP_1_AFTERIDHR) - currentChunkGroup = ChunksList.CHUNK_GROUP_1_AFTERIDHR; - else if (currentChunkGroup <= ChunksList.CHUNK_GROUP_3_AFTERPLTE) - currentChunkGroup = ChunksList.CHUNK_GROUP_3_AFTERPLTE; - else - currentChunkGroup = ChunksList.CHUNK_GROUP_5_AFTERIDAT; - } - - @Override - public boolean shouldSkipContent(final int len, final String id) { - if (super.shouldSkipContent(len, id)) - return true; - if (ChunkHelper.isCritical(id)) - return false;// critical chunks are never skipped - if (maxTotalBytesRead > 0 && len + getBytesCount() > maxTotalBytesRead) - throw new PngjInputException("Maximum total bytes to read exceeeded: " + maxTotalBytesRead + " offset:" + getBytesCount() + " len=" + len); - if (chunksToSkip.contains(id)) - return true; // specific skip - if (skipChunkMaxSize > 0 && len > skipChunkMaxSize) - return true; // too big chunk - if (maxBytesMetadata > 0 && len > maxBytesMetadata - bytesAncChunksLoaded) - return true; // too much ancillary chunks loaded - switch (chunkLoadBehaviour) { - case LOAD_CHUNK_IF_SAFE: - if (!ChunkHelper.isSafeToCopy(id)) - return true; - break; - case LOAD_CHUNK_NEVER: - return true; - default: - break; - } - return false; - } - - public long getBytesChunksLoaded() { - return bytesAncChunksLoaded; - } - - public int getCurrentChunkGroup() { - return currentChunkGroup; - } - - public void setChunksToSkip(final String... chunksToSkip) { - this.chunksToSkip.clear(); - for (final String c : chunksToSkip) - this.chunksToSkip.add(c); - } - - public void addChunkToSkip(final String chunkToSkip) { - chunksToSkip.add(chunkToSkip); - } - - public void dontSkipChunk(final String chunkToSkip) { - chunksToSkip.remove(chunkToSkip); - } - - public boolean firstChunksNotYetRead() { - return getCurrentChunkGroup() < ChunksList.CHUNK_GROUP_4_IDAT; - } - - @Override - protected void postProcessChunk(final ChunkReader chunkR) { - super.postProcessChunk(chunkR); - if (chunkR.getChunkRaw().id.equals(PngChunkIHDR.ID)) { - final PngChunkIHDR ch = new PngChunkIHDR(null); - ch.parseFromRaw(chunkR.getChunkRaw()); - imageInfo = ch.createImageInfo(); - curImageInfo = imageInfo; - if (ch.isInterlaced()) - deinterlacer = new Deinterlacer(curImageInfo); - chunksList = new ChunksList(imageInfo); - } - if (chunkR.mode == ChunkReaderMode.BUFFER && countChunkTypeAsAncillary(chunkR.getChunkRaw().id)) - bytesAncChunksLoaded += chunkR.getChunkRaw().len; - if (chunkR.mode == ChunkReaderMode.BUFFER || includeNonBufferedChunks) { - final PngChunk chunk = chunkFactory.createChunk(chunkR.getChunkRaw(), getImageInfo()); - chunksList.appendReadChunk(chunk, currentChunkGroup); - } - if (isDone()) - processEndPng(); - } - - protected boolean countChunkTypeAsAncillary(final String id) { - return !ChunkHelper.isCritical(id); - } - - @Override - protected DeflatedChunksSet createIdatSet(final String id) { - final IdatSet ids = new IdatSet(id, getCurImgInfo(), deinterlacer); - ids.setCallbackMode(callbackMode); - return ids; - } - - public IdatSet getIdatSet() { - final DeflatedChunksSet c = getCurReaderDeflatedSet(); - return c instanceof IdatSet ? (IdatSet) c : null; - } - - @Override - protected boolean isIdatKind(final String id) { - return id.equals(PngChunkIDAT.ID); - } - - @Override - public int consume(final byte[] buf, final int off, final int len) { - return super.consume(buf, off, len); - } - - /** - * sets a custom chunk factory. This is typically called with a custom class - * extends ChunkFactory, to adds custom - * chunks to the default well-know ones - * - * @param chunkFactory - */ - public void setChunkFactory(final IChunkFactory chunkFactory) { - this.chunkFactory = chunkFactory; - } - - /** - * Things to be done after IEND processing. This is not called if - * prematurely closed. - */ - protected void processEndPng() { - // nothing to do - } - - public ImageInfo getImageInfo() { - return imageInfo; - } - - public boolean isInterlaced() { - return deinterlacer != null; - } - - public Deinterlacer getDeinterlacer() { - return deinterlacer; - } - - @Override - protected void startNewChunk(final int len, final String id, final long offset) { - updateAndCheckChunkGroup(id); - super.startNewChunk(len, id, offset); - } - - @Override - public void close() { - if (currentChunkGroup != ChunksList.CHUNK_GROUP_6_END)// this could only happen if forced close - currentChunkGroup = ChunksList.CHUNK_GROUP_6_END; - super.close(); - } - - public List getChunks() { - return chunksList.getChunks(); - } - - public void setMaxTotalBytesRead(final long maxTotalBytesRead) { - this.maxTotalBytesRead = maxTotalBytesRead; - } - - public long getSkipChunkMaxSize() { - return skipChunkMaxSize; - } - - public void setSkipChunkMaxSize(final long skipChunkMaxSize) { - this.skipChunkMaxSize = skipChunkMaxSize; - } - - public long getMaxBytesMetadata() { - return maxBytesMetadata; - } - - public void setMaxBytesMetadata(final long maxBytesMetadata) { - this.maxBytesMetadata = maxBytesMetadata; - } - - public long getMaxTotalBytesRead() { - return maxTotalBytesRead; - } - - @Override - protected boolean shouldCheckCrc(final int len, final String id) { - return checkCrc; - } - - public boolean isCheckCrc() { - return checkCrc; - } - - public void setCheckCrc(final boolean checkCrc) { - this.checkCrc = checkCrc; - } - - public boolean isCallbackMode() { - return callbackMode; - } - - public Set getChunksToSkip() { - return chunksToSkip; - } - - public void setChunkLoadBehaviour(final ChunkLoadBehaviour chunkLoadBehaviour) { - this.chunkLoadBehaviour = chunkLoadBehaviour; - } - - public ImageInfo getCurImgInfo() { - return curImageInfo; - } - - public void updateCurImgInfo(final ImageInfo iminfo) { - if (!iminfo.equals(curImageInfo)) - curImageInfo = iminfo; - if (deinterlacer != null) - deinterlacer = new Deinterlacer(curImageInfo); // we could reset it, but... - } - - /** - * If true, the chunks with no data (because skipped or because processed - * like IDAT-type) are still stored in the - * PngChunks list, which might be more informative. - * - * Setting this to false saves a few bytes - * - * Default: false - * - * @param includeNonBufferedChunks - */ - public void setIncludeNonBufferedChunks(final boolean includeNonBufferedChunks) { - this.includeNonBufferedChunks = includeNonBufferedChunks; - } - -} diff --git a/teavm/src/main/java/ar/com/hjg/pngj/ChunkSeqSkipping.java b/teavm/src/main/java/ar/com/hjg/pngj/ChunkSeqSkipping.java deleted file mode 100644 index 96d765ac..00000000 --- a/teavm/src/main/java/ar/com/hjg/pngj/ChunkSeqSkipping.java +++ /dev/null @@ -1,76 +0,0 @@ -package ar.com.hjg.pngj; - -import java.util.ArrayList; -import java.util.List; - -import ar.com.hjg.pngj.ChunkReader.ChunkReaderMode; -import ar.com.hjg.pngj.chunks.ChunkRaw; - -/** - * This simple reader skips all chunks contents and stores the chunkRaw in a - * list. Useful to read chunks structure. - * - * Optionally the contents might be processed. This doesn't distinguish IDAT - * chunks - */ -public class ChunkSeqSkipping extends ChunkSeqReader { - - private final List chunks = new ArrayList<>(); - private boolean skip = true; - - /** - * @param skipAll - * if true, contents will be truly skipped, and CRC will not be - * computed - */ - public ChunkSeqSkipping(final boolean skipAll) { - super(true); - skip = skipAll; - } - - public ChunkSeqSkipping() { - this(true); - } - - @Override - protected ChunkReader createChunkReaderForNewChunk(final String id, final int len, final long offset, - final boolean skip) { - return new ChunkReader(len, id, offset, skip ? ChunkReaderMode.SKIP : ChunkReaderMode.PROCESS) { - @Override - protected void chunkDone() { - postProcessChunk(this); - } - - @Override - protected void processData(final int offsetinChhunk, final byte[] buf, final int off, final int len) { - processChunkContent(getChunkRaw(), offsetinChhunk, buf, off, len); - } - }; - } - - protected void processChunkContent(final ChunkRaw chunkRaw, final int offsetinChhunk, final byte[] buf, - final int off, final int len) { - // does nothing - } - - @Override - protected void postProcessChunk(final ChunkReader chunkR) { - super.postProcessChunk(chunkR); - chunks.add(chunkR.getChunkRaw()); - } - - @Override - protected boolean shouldSkipContent(final int len, final String id) { - return skip; - } - - @Override - protected boolean isIdatKind(final String id) { - return false; - } - - public List getChunks() { - return chunks; - } - -} diff --git a/teavm/src/main/java/ar/com/hjg/pngj/DeflatedChunkReader.java b/teavm/src/main/java/ar/com/hjg/pngj/DeflatedChunkReader.java deleted file mode 100644 index 426f53e8..00000000 --- a/teavm/src/main/java/ar/com/hjg/pngj/DeflatedChunkReader.java +++ /dev/null @@ -1,78 +0,0 @@ -package ar.com.hjg.pngj; - -import ar.com.hjg.pngj.chunks.PngChunkFDAT; - -/** - * - * Specialization of ChunkReader, for IDAT-like chunks. These chunks are part of - * a set of similar chunks (contiguos - * normally, not necessariyl) which conforms a zlib stream - */ -public class DeflatedChunkReader extends ChunkReader { - - protected final DeflatedChunksSet deflatedChunksSet; - protected boolean alsoBuffer = false; - - protected boolean skipBytes = false; // fDAT (APNG) skips 4 bytes) - protected byte[] skippedBytes; // only for fDAT - protected int seqNumExpected = -1; // only for fDAT - - public DeflatedChunkReader(final int clen, final String chunkid, final boolean checkCrc, final long offsetInPng, final DeflatedChunksSet iDatSet) { - super(clen, chunkid, offsetInPng, ChunkReaderMode.PROCESS); - deflatedChunksSet = iDatSet; - if (chunkid.equals(PngChunkFDAT.ID)) { - skipBytes = true; - skippedBytes = new byte[4]; - } - iDatSet.appendNewChunk(this); - } - - /** - * Delegates to ChunkReaderDeflatedSet.processData() - */ - @Override - protected void processData(final int offsetInchunk, final byte[] buf, int off, int len) { - if (skipBytes && offsetInchunk < 4) - for (int oc = offsetInchunk; oc < 4 && len > 0; oc++, off++, len--) - skippedBytes[oc] = buf[off]; - if (len > 0) { // delegate to idatSet - deflatedChunksSet.processBytes(buf, off, len); - if (alsoBuffer) - System.arraycopy(buf, off, getChunkRaw().data, read, len); - } - } - - /** - * only a stupid check for fDAT (I wonder how many APGN readers do this) - */ - @Override - protected void chunkDone() { - if (skipBytes && getChunkRaw().id.equals(PngChunkFDAT.ID)) - if (seqNumExpected >= 0) { - final int seqNum = PngHelperInternal.readInt4fromBytes(skippedBytes, 0); - if (seqNum != seqNumExpected) - throw new PngjInputException("bad chunk sequence for fDAT chunk " + seqNum + " expected " + seqNumExpected); - } - } - - @Override - public boolean isFromDeflatedSet() { - return true; - } - - /** - * In some rare cases you might want to also buffer the data? - */ - public void setAlsoBuffer() { - if (read > 0) - throw new RuntimeException("too late"); - alsoBuffer = true; - getChunkRaw().allocData(); - } - - /** only relevant for fDAT */ - public void setSeqNumExpected(final int seqNumExpected) { - this.seqNumExpected = seqNumExpected; - } - -} diff --git a/teavm/src/main/java/ar/com/hjg/pngj/DeflatedChunksSet.java b/teavm/src/main/java/ar/com/hjg/pngj/DeflatedChunksSet.java deleted file mode 100644 index b4feeb32..00000000 --- a/teavm/src/main/java/ar/com/hjg/pngj/DeflatedChunksSet.java +++ /dev/null @@ -1,422 +0,0 @@ -package ar.com.hjg.pngj; - -import java.util.zip.DataFormatException; -import java.util.zip.Inflater; - -/** - * A set of IDAT-like chunks which, concatenated, form a zlib stream. - *

- * The inflated stream is intented to be read as a sequence of "rows", of which - * the caller knows the lengths (not - * necessary equal) and number. - *

- * Eg: For IDAT non-interlaced images, a row has bytesPerRow + 1 filter byte
- * For interlaced images, the lengths are variable. - *

- * This class can work in sync (polled) mode or async (callback) mode. But for - * callback mode the method - * processRowCallback() must be overriden - *

- * See {@link IdatSet}, which is mostly used and has a slightly simpler use.
- * See DeflatedChunkSetTest for example of use. - */ -public class DeflatedChunksSet { - - protected byte[] row; // a "row" here means a raw (uncopressed filtered) part of the IDAT stream, - // normally a image row (or subimage row for interlaced) plus a filter byte - private int rowfilled; // effective/valid length of row - private int rowlen; // what amount of bytes is to be interpreted as a complete "row". can change - // (for interlaced) - private int rown; // only coincide with image row if non-interlaced - incremented by - // setNextRowSize() - - /* - * States WAITING_FOR_INPUT ROW_READY WORK_DONE TERMINATED - * - * processBytes() is externally called, prohibited in READY (in DONE it's ignored) - * - * WARNING: inflater.finished() != DONE (not enough, not neccesary) DONE means that we have already uncompressed all - * the data of interest. - * - * In non-callback mode, prepareForNextRow() is also externally called, in - * - * Flow: - processBytes() calls inflateData() - inflateData() : if buffer is filled goes to READY else if ! - * inf.finished goes to WAITING else if any data goes to READY (incomplete data to be read) else goes to DONE - in - * Callback mode, after going to READY, n=processCallback() is called and then prepareForNextRow(n) is called. - in - * Polled mode, prepareForNextRow(n) must be called from outside (after checking state=READY) - prepareForNextRow(n) - * goes to DONE if n==0 calls inflateData() again - end() goes to DONE - */ - private enum State { - WAITING_FOR_INPUT, // waiting for more input - ROW_READY, // ready for consumption (might be less than fully filled), ephemeral for CALLBACK - // mode - WORK_DONE, // all data of interest has been read, but we might accept still more trailing chunks - // (we'll ignore them) - TERMINATED; // we are done, and also won't accept more IDAT chunks - - public boolean isDone() { - return this == WORK_DONE || this == TERMINATED; - } // the caller has already uncompressed all the data of interest or EOF - - public boolean isTerminated() { - return this == TERMINATED; - } // we dont accept more chunks - } - - State state = State.WAITING_FOR_INPUT; // never null - - private Inflater inf; - private final boolean infOwn; // true if we own the inflater (we created it) - - private DeflatedChunkReader curChunk; - - private boolean callbackMode = true; - private long nBytesIn = 0; // count the total compressed bytes that have been fed - private long nBytesOut = 0; // count the total uncompressed bytes - int chunkNum = -1; // incremented at each new chunk start - int firstChunqSeqNum = -1; // expected seq num for first chunk. used only for fDAT (APNG) - - /** - * All IDAT-like chunks that form a same DeflatedChunksSet should have the - * same id - */ - public final String chunkid; - - /** - * @param initialRowLen - * Length in bytes of first "row" (see description) - * @param maxRowLen - * Max length in bytes of "rows" - * @param inflater - * Can be null. If not null, must be already reset (and it must - * be closed/released by caller!) - */ - public DeflatedChunksSet(final String chunkid, final int initialRowLen, final int maxRowLen, final Inflater inflater, final byte[] buffer) { - this.chunkid = chunkid; - rowlen = initialRowLen; - if (initialRowLen < 1 || maxRowLen < initialRowLen) - throw new PngjException("bad inital row len " + initialRowLen); - if (inflater != null) { - inf = inflater; - infOwn = false; - } else { - inf = new Inflater(); - infOwn = true; // inflater is own, we will release on close() - } - row = buffer != null && buffer.length >= initialRowLen ? buffer : new byte[maxRowLen]; - rown = -1; - state = State.WAITING_FOR_INPUT; - try { - prepareForNextRow(initialRowLen); - } catch (final RuntimeException e) { - close(); - throw e; - } - } - - public DeflatedChunksSet(final String chunkid, final int initialRowLen, final int maxRowLen) { - this(chunkid, initialRowLen, maxRowLen, null, null); - } - - protected void appendNewChunk(final DeflatedChunkReader cr) { - // all chunks must have same id - if (!chunkid.equals(cr.getChunkRaw().id)) - throw new PngjInputException("Bad chunk inside IdatSet, id:" + cr.getChunkRaw().id + ", expected:" + chunkid); - curChunk = cr; - chunkNum++; - if (firstChunqSeqNum >= 0) - cr.setSeqNumExpected(chunkNum + firstChunqSeqNum); - } - - /** - * Feeds the inflater with the compressed bytes - * - * In poll mode, the caller should not call repeatedly this, without - * consuming first, checking - * isDataReadyForConsumer() - * - * @param buf - * @param off - * @param len - */ - protected void processBytes(final byte[] buf, final int off, final int len) { - nBytesIn += len; - // PngHelperInternal.LOGGER.info("processing compressed bytes in chunkreader : " + len); - if (len < 1 || state.isDone()) - return; - if (state == State.ROW_READY) - throw new PngjInputException("this should only be called if waitingForMoreInput"); - if (inf.needsDictionary() || !inf.needsInput()) - throw new RuntimeException("should not happen"); - inf.setInput(buf, off, len); - // PngHelperInternal.debug("entering processs bytes, state=" + state + - // " callback="+callbackMode); - if (isCallbackMode()) - while (inflateData()) { - final int nextRowLen = processRowCallback(); - prepareForNextRow(nextRowLen); - if (isDone()) - processDoneCallback(); - } - else - inflateData(); - } - - /* - * This never inflates more than one row This returns true if this has resulted in a row being ready and preprocessed - * with preProcessRow (in callback mode, we should call immediately processRowCallback() and - * prepareForNextRow(nextRowLen) - */ - private boolean inflateData() { - try { - // PngHelperInternal.debug("entering inflateData bytes, state=" + state + - // " callback="+callbackMode); - if (state == State.ROW_READY) - throw new PngjException("invalid state");// assert - if (state.isDone()) - return false; - int ninflated = 0; - if (row == null || row.length < rowlen) - row = new byte[rowlen]; // should not happen - if (rowfilled < rowlen && !inf.finished()) { - try { - ninflated = inf.inflate(row, rowfilled, rowlen - rowfilled); - } catch (final DataFormatException e) { - throw new PngjInputException("error decompressing zlib stream ", e); - } - rowfilled += ninflated; - nBytesOut += ninflated; - } - State nextstate = null; - if (rowfilled == rowlen) - nextstate = State.ROW_READY; // complete row, process it - else if (!inf.finished()) - nextstate = State.WAITING_FOR_INPUT; - else if (rowfilled > 0) - nextstate = State.ROW_READY; // complete row, process it - else - nextstate = State.WORK_DONE; // eof, no more data - state = nextstate; - if (state == State.ROW_READY) { - preProcessRow(); - return true; - } - } catch (final RuntimeException e) { - close(); - throw e; - } - return false; - } - - /** - * Called automatically in all modes when a full row has been inflated. - */ - protected void preProcessRow() { - - } - - /** - * Callback, must be implemented in callbackMode - *

- * This should use {@link #getRowFilled()} and {@link #getInflatedRow()} to - * access the row. - *

- * Must return byes of next row, for next callback. - */ - protected int processRowCallback() { - throw new PngjInputException("not implemented"); - } - - /** - * Callback, to be implemented in callbackMode - *

- * This will be called once to notify state done - */ - protected void processDoneCallback() {} - - /** - * Inflated buffer. - * - * The effective length is given by {@link #getRowFilled()} - */ - public byte[] getInflatedRow() { - return row; - } - - /** - * Should be called after the previous row was processed - *

- * Pass 0 or negative to signal that we are done (not expecting more bytes) - *

- * This resets {@link #rowfilled} - *

- * The - */ - public void prepareForNextRow(final int len) { - rowfilled = 0; - rown++; - if (len < 1) { - rowlen = 0; - done(); - } else if (inf.finished()) { - rowlen = 0; - done(); - } else { - state = State.WAITING_FOR_INPUT; - rowlen = len; - if (!callbackMode) - inflateData(); - } - } - - /** - * In this state, the object is waiting for more input to deflate. - *

- * Only in this state it's legal to feed this - */ - public boolean isWaitingForMoreInput() { - return state == State.WAITING_FOR_INPUT; - } - - /** - * In this state, the object is waiting the caller to retrieve inflated data - *

- * Effective length: see {@link #getRowFilled()} - */ - public boolean isRowReady() { - return state == State.ROW_READY; - } - - /** - * In this state, all relevant data has been uncompressed and retrieved - * (exceptionally, the reading has ended - * prematurely). - *

- * We can still feed this object, but the bytes will be swallowed/ignored. - */ - public boolean isDone() { - return state.isDone(); - } - - public boolean isTerminated() { - return state.isTerminated(); - } - - /** - * This will be called by the owner to report us the next chunk to come. We - * can make our own internal changes and - * checks. This returns true if we acknowledge the next chunk as part of - * this set - */ - public boolean ackNextChunkId(final String id) { - if (state.isTerminated()) - return false; - else if (id.equals(chunkid)) - return true; - else if (!allowOtherChunksInBetween(id)) { - if (state.isDone()) { - if (!isTerminated()) - terminate(); - return false; - } else - throw new PngjInputException("Unexpected chunk " + id + " while " + chunkid + " set is not done"); - } else - return true; - } - - protected void terminate() { - close(); - } - - /** - * This should be called when discarding this object, or for aborting. - * Secure, idempotent Don't use this just to - * notify this object that it has no more work to do, see {@link #done()} - */ - public void close() { - try { - if (!state.isTerminated()) - state = State.TERMINATED; - if (infOwn && inf != null) { - inf.end();// we end the Inflater only if we created it - inf = null; - } - } catch (final Exception e) {} - } - - /** - * Forces the DONE state, this object won't uncompress more data. It's still - * not terminated, it will accept more IDAT - * chunks, but will ignore them. - */ - public void done() { - if (!isDone()) - state = State.WORK_DONE; - } - - /** - * Target size of the current row, including filter byte.
- * should coincide (or be less than) with row.length - */ - public int getRowLen() { - return rowlen; - } - - /** This the amount of valid bytes in the buffer */ - public int getRowFilled() { - return rowfilled; - } - - /** - * Get current (last) row number. - *

- * This corresponds to the raw numeration of rows as seen by the deflater. - * Not the same as the real image row, if - * interlaced. - * - */ - public int getRown() { - return rown; - } - - /** - * Some IDAT-like set can allow other chunks in between (APGN?). - *

- * Normally false. - * - * @param id - * Id of the other chunk that appeared in middel of this set. - * @return true if allowed - */ - public boolean allowOtherChunksInBetween(final String id) { - return false; - } - - /** - * Callback mode = async processing - */ - public boolean isCallbackMode() { - return callbackMode; - } - - public void setCallbackMode(final boolean callbackMode) { - this.callbackMode = callbackMode; - } - - /** total number of bytes that have been fed to this object */ - public long getBytesIn() { - return nBytesIn; - } - - /** total number of bytes that have been uncompressed */ - public long getBytesOut() { - return nBytesOut; - } - - @Override - public String toString() { - final StringBuilder sb = new StringBuilder("idatSet : " + curChunk.getChunkRaw().id + " state=" + state + " rows=" + rown + " bytes=" + nBytesIn + "/" + nBytesOut); - return sb.toString(); - } - -} diff --git a/teavm/src/main/java/ar/com/hjg/pngj/Deinterlacer.java b/teavm/src/main/java/ar/com/hjg/pngj/Deinterlacer.java deleted file mode 100644 index b9c895b7..00000000 --- a/teavm/src/main/java/ar/com/hjg/pngj/Deinterlacer.java +++ /dev/null @@ -1,197 +0,0 @@ -package ar.com.hjg.pngj; - -public class Deinterlacer { - final ImageInfo imi; - private int pass; // 1-7 - private int rows, cols; - int dY, dX, oY, oX; // current step and offset (in pixels) - int oXsamples, dXsamples; // step in samples - - // current row in the virtual subsampled image; this increments (by 1) from 0 to rows/dy 7 times - private int currRowSubimg; - // in the real image, this will cycle from 0 to im.rows in different steps, 7 times - private int currRowReal; - private int currRowSeq; // not counting empty rows - - int totalRows; - private boolean ended = false; - - public Deinterlacer(final ImageInfo iminfo) { - imi = iminfo; - pass = 0; - currRowSubimg = -1; - currRowReal = -1; - currRowSeq = 0; - ended = false; - totalRows = 0; // lazy compute - setPass(1); - setRow(0); - } - - /** this refers to the row currRowSubimg */ - private void setRow(final int n) { // This should be called only intercally, in sequential order - currRowSubimg = n; - currRowReal = n * dY + oY; - if (currRowReal < 0 || currRowReal >= imi.rows) - throw new PngjExceptionInternal("bad row - this should not happen"); - } - - /** Skips passes with no rows. Return false is no more rows */ - boolean nextRow() { - currRowSeq++; - if (rows == 0 || currRowSubimg >= rows - 1) { // next pass - if (pass == 7) { - ended = true; - return false; - } - setPass(pass + 1); - if (rows == 0) { - currRowSeq--; - return nextRow(); - } - setRow(0); - } else - setRow(currRowSubimg + 1); - return true; - } - - boolean isEnded() { - return ended; - } - - void setPass(final int p) { - if (pass == p) - return; - pass = p; - final byte[] pp = Deinterlacer.paramsForPass(p);// dx,dy,ox,oy - dX = pp[0]; - dY = pp[1]; - oX = pp[2]; - oY = pp[3]; - rows = imi.rows > oY ? (imi.rows + dY - 1 - oY) / dY : 0; - cols = imi.cols > oX ? (imi.cols + dX - 1 - oX) / dX : 0; - if (cols == 0) - rows = 0; // well, really... - dXsamples = dX * imi.channels; - oXsamples = oX * imi.channels; - } - - static byte[] paramsForPass(final int p) {// dx,dy,ox,oy - switch (p) { - case 1: - return new byte[] { 8, 8, 0, 0 }; - case 2: - return new byte[] { 8, 8, 4, 0 }; - case 3: - return new byte[] { 4, 8, 0, 4 }; - case 4: - return new byte[] { 4, 4, 2, 0 }; - case 5: - return new byte[] { 2, 4, 0, 2 }; - case 6: - return new byte[] { 2, 2, 1, 0 }; - case 7: - return new byte[] { 1, 2, 0, 1 }; - default: - throw new PngjExceptionInternal("bad interlace pass" + p); - } - } - - /** - * current row number inside the "sub image" - */ - int getCurrRowSubimg() { - return currRowSubimg; - } - - /** - * current row number inside the "real image" - */ - int getCurrRowReal() { - return currRowReal; - } - - /** - * current pass number (1-7) - */ - int getPass() { - return pass; - } - - /** - * How many rows has the current pass? - **/ - int getRows() { - return rows; - } - - /** - * How many columns (pixels) are there in the current row - */ - int getCols() { - return cols; - } - - public int getPixelsToRead() { - return getCols(); - } - - public int getBytesToRead() { // not including filter byte - return (imi.bitspPixel * getPixelsToRead() + 7) / 8; - } - - public int getdY() { - return dY; - } - - /* - * in pixels - */ - public int getdX() { - return dX; - } - - public int getoY() { - return oY; - } - - /* - * in pixels - */ - public int getoX() { - return oX; - } - - public int getTotalRows() { - if (totalRows == 0) - for (int p = 1; p <= 7; p++) { - final byte[] pp = Deinterlacer.paramsForPass(p); // dx dy ox oy - final int rows = imi.rows > pp[3] ? (imi.rows + pp[1] - 1 - pp[3]) / pp[1] : 0; - final int cols = imi.cols > pp[2] ? (imi.cols + pp[0] - 1 - pp[2]) / pp[0] : 0; - if (rows > 0 && cols > 0) - totalRows += rows; - } - return totalRows; - } - - /** - * total unfiltered bytes in the image, including the filter byte - */ - public long getTotalRawBytes() { // including the filter byte - long bytes = 0; - for (int p = 1; p <= 7; p++) { - final byte[] pp = Deinterlacer.paramsForPass(p); // dx dy ox oy - final int rows = imi.rows > pp[3] ? (imi.rows + pp[1] - 1 - pp[3]) / pp[1] : 0; - final int cols = imi.cols > pp[2] ? (imi.cols + pp[0] - 1 - pp[2]) / pp[0] : 0; - final int bytesr = (imi.bitspPixel * cols + 7) / 8; // without filter byte - if (rows > 0 && cols > 0) - bytes += rows * (1 + (long) bytesr); - } - return bytes; - } - - public int getCurrRowSeq() { - return currRowSeq; - } - -} diff --git a/teavm/src/main/java/ar/com/hjg/pngj/FilterType.java b/teavm/src/main/java/ar/com/hjg/pngj/FilterType.java deleted file mode 100644 index b71bc6e6..00000000 --- a/teavm/src/main/java/ar/com/hjg/pngj/FilterType.java +++ /dev/null @@ -1,125 +0,0 @@ -package ar.com.hjg.pngj; - -import java.util.HashMap; - -/** - * Internal PNG predictor filter type - * - * Negative values are pseudo types, actually global strategies for writing, - * that (can) result on different real filters - * for different rows - */ -public enum FilterType { - /** - * No filter. - */ - FILTER_NONE(0), - /** - * SUB filter (uses same row) - */ - FILTER_SUB(1), - /** - * UP filter (uses previous row) - */ - FILTER_UP(2), - /** - * AVERAGE filter - */ - FILTER_AVERAGE(3), - /** - * PAETH predictor - */ - FILTER_PAETH(4), - /** - * Default strategy: select one of the standard filters depending on global - * image parameters - */ - FILTER_DEFAULT(-1), - /** - * @deprecated use #FILTER_ADAPTIVE_FAST - */ - FILTER_AGGRESSIVE(-2), - /** - * @deprecated use #FILTER_ADAPTIVE_MEDIUM or #FILTER_ADAPTIVE_FULL - */ - FILTER_VERYAGGRESSIVE(-4), - /** - * Adaptative strategy, sampling each row, or almost - */ - FILTER_ADAPTIVE_FULL(-4), - /** - * Adaptive strategy, skippping some rows - */ - FILTER_ADAPTIVE_MEDIUM(-3), // samples about 1/4 row - /** - * Adaptative strategy, skipping many rows - more speed - */ - FILTER_ADAPTIVE_FAST(-2), // samples each 8 or 16 rows - /** - * Experimental - */ - FILTER_SUPER_ADAPTIVE(-10), // - /** - * Preserves the filter passed in original row. - */ - FILTER_PRESERVE(-40), - /** - * Uses all fiters, one for lines, cyciclally. Only for tests. - */ - FILTER_CYCLIC(-50), - /** - * Not specified, placeholder for unknown or NA filters. - */ - FILTER_UNKNOWN(-100); - - public final int val; - - private FilterType(final int val) { - this.val = val; - } - - private static HashMap byVal; - - static { - FilterType.byVal = new HashMap<>(); - for (final FilterType ft : FilterType.values()) - FilterType.byVal.put(ft.val, ft); - } - - public static FilterType getByVal(final int i) { - return FilterType.byVal.get(i); - } - - /** only considers standard */ - public static boolean isValidStandard(final int i) { - return i >= 0 && i <= 4; - } - - public static boolean isValidStandard(final FilterType fy) { - return fy != null && FilterType.isValidStandard(fy.val); - } - - public static boolean isAdaptive(final FilterType fy) { - return fy.val <= -2 && fy.val >= -4; - } - - /** - * Returns all "standard" filters - */ - public static FilterType[] getAllStandard() { - return new FilterType[] { FILTER_NONE, FILTER_SUB, FILTER_UP, FILTER_AVERAGE, FILTER_PAETH }; - } - - public static FilterType[] getAllStandardNoneLast() { - return new FilterType[] { FILTER_SUB, FILTER_UP, FILTER_AVERAGE, FILTER_PAETH, FILTER_NONE }; - } - - public static FilterType[] getAllStandardExceptNone() { - return new FilterType[] { FILTER_SUB, FILTER_UP, FILTER_AVERAGE, FILTER_PAETH }; - } - - static FilterType[] getAllStandardForFirstRow() { - return new FilterType[] { FILTER_SUB, FILTER_NONE }; - } - -} diff --git a/teavm/src/main/java/ar/com/hjg/pngj/IBytesConsumer.java b/teavm/src/main/java/ar/com/hjg/pngj/IBytesConsumer.java deleted file mode 100644 index bba345f6..00000000 --- a/teavm/src/main/java/ar/com/hjg/pngj/IBytesConsumer.java +++ /dev/null @@ -1,16 +0,0 @@ -package ar.com.hjg.pngj; - -/** - * Bytes consumer. Objects implementing this interface can act as bytes - * consumers, that are "fed" with bytes. - */ -public interface IBytesConsumer { - /** - * Eats some bytes, at most len. - *

- * Returns bytes actually consumed. A negative return value signals that the - * consumer is done, it refuses to eat more - * bytes. This should only return 0 if len is 0 - */ - int consume(byte[] buf, int offset, int len); -} diff --git a/teavm/src/main/java/ar/com/hjg/pngj/IChunkFactory.java b/teavm/src/main/java/ar/com/hjg/pngj/IChunkFactory.java deleted file mode 100644 index 4e0f814d..00000000 --- a/teavm/src/main/java/ar/com/hjg/pngj/IChunkFactory.java +++ /dev/null @@ -1,23 +0,0 @@ -package ar.com.hjg.pngj; - -import ar.com.hjg.pngj.chunks.ChunkRaw; -import ar.com.hjg.pngj.chunks.PngChunk; - -/** - * Factory to create a {@link PngChunk} from a {@link ChunkRaw}. - *

- * Used by {@link PngReader} - */ -public interface IChunkFactory { - - /** - * @param chunkRaw - * Chunk in raw form. Data can be null if it was skipped or - * processed directly (eg IDAT) - * @param imgInfo - * Not normally necessary, but some chunks want this info - * @return should never return null. - */ - PngChunk createChunk(ChunkRaw chunkRaw, ImageInfo imgInfo); - -} diff --git a/teavm/src/main/java/ar/com/hjg/pngj/IDatChunkWriter.java b/teavm/src/main/java/ar/com/hjg/pngj/IDatChunkWriter.java deleted file mode 100644 index f3c7d0c6..00000000 --- a/teavm/src/main/java/ar/com/hjg/pngj/IDatChunkWriter.java +++ /dev/null @@ -1,132 +0,0 @@ -package ar.com.hjg.pngj; - -import java.io.OutputStream; - -import ar.com.hjg.pngj.chunks.ChunkHelper; -import ar.com.hjg.pngj.chunks.ChunkRaw; - -/** - * Outputs a sequence of IDAT-like chunk, that is filled progressively until the - * max chunk length is reached (or until - * flush()) - */ -public class IDatChunkWriter { - - private static final int MAX_LEN_DEFAULT = 32768; // 32K rather arbitrary - data only - - private final OutputStream outputStream; - private final int maxChunkLen; - private byte[] buf; - - private int offset = 0; - private int availLen; - private long totalBytesWriten = 0; // including header+crc - private int chunksWriten = 0; - - public IDatChunkWriter(final OutputStream outputStream) { - this(outputStream, 0); - } - - public IDatChunkWriter(final OutputStream outputStream, final int maxChunkLength) { - this.outputStream = outputStream; - maxChunkLen = maxChunkLength > 0 ? maxChunkLength : IDatChunkWriter.MAX_LEN_DEFAULT; - buf = new byte[maxChunkLen]; - availLen = maxChunkLen - offset; - postReset(); - } - - public IDatChunkWriter(final OutputStream outputStream, final byte[] b) { - this.outputStream = outputStream; - buf = b != null ? b : new byte[IDatChunkWriter.MAX_LEN_DEFAULT]; - maxChunkLen = b.length; - availLen = maxChunkLen - offset; - postReset(); - } - - protected byte[] getChunkId() { - return ChunkHelper.b_IDAT; - } - - /** - * Writes a chhunk if there is more than minLenToWrite. - * - * This is normally called internally, but can be called explicitly to force - * flush. - */ - public final void flush() { - if (offset > 0 && offset >= minLenToWrite()) { - final ChunkRaw c = new ChunkRaw(offset, getChunkId(), false); - c.data = buf; - c.writeChunk(outputStream); - totalBytesWriten += c.len + 12; - chunksWriten++; - offset = 0; - availLen = maxChunkLen; - postReset(); - } - } - - public int getOffset() { - return offset; - } - - public int getAvailLen() { - return availLen; - } - - /** triggers an flush+reset if appropiate */ - public void incrementOffset(final int n) { - offset += n; - availLen -= n; - if (availLen < 0) - throw new PngjOutputException("Anomalous situation"); - if (availLen == 0) - flush(); - } - - /** - * this should rarely be used, the normal way (to avoid double copying) is - * to get the buffer and write directly to it - */ - public void write(final byte[] b, int o, int len) { - while (len > 0) { - final int n = len <= availLen ? len : availLen; - System.arraycopy(b, o, buf, offset, n); - incrementOffset(n); - len -= n; - o += n; - } - } - - /** this will be called after reset */ - protected void postReset() { - // fdat could override this (and minLenToWrite) to add a prefix - } - - protected int minLenToWrite() { - return 1; - } - - public void close() { - flush(); - offset = 0; - buf = null; - } - - /** - * You can write directly to this buffer, using {@link #getOffset()} and - * {@link #getAvailLen()}. You should call - * {@link #incrementOffset(int)} inmediately after. - */ - public byte[] getBuf() { - return buf; - } - - public long getTotalBytesWriten() { - return totalBytesWriten; - } - - public int getChunksWriten() { - return chunksWriten; - } -} diff --git a/teavm/src/main/java/ar/com/hjg/pngj/IImageLine.java b/teavm/src/main/java/ar/com/hjg/pngj/IImageLine.java deleted file mode 100644 index c1275194..00000000 --- a/teavm/src/main/java/ar/com/hjg/pngj/IImageLine.java +++ /dev/null @@ -1,50 +0,0 @@ -package ar.com.hjg.pngj; - -/** - * General format-translated image line. - *

- * The methods from this interface provides translation from/to PNG raw - * unfiltered pixel data, for each image line. This - * doesn't make any assumptions of underlying storage. - *

- * The user of this library will not normally use this methods, but instead will - * cast to a more concrete implementation, - * as {@link ImageLineInt} or {@link ImageLineByte} with its methods for - * accessing the pixel values. - */ -public interface IImageLine { - - /** - * Extract pixels from a raw unlfilterd PNG row. Len is the total amount of - * bytes in the array, including the first - * byte (filter type) - * - * Arguments offset and step (0 and 1 for non interlaced) are in PIXELS. - * It's guaranteed that when step==1 then - * offset=0 - * - * Notice that when step!=1 the data is partial, this method will be called - * several times - * - * Warning: the data in array 'raw' starts at position 0 and has 'len' - * consecutive bytes. 'offset' and 'step' refer to - * the pixels in destination - */ - void readFromPngRaw(byte[] raw, int len, int offset, int step); - - /** - * This is called when the read for the line has been completed (eg for - * interlaced). It's called exactly once for each - * line. This is provided in case the class needs to to some postprocessing. - */ - void endReadFromPngRaw(); - - /** - * Writes the line to a PNG raw byte array, in the unfiltered PNG format - * Notice that the first byte is the filter - * type, you should write it only if you know it. - * - */ - void writeToPngRaw(byte[] raw); - -} diff --git a/teavm/src/main/java/ar/com/hjg/pngj/IImageLineArray.java b/teavm/src/main/java/ar/com/hjg/pngj/IImageLineArray.java deleted file mode 100644 index 3b2fdfa8..00000000 --- a/teavm/src/main/java/ar/com/hjg/pngj/IImageLineArray.java +++ /dev/null @@ -1,26 +0,0 @@ -package ar.com.hjg.pngj; - -/** - * This interface is just for the sake of unifying some methods of - * {@link ImageLineHelper} that can use both - * {@link ImageLineInt} or {@link ImageLineByte}. It's not very useful outside - * that, and the user should not rely much - * on this. - */ -public interface IImageLineArray { - ImageInfo getImageInfo(); - - FilterType getFilterType(); - - /** - * length of array (should correspond to samples) - */ - int getSize(); - - /** - * Get i-th element of array (for 0 to size-1). The meaning of this is type - * dependent. For ImageLineInt and - * ImageLineByte is the sample value. - */ - int getElem(int i); -} diff --git a/teavm/src/main/java/ar/com/hjg/pngj/IImageLineFactory.java b/teavm/src/main/java/ar/com/hjg/pngj/IImageLineFactory.java deleted file mode 100644 index aaf4202a..00000000 --- a/teavm/src/main/java/ar/com/hjg/pngj/IImageLineFactory.java +++ /dev/null @@ -1,8 +0,0 @@ -package ar.com.hjg.pngj; - -/** - * Image Line factory. - */ -public interface IImageLineFactory { - T createImageLine(ImageInfo iminfo); -} diff --git a/teavm/src/main/java/ar/com/hjg/pngj/IImageLineSet.java b/teavm/src/main/java/ar/com/hjg/pngj/IImageLineSet.java deleted file mode 100644 index 6c4b659d..00000000 --- a/teavm/src/main/java/ar/com/hjg/pngj/IImageLineSet.java +++ /dev/null @@ -1,64 +0,0 @@ -package ar.com.hjg.pngj; - -/** - * Set of {@link IImageLine} elements. - *

- * This is actually a "virtual" set, it can be implemented in several ways; for - * example - *

    - *
  • Cursor-like: stores only one line, which is implicitly moved when - * requested
  • - *
  • All lines: all lines stored as an array of IImageLine
  • - *
  • - * Subset of lines: eg, only first 3 lines, or odd numbered lines. Or a band of - * neighbours lines that is moved like a - * cursor.
  • - * The ImageLine that PngReader returns is hosted by a IImageLineSet (this - * abstraction allows the implementation to deal - * with interlaced images cleanly) but the library user does not normally needs - * to know that (or rely on that), except - * for the {@link PngReader#readRows()} method. - *
- */ -public interface IImageLineSet { - - /** - * Asks for imageline corresponding to row n in the original image - * (zero based). This can trigger side - * effects in this object (eg, advance a cursor, set current row number...) - * In some scenarios, this should be consider - * as alias to (pseudocode) positionAtLine(n); getCurrentLine(); - *

- * Throws exception if not available. The caller is supposed to know what - * he/she is doing - **/ - IImageLine getImageLine(int n); - - /** - * Like {@link #getImageLine(int)} but uses the raw numbering inside the - * LineSet This makes little sense for a cursor - * - * @param n - * Should normally go from 0 to {@link #size()} - * @return - */ - IImageLine getImageLineRawNum(int n); - - /** - * Returns true if the set contain row n (in the original - * image,zero based) currently allocated. - *

- * If it's a single-cursor, this should return true only if it's positioned - * there. (notice that hasImageLine(n) can - * return false, but getImageLine(n) can be ok) - * - **/ - boolean hasImageLine(int n); - - /** - * Internal size of allocated rows This is informational, it should rarely - * be important for the caller. - **/ - int size(); - -} diff --git a/teavm/src/main/java/ar/com/hjg/pngj/IImageLineSetFactory.java b/teavm/src/main/java/ar/com/hjg/pngj/IImageLineSetFactory.java deleted file mode 100644 index 821dd6fe..00000000 --- a/teavm/src/main/java/ar/com/hjg/pngj/IImageLineSetFactory.java +++ /dev/null @@ -1,32 +0,0 @@ -package ar.com.hjg.pngj; - -/** - * Factory of {@link IImageLineSet}, used by {@link PngReader}. - *

- * - * @param - * Generic type of IImageLine - */ -public interface IImageLineSetFactory { - /** - * Creates a new {@link IImageLineSet} - * - * If singleCursor=true, the caller will read and write one row fully at a - * time, in order (it'll never try to read out - * of order lines), so the implementation can opt for allocate only one - * line. - * - * @param imgInfo - * Image info - * @param singleCursor - * : will read/write one row at a time - * @param nlines - * : how many lines we plan to read - * @param noffset - * : how many lines we want to skip from the original image - * (normally 0) - * @param step - * : row step (normally 1) - */ - IImageLineSet create(ImageInfo imgInfo, boolean singleCursor, int nlines, int noffset, int step); -} diff --git a/teavm/src/main/java/ar/com/hjg/pngj/IPngWriterFactory.java b/teavm/src/main/java/ar/com/hjg/pngj/IPngWriterFactory.java deleted file mode 100644 index c8c464e5..00000000 --- a/teavm/src/main/java/ar/com/hjg/pngj/IPngWriterFactory.java +++ /dev/null @@ -1,7 +0,0 @@ -package ar.com.hjg.pngj; - -import java.io.OutputStream; - -public interface IPngWriterFactory { - PngWriter createPngWriter(OutputStream outputStream, ImageInfo imgInfo); -} diff --git a/teavm/src/main/java/ar/com/hjg/pngj/IdatSet.java b/teavm/src/main/java/ar/com/hjg/pngj/IdatSet.java deleted file mode 100644 index cd0c49d5..00000000 --- a/teavm/src/main/java/ar/com/hjg/pngj/IdatSet.java +++ /dev/null @@ -1,246 +0,0 @@ -package ar.com.hjg.pngj; - -import java.util.Arrays; -import java.util.zip.Checksum; -import java.util.zip.Inflater; - -/** - * This object process the concatenation of IDAT chunks. - *

- * It extends {@link DeflatedChunksSet}, adding the intelligence to unfilter - * rows, and to understand row lenghts in - * terms of ImageInfo and (eventually) Deinterlacer - */ -public class IdatSet extends DeflatedChunksSet { - - protected byte rowUnfiltered[]; - protected byte rowUnfilteredPrev[]; - protected final ImageInfo imgInfo; // in the case of APNG this is the frame image - protected final Deinterlacer deinterlacer; - - final RowInfo rowinfo; // info for the last processed row, for debug - - protected int filterUseStat[] = new int[5]; // for stats - - /** - * @param id - * Chunk id (first chunk), should be shared by all concatenated - * chunks - * @param iminfo - * Image info - * @param deinterlacer - * Not null if interlaced - */ - public IdatSet(final String id, final ImageInfo iminfo, final Deinterlacer deinterlacer) { - this(id, iminfo, deinterlacer, null, null); - } - - /** - * Special constructor with preallocated buffer. - *

- *

- * Same as {@link #IdatSet(String, ImageInfo, Deinterlacer)}, but you can - * pass a Inflater (will be reset internally), - * and a buffer (will be used only if size is enough) - */ - public IdatSet(final String id, final ImageInfo iminfo, final Deinterlacer deinterlacer, final Inflater inf, final byte[] buffer) { - super(id, deinterlacer != null ? deinterlacer.getBytesToRead() + 1 : iminfo.bytesPerRow + 1, iminfo.bytesPerRow + 1, inf, buffer); - imgInfo = iminfo; - this.deinterlacer = deinterlacer; - rowinfo = new RowInfo(iminfo, deinterlacer); - } - - /** - * Applies PNG un-filter to inflated raw line. Result in - * {@link #getUnfilteredRow()} {@link #getRowLen()} - */ - public void unfilterRow() { - unfilterRow(rowinfo.bytesRow); - } - - // nbytes: NOT including the filter byte. leaves result in rowUnfiltered - protected void unfilterRow(final int nbytes) { - if (rowUnfiltered == null || rowUnfiltered.length < row.length) { - rowUnfiltered = new byte[row.length]; - rowUnfilteredPrev = new byte[row.length]; - } - if (rowinfo.rowNsubImg == 0) - Arrays.fill(rowUnfiltered, (byte) 0); // see swap that follows - // swap - final byte[] tmp = rowUnfiltered; - rowUnfiltered = rowUnfilteredPrev; - rowUnfilteredPrev = tmp; - - final int ftn = row[0]; - if (!FilterType.isValidStandard(ftn)) - throw new PngjInputException("Filter type " + ftn + " invalid"); - final FilterType ft = FilterType.getByVal(ftn); - filterUseStat[ftn]++; - rowUnfiltered[0] = row[0]; // we copy the filter type, can be useful - switch (ft) { - case FILTER_NONE: - unfilterRowNone(nbytes); - break; - case FILTER_SUB: - unfilterRowSub(nbytes); - break; - case FILTER_UP: - unfilterRowUp(nbytes); - break; - case FILTER_AVERAGE: - unfilterRowAverage(nbytes); - break; - case FILTER_PAETH: - unfilterRowPaeth(nbytes); - break; - default: - throw new PngjInputException("Filter type " + ftn + " not implemented"); - } - } - - private void unfilterRowAverage(final int nbytes) { - int i, j, x; - for (j = 1 - imgInfo.bytesPixel, i = 1; i <= nbytes; i++, j++) { - x = j > 0 ? rowUnfiltered[j] & 0xff : 0; - rowUnfiltered[i] = (byte) (row[i] + (x + (rowUnfilteredPrev[i] & 0xFF)) / 2); - } - } - - private void unfilterRowNone(final int nbytes) { - for (int i = 1; i <= nbytes; i++) - rowUnfiltered[i] = row[i]; - } - - private void unfilterRowPaeth(final int nbytes) { - int i, j, x, y; - for (j = 1 - imgInfo.bytesPixel, i = 1; i <= nbytes; i++, j++) { - x = j > 0 ? rowUnfiltered[j] & 0xFF : 0; - y = j > 0 ? rowUnfilteredPrev[j] & 0xFF : 0; - rowUnfiltered[i] = (byte) (row[i] + PngHelperInternal.filterPaethPredictor(x, rowUnfilteredPrev[i] & 0xFF, y)); - } - } - - private void unfilterRowSub(final int nbytes) { - int i, j; - for (i = 1; i <= imgInfo.bytesPixel; i++) - rowUnfiltered[i] = row[i]; - for (j = 1, i = imgInfo.bytesPixel + 1; i <= nbytes; i++, j++) - rowUnfiltered[i] = (byte) (row[i] + rowUnfiltered[j]); - } - - private void unfilterRowUp(final int nbytes) { - for (int i = 1; i <= nbytes; i++) - rowUnfiltered[i] = (byte) (row[i] + rowUnfilteredPrev[i]); - } - - /** - * does the unfiltering of the inflated row, and updates row info - */ - @Override - protected void preProcessRow() { - super.preProcessRow(); - rowinfo.update(getRown()); - unfilterRow(); - rowinfo.updateBuf(rowUnfiltered, rowinfo.bytesRow + 1); - } - - /** - * Method for async/callback mode . - *

- * In callback mode will be called as soon as each row is retrieved - * (inflated and unfiltered), after - * {@link #preProcessRow()} - *

- * This is a dummy implementation (this normally should be overriden) that - * does nothing more than compute the length - * of next row. - *

- * The return value is essential - *

- * - * @return Length of next row, in bytes (including filter byte), - * non-positive if done - */ - @Override - protected int processRowCallback() { - final int bytesNextRow = advanceToNextRow(); - return bytesNextRow; - } - - @Override - protected void processDoneCallback() { - super.processDoneCallback(); - } - - /** - * Signals that we are done with the previous row, begin reading the next - * one. - *

- * In polled mode, calls setNextRowLen() - *

- * Warning: after calling this, the unfilterRow is invalid! - * - * @return Returns nextRowLen - */ - public int advanceToNextRow() { - // PngHelperInternal.LOGGER.info("advanceToNextRow"); - int bytesNextRow; - if (deinterlacer == null) - bytesNextRow = getRown() >= imgInfo.rows - 1 ? 0 : imgInfo.bytesPerRow + 1; - else { - final boolean more = deinterlacer.nextRow(); - bytesNextRow = more ? deinterlacer.getBytesToRead() + 1 : 0; - } - if (!isCallbackMode()) - prepareForNextRow(bytesNextRow); - return bytesNextRow; - } - - @Override - public boolean isRowReady() { - return !isWaitingForMoreInput(); - - } - - /** - * Unfiltered row. - *

- * This should be called only if {@link #isRowReady()} returns true. - *

- * To get real length, use {@link #getRowLen()} - *

- * - * @return Unfiltered row, includes filter byte - */ - public byte[] getUnfilteredRow() { - return rowUnfiltered; - } - - public Deinterlacer getDeinterlacer() { - return deinterlacer; - } - - void updateCrcs(final Checksum... idatCrcs) { - for (final Checksum idatCrca : idatCrcs) - if (idatCrca != null)// just for testing - idatCrca.update(getUnfilteredRow(), 1, getRowFilled() - 1); - } - - @Override - public void close() { - super.close(); - rowUnfiltered = null;// not really necessary... - rowUnfilteredPrev = null; - } - - /** - * Only for debug/stats - * - * @return Array of 5 integers (sum equal numbers of rows) counting each - * filter use - */ - public int[] getFilterUseStat() { - return filterUseStat; - } - -} diff --git a/teavm/src/main/java/ar/com/hjg/pngj/ImageInfo.java b/teavm/src/main/java/ar/com/hjg/pngj/ImageInfo.java deleted file mode 100644 index 850ac2c0..00000000 --- a/teavm/src/main/java/ar/com/hjg/pngj/ImageInfo.java +++ /dev/null @@ -1,265 +0,0 @@ -package ar.com.hjg.pngj; - -import java.util.zip.Checksum; - -/** - * Simple immutable wrapper for basic image info. - *

- * Some parameters are redundant, but the constructor receives an 'orthogonal' - * subset. - *

- * ref: http://www.w3.org/TR/PNG/#11IHDR - */ -public class ImageInfo { - - /** - * Absolute allowed maximum value for rows and cols (2^24 ~16 million). - * (bytesPerRow must fit in a 32bit integer, - * though total amount of pixels not necessarily). - */ - public static final int MAX_COLS_ROW = 16777216; - - /** - * Cols= Image width, in pixels. - */ - public final int cols; - - /** - * Rows= Image height, in pixels - */ - public final int rows; - - /** - * Bits per sample (per channel) in the buffer (1-2-4-8-16). This is 8-16 - * for RGB/ARGB images, 1-2-4-8 for grayscale. - * For indexed images, number of bits per palette index (1-2-4-8) - */ - public final int bitDepth; - - /** - * Number of channels, as used internally: 3 for RGB, 4 for RGBA, 2 for GA - * (gray with alpha), 1 for grayscale or - * indexed. - */ - public final int channels; - - /** - * Flag: true if has alpha channel (RGBA/GA) - */ - public final boolean alpha; - - /** - * Flag: true if is grayscale (G/GA) - */ - public final boolean greyscale; - - /** - * Flag: true if image is indexed, i.e., it has a palette - */ - public final boolean indexed; - - /** - * Flag: true if image internally uses less than one byte per sample (bit - * depth 1-2-4) - */ - public final boolean packed; - - /** - * Bits used for each pixel in the buffer: channel * bitDepth - */ - public final int bitspPixel; - - /** - * rounded up value: this is only used internally for filter - */ - public final int bytesPixel; - - /** - * ceil(bitspp*cols/8) - does not include filter - */ - public final int bytesPerRow; - - /** - * Equals cols * channels - */ - public final int samplesPerRow; - - /** - * Amount of "packed samples" : when several samples are stored in a single - * byte (bitdepth 1,2 4) they are counted as - * one "packed sample". This is less that samplesPerRow only when bitdepth - * is 1-2-4 (flag packed = true) - *

- * This equals the number of elements in the scanline array if working with - * packedMode=true - *

- * For internal use, client code should rarely access this. - */ - public final int samplesPerRowPacked; - - private long totalPixels = -1; // lazy getter - - private long totalRawBytes = -1; // lazy getter - - /** - * Short constructor: assumes truecolor (RGB/RGBA) - */ - public ImageInfo(final int cols, final int rows, final int bitdepth, final boolean alpha) { - this(cols, rows, bitdepth, alpha, false, false); - } - - /** - * Full constructor - * - * @param cols - * Width in pixels - * @param rows - * Height in pixels - * @param bitdepth - * Bits per sample, in the buffer : 8-16 for RGB true color and - * greyscale - * @param alpha - * Flag: has an alpha channel (RGBA or GA) - * @param grayscale - * Flag: is gray scale (any bitdepth, with or without alpha) - * @param indexed - * Flag: has palette - */ - public ImageInfo(final int cols, final int rows, final int bitdepth, final boolean alpha, final boolean grayscale, final boolean indexed) { - this.cols = cols; - this.rows = rows; - this.alpha = alpha; - this.indexed = indexed; - greyscale = grayscale; - if (greyscale && indexed) - throw new PngjException("palette and greyscale are mutually exclusive"); - channels = grayscale || indexed ? alpha ? 2 : 1 : alpha ? 4 : 3; - // http://www.w3.org/TR/PNG/#11IHDR - bitDepth = bitdepth; - packed = bitdepth < 8; - bitspPixel = channels * bitDepth; - bytesPixel = (bitspPixel + 7) / 8; - bytesPerRow = (bitspPixel * cols + 7) / 8; - samplesPerRow = channels * this.cols; - samplesPerRowPacked = packed ? bytesPerRow : samplesPerRow; - // several checks - switch (bitDepth) { - case 1: - case 2: - case 4: - if (!(this.indexed || greyscale)) - throw new PngjException("only indexed or grayscale can have bitdepth=" + bitDepth); - break; - case 8: - break; - case 16: - if (this.indexed) - throw new PngjException("indexed can't have bitdepth=" + bitDepth); - break; - default: - throw new PngjException("invalid bitdepth=" + bitDepth); - } - if (cols < 1 || cols > ImageInfo.MAX_COLS_ROW) - throw new PngjException("invalid cols=" + cols + " ???"); - if (rows < 1 || rows > ImageInfo.MAX_COLS_ROW) - throw new PngjException("invalid rows=" + rows + " ???"); - if (samplesPerRow < 1) - throw new PngjException("invalid image parameters (overflow?)"); - } - - /** - * returns a copy with different size - * - * @param cols - * if non-positive, the original is used - * @param rows - * if non-positive, the original is used - * @return a new copy with the specified size and same properties - */ - public ImageInfo withSize(final int cols, final int rows) { - return new ImageInfo(cols > 0 ? cols : this.cols, rows > 0 ? rows : this.rows, bitDepth, alpha, greyscale, indexed); - } - - public long getTotalPixels() { - if (totalPixels < 0) - totalPixels = cols * (long) rows; - return totalPixels; - } - - /** - * Total uncompressed bytes in IDAT, including filter byte. This is not - * valid for interlaced. - */ - public long getTotalRawBytes() { - if (totalRawBytes < 0) - totalRawBytes = (bytesPerRow + 1) * (long) rows; - return totalRawBytes; - } - - @Override - public String toString() { - return "ImageInfo [cols=" + cols + ", rows=" + rows + ", bitDepth=" + bitDepth + ", channels=" + channels + ", alpha=" + alpha + ", greyscale=" + greyscale + ", indexed=" + indexed + "]"; - } - - /** - * Brief info: COLSxROWS[dBITDEPTH][a][p][g] ( the default dBITDEPTH='d8' is - * ommited) - **/ - public String toStringBrief() { - return String.valueOf(cols) + "x" + rows + (bitDepth != 8 ? "d" + bitDepth : "") + (alpha ? "a" : "") + (indexed ? "p" : "") + (greyscale ? "g" : ""); - } - - public String toStringDetail() { - return "ImageInfo [cols=" + cols + ", rows=" + rows + ", bitDepth=" + bitDepth + ", channels=" + channels + ", bitspPixel=" + bitspPixel + ", bytesPixel=" + bytesPixel + ", bytesPerRow=" + bytesPerRow + ", samplesPerRow=" + samplesPerRow + ", samplesPerRowP=" + samplesPerRowPacked + ", alpha=" + alpha + ", greyscale=" + greyscale + ", indexed=" + indexed + ", packed=" + packed + "]"; - } - - void updateCrc(final Checksum crc) { - crc.update((byte) rows); - crc.update((byte) (rows >> 8)); - crc.update((byte) (rows >> 16)); - crc.update((byte) cols); - crc.update((byte) (cols >> 8)); - crc.update((byte) (cols >> 16)); - crc.update((byte) bitDepth); - crc.update((byte) (indexed ? 1 : 2)); - crc.update((byte) (greyscale ? 3 : 4)); - crc.update((byte) (alpha ? 3 : 4)); - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + (alpha ? 1231 : 1237); - result = prime * result + bitDepth; - result = prime * result + cols; - result = prime * result + (greyscale ? 1231 : 1237); - result = prime * result + (indexed ? 1231 : 1237); - result = prime * result + rows; - return result; - } - - @Override - public boolean equals(final Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - final ImageInfo other = (ImageInfo) obj; - if (alpha != other.alpha) - return false; - if (bitDepth != other.bitDepth) - return false; - if (cols != other.cols) - return false; - if (greyscale != other.greyscale) - return false; - if (indexed != other.indexed) - return false; - if (rows != other.rows) - return false; - return true; - } -} diff --git a/teavm/src/main/java/ar/com/hjg/pngj/ImageLineByte.java b/teavm/src/main/java/ar/com/hjg/pngj/ImageLineByte.java deleted file mode 100644 index f883e9d2..00000000 --- a/teavm/src/main/java/ar/com/hjg/pngj/ImageLineByte.java +++ /dev/null @@ -1,191 +0,0 @@ -package ar.com.hjg.pngj; - -/** - * Lightweight wrapper for an image scanline, used for read and write. - *

- * This object can be (usually it is) reused while iterating over the image - * lines. - *

- * See scanline field, to understand the format. - * - * Format: byte (one bytes per sample) (for 16bpp the extra byte is placed in an - * extra array) - */ -public class ImageLineByte implements IImageLine, IImageLineArray { - public final ImageInfo imgInfo; - - final byte[] scanline; - final byte[] scanline2; // only used for 16 bpp (less significant byte) Normally you'd prefer - // ImageLineInt in this case - - protected FilterType filterType; // informational ; only filled by the reader. not significant for - // interlaced - final int size; // = imgInfo.samplePerRowPacked, if packed:imgInfo.samplePerRow elswhere - - public ImageLineByte(final ImageInfo imgInfo) { - this(imgInfo, null); - } - - public ImageLineByte(final ImageInfo imgInfo, final byte[] sci) { - this.imgInfo = imgInfo; - filterType = FilterType.FILTER_UNKNOWN; - size = imgInfo.samplesPerRow; - scanline = sci != null && sci.length >= size ? sci : new byte[size]; - scanline2 = imgInfo.bitDepth == 16 ? new byte[size] : null; - } - - /** - * Returns a factory for this object - */ - public static IImageLineFactory getFactory() { - return iminfo -> new ImageLineByte(iminfo); - } - - public FilterType getFilterUsed() { - return filterType; - } - - /** - * One byte per sample. This can be used also for 16bpp images, but in this - * case this loses the less significant - * 8-bits ; see also getScanlineByte2 and getElem. - */ - public byte[] getScanlineByte() { - return scanline; - } - - /** - * only for 16bpp (less significant byte) - * - * @return null for less than 16bpp - */ - public byte[] getScanlineByte2() { - return scanline2; - } - - /** - * Basic info - */ - @Override - public String toString() { - return " cols=" + imgInfo.cols + " bpc=" + imgInfo.bitDepth + " size=" + scanline.length; - } - - @Override - public void readFromPngRaw(final byte[] raw, final int len, final int offset, final int step) { - filterType = FilterType.getByVal(raw[0]); // only for non interlaced line the filter is significative - final int len1 = len - 1; - final int step1 = (step - 1) * imgInfo.channels; - if (imgInfo.bitDepth == 8) { - if (step == 1) - System.arraycopy(raw, 1, scanline, 0, len1); - else - for (int s = 1, c = 0, i = offset * imgInfo.channels; s <= len1; s++, i++) { - scanline[i] = raw[s]; - c++; - if (c == imgInfo.channels) { - c = 0; - i += step1; - } - } - } else if (imgInfo.bitDepth == 16) { - if (step == 1) - for (int i = 0, s = 1; i < imgInfo.samplesPerRow; i++) { - scanline[i] = raw[s++]; // get the first byte - scanline2[i] = raw[s++]; // get the first byte - } - else - for (int s = 1, c = 0, i = offset != 0 ? offset * imgInfo.channels : 0; s <= len1; i++) { - scanline[i] = raw[s++]; - scanline2[i] = raw[s++]; - c++; - if (c == imgInfo.channels) { - c = 0; - i += step1; - } - } - } else { // packed formats - int mask0, mask, shi, bd; - bd = imgInfo.bitDepth; - mask0 = ImageLineHelper.getMaskForPackedFormats(bd); - for (int i = offset * imgInfo.channels, r = 1, c = 0; r < len; r++) { - mask = mask0; - shi = 8 - bd; - do { - scanline[i] = (byte) ((raw[r] & mask) >> shi); - mask >>= bd; - shi -= bd; - i++; - c++; - if (c == imgInfo.channels) { - c = 0; - i += step1; - } - } while (mask != 0 && i < size); - } - } - } - - @Override - public void writeToPngRaw(final byte[] raw) { - raw[0] = (byte) filterType.val; - if (imgInfo.bitDepth == 8) - System.arraycopy(scanline, 0, raw, 1, size); - else if (imgInfo.bitDepth == 16) - for (int i = 0, s = 1; i < size; i++) { - raw[s++] = scanline[i]; - raw[s++] = scanline2[i]; - } - else { // packed formats - int shi, bd, v; - bd = imgInfo.bitDepth; - shi = 8 - bd; - v = 0; - for (int i = 0, r = 1; i < size; i++) { - v |= scanline[i] << shi; - shi -= bd; - if (shi < 0 || i == size - 1) { - raw[r++] = (byte) v; - shi = 8 - bd; - v = 0; - } - } - } - } - - @Override - public void endReadFromPngRaw() {} - - @Override - public int getSize() { - return size; - } - - @Override - public int getElem(final int i) { - return scanline2 == null ? scanline[i] & 0xFF : (scanline[i] & 0xFF) << 8 | scanline2[i] & 0xFF; - } - - public byte[] getScanline() { - return scanline; - } - - @Override - public ImageInfo getImageInfo() { - return imgInfo; - } - - @Override - public FilterType getFilterType() { - return filterType; - } - - /** - * This should rarely be used by client code. Only relevant if - * FilterPreserve==true - */ - public void setFilterType(final FilterType ft) { - filterType = ft; - } - -} diff --git a/teavm/src/main/java/ar/com/hjg/pngj/ImageLineHelper.java b/teavm/src/main/java/ar/com/hjg/pngj/ImageLineHelper.java deleted file mode 100644 index 6deb44bc..00000000 --- a/teavm/src/main/java/ar/com/hjg/pngj/ImageLineHelper.java +++ /dev/null @@ -1,469 +0,0 @@ -package ar.com.hjg.pngj; - -import java.util.Arrays; - -import ar.com.hjg.pngj.chunks.PngChunkPLTE; -import ar.com.hjg.pngj.chunks.PngChunkTRNS; - -/** - * Bunch of utility static methods to proces an image line at the pixel level. - *

- * WARNING: this has little testing/optimizing, and this API is not stable. some - * methods will probably be changed or - * removed if future releases. - *

- * WARNING: most methods for getting/setting values work currently only for - * ImageLine or ImageLineByte - */ -public class ImageLineHelper { - - static int[] DEPTH_UNPACK_1; - static int[] DEPTH_UNPACK_2; - static int[] DEPTH_UNPACK_4; - static int[][] DEPTH_UNPACK; - - static { - ImageLineHelper.initDepthScale(); - } - - private static void initDepthScale() { - ImageLineHelper.DEPTH_UNPACK_1 = new int[2]; - for (int i = 0; i < 2; i++) - ImageLineHelper.DEPTH_UNPACK_1[i] = i * 255; - ImageLineHelper.DEPTH_UNPACK_2 = new int[4]; - for (int i = 0; i < 4; i++) - ImageLineHelper.DEPTH_UNPACK_2[i] = i * 255 / 3; - ImageLineHelper.DEPTH_UNPACK_4 = new int[16]; - for (int i = 0; i < 16; i++) - ImageLineHelper.DEPTH_UNPACK_4[i] = i * 255 / 15; - ImageLineHelper.DEPTH_UNPACK = new int[][] { null, ImageLineHelper.DEPTH_UNPACK_1, ImageLineHelper.DEPTH_UNPACK_2, null, ImageLineHelper.DEPTH_UNPACK_4 }; - } - - /** - * When the bitdepth is less than 8, the imageLine is usually - * returned/expected unscaled. This method upscales it in - * place. Eg, if bitdepth=1, values 0-1 will be converted to 0-255 - */ - public static void scaleUp(final IImageLineArray line) { - if (line.getImageInfo().indexed || line.getImageInfo().bitDepth >= 8) - return; - final int[] scaleArray = ImageLineHelper.DEPTH_UNPACK[line.getImageInfo().bitDepth]; - if (line instanceof ImageLineInt) { - final ImageLineInt iline = (ImageLineInt) line; - for (int i = 0; i < iline.getSize(); i++) - iline.scanline[i] = scaleArray[iline.scanline[i]]; - } else if (line instanceof ImageLineByte) { - final ImageLineByte iline = (ImageLineByte) line; - for (int i = 0; i < iline.getSize(); i++) - iline.scanline[i] = (byte) scaleArray[iline.scanline[i]]; - } else - throw new PngjException("not implemented"); - } - - /** - * Reverse of {@link #scaleUp(IImageLineArray)} - */ - public static void scaleDown(final IImageLineArray line) { - if (line.getImageInfo().indexed || line.getImageInfo().bitDepth >= 8) - return; - if (line instanceof ImageLineInt) { - final int scalefactor = 8 - line.getImageInfo().bitDepth; - if (line instanceof ImageLineInt) { - final ImageLineInt iline = (ImageLineInt) line; - for (int i = 0; i < line.getSize(); i++) - iline.scanline[i] = iline.scanline[i] >> scalefactor; - } else if (line instanceof ImageLineByte) { - final ImageLineByte iline = (ImageLineByte) line; - for (int i = 0; i < line.getSize(); i++) - iline.scanline[i] = (byte) ((iline.scanline[i] & 0xFF) >> scalefactor); - } - } else - throw new PngjException("not implemented"); - } - - public static byte scaleUp(final int bitdepth, final byte v) { - return bitdepth < 8 ? (byte) ImageLineHelper.DEPTH_UNPACK[bitdepth][v] : v; - } - - public static byte scaleDown(final int bitdepth, final byte v) { - return bitdepth < 8 ? (byte) (v >> 8 - bitdepth) : v; - } - - /** - * Given an indexed line with a palette, unpacks as a RGB array, or RGBA if - * a non nul PngChunkTRNS chunk is passed - * - * @param line - * ImageLine as returned from PngReader - * @param pal - * Palette chunk - * @param trns - * Transparency chunk, can be null (absent) - * @param buf - * Preallocated array, optional - * @return R G B (A), one sample 0-255 per array element. Ready for - * pngw.writeRowInt() - */ - public static int[] palette2rgb(final ImageLineInt line, final PngChunkPLTE pal, final PngChunkTRNS trns, - final int[] buf) { - return ImageLineHelper.palette2rgb(line, pal, trns, buf, false); - } - - /** - * Warning: the line should be upscaled, see - * {@link #scaleUp(IImageLineArray)} - */ - static int[] lineToARGB32(final ImageLineByte line, final PngChunkPLTE pal, final PngChunkTRNS trns, int[] buf) { - final boolean alphachannel = line.imgInfo.alpha; - final int cols = line.getImageInfo().cols; - if (buf == null || buf.length < cols) - buf = new int[cols]; - int index, rgb, alpha, ga, g; - if (line.getImageInfo().indexed) {// palette - final int nindexesWithAlpha = trns != null ? trns.getPalletteAlpha().length : 0; - for (int c = 0; c < cols; c++) { - index = line.scanline[c] & 0xFF; - rgb = pal.getEntry(index); - alpha = index < nindexesWithAlpha ? trns.getPalletteAlpha()[index] : 255; - buf[c] = alpha << 24 | rgb; - } - } else if (line.imgInfo.greyscale) { // gray - ga = trns != null ? trns.getGray() : -1; - for (int c = 0, c2 = 0; c < cols; c++) { - g = line.scanline[c2++] & 0xFF; - alpha = alphachannel ? line.scanline[c2++] & 0xFF : g != ga ? 255 : 0; - buf[c] = alpha << 24 | g | g << 8 | g << 16; - } - } else { // true color - ga = trns != null ? trns.getRGB888() : -1; - for (int c = 0, c2 = 0; c < cols; c++) { - rgb = (line.scanline[c2++] & 0xFF) << 16 | (line.scanline[c2++] & 0xFF) << 8 | line.scanline[c2++] & 0xFF; - alpha = alphachannel ? line.scanline[c2++] & 0xFF : rgb != ga ? 255 : 0; - buf[c] = alpha << 24 | rgb; - } - } - return buf; - } - - /** - * Warning: the line should be upscaled, see - * {@link #scaleUp(IImageLineArray)} - */ - static byte[] lineToRGBA8888(final ImageLineByte line, final PngChunkPLTE pal, final PngChunkTRNS trns, - byte[] buf) { - final boolean alphachannel = line.imgInfo.alpha; - final int cols = line.imgInfo.cols; - final int bytes = cols * 4; - if (buf == null || buf.length < bytes) - buf = new byte[bytes]; - int index, rgb, ga; - byte val; - if (line.imgInfo.indexed) {// palette - final int nindexesWithAlpha = trns != null ? trns.getPalletteAlpha().length : 0; - for (int c = 0, b = 0; c < cols; c++) { - index = line.scanline[c] & 0xFF; - rgb = pal.getEntry(index); - buf[b++] = (byte) (rgb >> 16 & 0xFF); - buf[b++] = (byte) (rgb >> 8 & 0xFF); - buf[b++] = (byte) (rgb & 0xFF); - buf[b++] = (byte) (index < nindexesWithAlpha ? trns.getPalletteAlpha()[index] : 255); - } - } else if (line.imgInfo.greyscale) { // - ga = trns != null ? trns.getGray() : -1; - for (int c = 0, b = 0; b < bytes;) { - val = line.scanline[c++]; - buf[b++] = val; - buf[b++] = val; - buf[b++] = val; - buf[b++] = alphachannel ? line.scanline[c++] : (val & 0xFF) == ga ? (byte) 0 : (byte) 255; - } - } else if (alphachannel) // same format! - System.arraycopy(line.scanline, 0, buf, 0, bytes); - else - for (int c = 0, b = 0; b < bytes;) { - buf[b++] = line.scanline[c++]; - buf[b++] = line.scanline[c++]; - buf[b++] = line.scanline[c++]; - buf[b++] = (byte) 255; // tentative (probable) - if (trns != null && buf[b - 3] == (byte) trns.getRGB()[0] && buf[b - 2] == (byte) trns.getRGB()[1] && buf[b - 1] == (byte) trns.getRGB()[2]) // not - // very - // efficient, - // but - // not - // frecuent - buf[b - 1] = 0; - } - return buf; - } - - static byte[] lineToRGB888(final ImageLineByte line, final PngChunkPLTE pal, byte[] buf) { - final boolean alphachannel = line.imgInfo.alpha; - final int cols = line.imgInfo.cols; - final int bytes = cols * 3; - if (buf == null || buf.length < bytes) - buf = new byte[bytes]; - byte val; - final int[] rgb = new int[3]; - if (line.imgInfo.indexed) - for (int c = 0, b = 0; c < cols; c++) { - pal.getEntryRgb(line.scanline[c] & 0xFF, rgb); - buf[b++] = (byte) rgb[0]; - buf[b++] = (byte) rgb[1]; - buf[b++] = (byte) rgb[2]; - } - else if (line.imgInfo.greyscale) - for (int c = 0, b = 0; b < bytes;) { - val = line.scanline[c++]; - buf[b++] = val; - buf[b++] = val; - buf[b++] = val; - if (alphachannel) - c++; // skip alpha - } - else if (!alphachannel) // same format! - System.arraycopy(line.scanline, 0, buf, 0, bytes); - else - for (int c = 0, b = 0; b < bytes;) { - buf[b++] = line.scanline[c++]; - buf[b++] = line.scanline[c++]; - buf[b++] = line.scanline[c++]; - c++;// skip alpha - } - return buf; - } - - /** - * Same as palette2rgbx , but returns rgba always, even if trns is null - * - * @param line - * ImageLine as returned from PngReader - * @param pal - * Palette chunk - * @param trns - * Transparency chunk, can be null (absent) - * @param buf - * Preallocated array, optional - * @return R G B (A), one sample 0-255 per array element. Ready for - * pngw.writeRowInt() - */ - public static int[] palette2rgba(final ImageLineInt line, final PngChunkPLTE pal, final PngChunkTRNS trns, - final int[] buf) { - return ImageLineHelper.palette2rgb(line, pal, trns, buf, true); - } - - public static int[] palette2rgb(final ImageLineInt line, final PngChunkPLTE pal, final int[] buf) { - return ImageLineHelper.palette2rgb(line, pal, null, buf, false); - } - - /** this is not very efficient, only for tests and troubleshooting */ - public static int[] convert2rgba(final IImageLineArray line, final PngChunkPLTE pal, final PngChunkTRNS trns, - int[] buf) { - final ImageInfo imi = line.getImageInfo(); - final int nsamples = imi.cols * 4; - if (buf == null || buf.length < nsamples) - buf = new int[nsamples]; - final int maxval = imi.bitDepth == 16 ? (1 << 16) - 1 : 255; - Arrays.fill(buf, maxval); - - if (imi.indexed) { - final int tlen = trns != null ? trns.getPalletteAlpha().length : 0; - for (int s = 0; s < imi.cols; s++) { - final int index = line.getElem(s); - pal.getEntryRgb(index, buf, s * 4); - if (index < tlen) - buf[s * 4 + 3] = trns.getPalletteAlpha()[index]; - } - } else if (imi.greyscale) { - int[] unpack = null; - if (imi.bitDepth < 8) - unpack = ImageLineHelper.DEPTH_UNPACK[imi.bitDepth]; - for (int s = 0, i = 0, p = 0; p < imi.cols; p++) { - buf[s++] = unpack != null ? unpack[line.getElem(i++)] : line.getElem(i++); - buf[s] = buf[s - 1]; - s++; - buf[s] = buf[s - 1]; - s++; - if (imi.channels == 2) - buf[s++] = unpack != null ? unpack[line.getElem(i++)] : line.getElem(i++); - else - buf[s++] = maxval; - } - } else - for (int s = 0, i = 0, p = 0; p < imi.cols; p++) { - buf[s++] = line.getElem(i++); - buf[s++] = line.getElem(i++); - buf[s++] = line.getElem(i++); - buf[s++] = imi.alpha ? line.getElem(i++) : maxval; - } - return buf; - } - - private static int[] palette2rgb(final IImageLine line, final PngChunkPLTE pal, final PngChunkTRNS trns, int[] buf, - final boolean alphaForced) { - final boolean isalpha = trns != null; - final int channels = isalpha ? 4 : 3; - final ImageLineInt linei = (ImageLineInt) (line instanceof ImageLineInt ? line : null); - final ImageLineByte lineb = (ImageLineByte) (line instanceof ImageLineByte ? line : null); - final boolean isbyte = lineb != null; - final int cols = linei != null ? linei.imgInfo.cols : lineb.imgInfo.cols; - final int nsamples = cols * channels; - if (buf == null || buf.length < nsamples) - buf = new int[nsamples]; - final int nindexesWithAlpha = trns != null ? trns.getPalletteAlpha().length : 0; - for (int c = 0; c < cols; c++) { - final int index = isbyte ? lineb.scanline[c] & 0xFF : linei.scanline[c]; - pal.getEntryRgb(index, buf, c * channels); - if (isalpha) { - final int alpha = index < nindexesWithAlpha ? trns.getPalletteAlpha()[index] : 255; - buf[c * channels + 3] = alpha; - } - } - return buf; - } - - /** - * what follows is pretty uninteresting/untested/obsolete, subject to change - */ - /** - * Just for basic info or debugging. Shows values for first and last pixel. - * Does not include alpha - */ - public static String infoFirstLastPixels(final ImageLineInt line) { - return line.imgInfo.channels == 1 ? String.format("first=(%d) last=(%d)", line.scanline[0], line.scanline[line.scanline.length - 1]) : String.format("first=(%d %d %d) last=(%d %d %d)", line.scanline[0], line.scanline[1], line.scanline[2], line.scanline[line.scanline.length - line.imgInfo.channels], line.scanline[line.scanline.length - line.imgInfo.channels + 1], line.scanline[line.scanline.length - line.imgInfo.channels + 2]); - } - - /** - * integer packed R G B only for bitdepth=8! (does not check!) - * - **/ - public static int getPixelRGB8(final IImageLine line, final int column) { - if (line instanceof ImageLineInt) { - final int offset = column * ((ImageLineInt) line).imgInfo.channels; - final int[] scanline = ((ImageLineInt) line).getScanline(); - return scanline[offset] << 16 | scanline[offset + 1] << 8 | scanline[offset + 2]; - } else if (line instanceof ImageLineByte) { - final int offset = column * ((ImageLineByte) line).imgInfo.channels; - final byte[] scanline = ((ImageLineByte) line).getScanline(); - return (scanline[offset] & 0xff) << 16 | (scanline[offset + 1] & 0xff) << 8 | scanline[offset + 2] & 0xff; - } else - throw new PngjException("Not supported " + line.getClass()); - } - - public static int getPixelARGB8(final IImageLine line, final int column) { - if (line instanceof ImageLineInt) { - final int offset = column * ((ImageLineInt) line).imgInfo.channels; - final int[] scanline = ((ImageLineInt) line).getScanline(); - return scanline[offset + 3] << 24 | scanline[offset] << 16 | scanline[offset + 1] << 8 | scanline[offset + 2]; - } else if (line instanceof ImageLineByte) { - final int offset = column * ((ImageLineByte) line).imgInfo.channels; - final byte[] scanline = ((ImageLineByte) line).getScanline(); - return (scanline[offset + 3] & 0xff) << 24 | (scanline[offset] & 0xff) << 16 | (scanline[offset + 1] & 0xff) << 8 | scanline[offset + 2] & 0xff; - } else - throw new PngjException("Not supported " + line.getClass()); - } - - public static void setPixelsRGB8(final ImageLineInt line, final int[] rgb) { - for (int i = 0, j = 0; i < line.imgInfo.cols; i++) { - line.scanline[j++] = rgb[i] >> 16 & 0xFF; - line.scanline[j++] = rgb[i] >> 8 & 0xFF; - line.scanline[j++] = rgb[i] & 0xFF; - } - } - - public static void setPixelRGB8(final ImageLineInt line, int col, final int r, final int g, final int b) { - col *= line.imgInfo.channels; - line.scanline[col++] = r; - line.scanline[col++] = g; - line.scanline[col] = b; - } - - public static void setPixelRGB8(final ImageLineInt line, final int col, final int rgb) { - ImageLineHelper.setPixelRGB8(line, col, rgb >> 16 & 0xFF, rgb >> 8 & 0xFF, rgb & 0xFF); - } - - public static void setPixelsRGBA8(final ImageLineInt line, final int[] rgb) { - for (int i = 0, j = 0; i < line.imgInfo.cols; i++) { - line.scanline[j++] = rgb[i] >> 16 & 0xFF; - line.scanline[j++] = rgb[i] >> 8 & 0xFF; - line.scanline[j++] = rgb[i] & 0xFF; - line.scanline[j++] = rgb[i] >> 24 & 0xFF; - } - } - - public static void setPixelRGBA8(final ImageLineInt line, int col, final int r, final int g, final int b, - final int a) { - col *= line.imgInfo.channels; - line.scanline[col++] = r; - line.scanline[col++] = g; - line.scanline[col++] = b; - line.scanline[col] = a; - } - - public static void setPixelRGBA8(final ImageLineInt line, final int col, final int rgb) { - ImageLineHelper.setPixelRGBA8(line, col, rgb >> 16 & 0xFF, rgb >> 8 & 0xFF, rgb & 0xFF, rgb >> 24 & 0xFF); - } - - public static void setValD(final ImageLineInt line, final int i, final double d) { - line.scanline[i] = ImageLineHelper.double2int(line, d); - } - - public static int interpol(final int a, final int b, final int c, final int d, final double dx, final double dy) { - // a b -> x (0-1) - // c d - final double e = a * (1.0 - dx) + b * dx; - final double f = c * (1.0 - dx) + d * dx; - return (int) (e * (1 - dy) + f * dy + 0.5); - } - - public static double int2double(final ImageLineInt line, final int p) { - return line.imgInfo.bitDepth == 16 ? p / 65535.0 : p / 255.0; - // TODO: replace my multiplication? check for other bitdepths - } - - public static double int2doubleClamped(final ImageLineInt line, final int p) { - // TODO: replace my multiplication? - final double d = line.imgInfo.bitDepth == 16 ? p / 65535.0 : p / 255.0; - return d <= 0.0 ? 0 : d >= 1.0 ? 1.0 : d; - } - - public static int double2int(final ImageLineInt line, double d) { - d = d <= 0.0 ? 0 : d >= 1.0 ? 1.0 : d; - return line.imgInfo.bitDepth == 16 ? (int) (d * 65535.0 + 0.5) : (int) (d * 255.0 + 0.5); // - } - - public static int double2intClamped(final ImageLineInt line, double d) { - d = d <= 0.0 ? 0 : d >= 1.0 ? 1.0 : d; - return line.imgInfo.bitDepth == 16 ? (int) (d * 65535.0 + 0.5) : (int) (d * 255.0 + 0.5); // - } - - public static int clampTo_0_255(final int i) { - return i > 255 ? 255 : i < 0 ? 0 : i; - } - - public static int clampTo_0_65535(final int i) { - return i > 65535 ? 65535 : i < 0 ? 0 : i; - } - - public static int clampTo_128_127(final int x) { - return x > 127 ? 127 : x < -128 ? -128 : x; - } - - public static int getMaskForPackedFormats(final int bitDepth) { // Utility function for pack/unpack - if (bitDepth == 4) - return 0xf0; - else if (bitDepth == 2) - return 0xc0; - else - return 0x80; // bitDepth == 1 - } - - public static int getMaskForPackedFormatsLs(final int bitDepth) { // Utility function for pack/unpack - if (bitDepth == 4) - return 0x0f; - else if (bitDepth == 2) - return 0x03; - else - return 0x01; // bitDepth == 1 - } - -} diff --git a/teavm/src/main/java/ar/com/hjg/pngj/ImageLineInt.java b/teavm/src/main/java/ar/com/hjg/pngj/ImageLineInt.java deleted file mode 100644 index 310ec132..00000000 --- a/teavm/src/main/java/ar/com/hjg/pngj/ImageLineInt.java +++ /dev/null @@ -1,203 +0,0 @@ -package ar.com.hjg.pngj; - -/** - * Represents an image line, integer format (one integer by sample). See - * {@link #scanline} to understand the format. - */ -public class ImageLineInt implements IImageLine, IImageLineArray { - public final ImageInfo imgInfo; - - /** - * The 'scanline' is an array of integers, corresponds to an image line - * (row). - *

- * Each int is a "sample" (one for channel), (0-255 or 0-65535) - * in the corresponding PNG sequence: - * R G B R G B... or R G B A R G B A... - * or g g g ... or i i i (palette index) - *

- * For bitdepth=1/2/4 the value is not scaled (hence, eg, if bitdepth=2 the - * range will be 0-4) - *

- * To convert a indexed line to RGB values, see - * {@link ImageLineHelper#palette2rgb(ImageLineInt, ar.com.hjg.pngj.chunks.PngChunkPLTE, int[])} - * (you can't do the - * reverse) - */ - protected final int[] scanline; - - /** - * number of elements in the scanline - */ - protected final int size; - - /** - * informational ; only filled by the reader. not meaningful for interlaced - */ - protected FilterType filterType = FilterType.FILTER_UNKNOWN; - - /** - * @param imgInfo - * Inmutable ImageInfo, basic parameters of the image we are - * reading or writing - */ - public ImageLineInt(final ImageInfo imgInfo) { - this(imgInfo, null); - } - - /** - * @param imgInfo - * Inmutable ImageInfo, basic parameters of the image we are - * reading or writing - * @param sci - * prealocated buffer (can be null) - */ - public ImageLineInt(final ImageInfo imgInfo, final int[] sci) { - this.imgInfo = imgInfo; - filterType = FilterType.FILTER_UNKNOWN; - size = imgInfo.samplesPerRow; - scanline = sci != null && sci.length >= size ? sci : new int[size]; - } - - /** - * Helper method, returns a default factory for this object - * - */ - public static IImageLineFactory getFactory() { - return iminfo -> new ImageLineInt(iminfo); - } - - @Override - public FilterType getFilterType() { - return filterType; - } - - /** - * This should rarely be used by client code. Only relevant if - * FilterPreserve==true - */ - public void setFilterType(final FilterType ft) { - filterType = ft; - } - - /** - * Basic info - */ - @Override - public String toString() { - return " cols=" + imgInfo.cols + " bpc=" + imgInfo.bitDepth + " size=" + scanline.length; - } - - @Override - public void readFromPngRaw(final byte[] raw, final int len, final int offset, final int step) { - setFilterType(FilterType.getByVal(raw[0])); - final int len1 = len - 1; - final int step1 = (step - 1) * imgInfo.channels; - if (imgInfo.bitDepth == 8) { - if (step == 1) - for (int i = 0; i < size; i++) - scanline[i] = raw[i + 1] & 0xff; - else - for (int s = 1, c = 0, i = offset * imgInfo.channels; s <= len1; s++, i++) { - scanline[i] = raw[s] & 0xff; - c++; - if (c == imgInfo.channels) { - c = 0; - i += step1; - } - } - } else if (imgInfo.bitDepth == 16) { - if (step == 1) - for (int i = 0, s = 1; i < size; i++) - scanline[i] = (raw[s++] & 0xFF) << 8 | raw[s++] & 0xFF; // 16 bitspc - else - for (int s = 1, c = 0, i = offset != 0 ? offset * imgInfo.channels : 0; s <= len1; s++, i++) { - scanline[i] = (raw[s++] & 0xFF) << 8 | raw[s] & 0xFF; // 16 bitspc - c++; - if (c == imgInfo.channels) { - c = 0; - i += step1; - } - } - } else { // packed formats - int mask0, mask, shi, bd; - bd = imgInfo.bitDepth; - mask0 = ImageLineHelper.getMaskForPackedFormats(bd); - for (int i = offset * imgInfo.channels, r = 1, c = 0; r < len; r++) { - mask = mask0; - shi = 8 - bd; - do { - scanline[i++] = (raw[r] & mask) >> shi; - mask >>= bd; - shi -= bd; - c++; - if (c == imgInfo.channels) { - c = 0; - i += step1; - } - } while (mask != 0 && i < size); - } - } - } - - @Override - public void writeToPngRaw(final byte[] raw) { - raw[0] = (byte) filterType.val; - if (imgInfo.bitDepth == 8) - for (int i = 0; i < size; i++) - raw[i + 1] = (byte) scanline[i]; - else if (imgInfo.bitDepth == 16) - for (int i = 0, s = 1; i < size; i++) { - raw[s++] = (byte) (scanline[i] >> 8); - raw[s++] = (byte) (scanline[i] & 0xff); - } - else { // packed formats - int shi, bd, v; - bd = imgInfo.bitDepth; - shi = 8 - bd; - v = 0; - for (int i = 0, r = 1; i < size; i++) { - v |= scanline[i] << shi; - shi -= bd; - if (shi < 0 || i == size - 1) { - raw[r++] = (byte) v; - shi = 8 - bd; - v = 0; - } - } - } - } - - /** - * Does nothing in this implementation - */ - @Override - public void endReadFromPngRaw() { - - } - - /** - * @see #size - */ - @Override - public int getSize() { - return size; - } - - @Override - public int getElem(final int i) { - return scanline[i]; - } - - /** - * @return see {@link #scanline} - */ - public int[] getScanline() { - return scanline; - } - - @Override - public ImageInfo getImageInfo() { - return imgInfo; - } -} diff --git a/teavm/src/main/java/ar/com/hjg/pngj/ImageLineSetDefault.java b/teavm/src/main/java/ar/com/hjg/pngj/ImageLineSetDefault.java deleted file mode 100644 index d1a1ed23..00000000 --- a/teavm/src/main/java/ar/com/hjg/pngj/ImageLineSetDefault.java +++ /dev/null @@ -1,159 +0,0 @@ -package ar.com.hjg.pngj; - -import java.util.ArrayList; -import java.util.List; - -/** - * Default implementation of {@link IImageLineSet}. - *

- * Supports all modes: single cursor, full rows, or partial. This should not be - * used for - */ -public abstract class ImageLineSetDefault implements IImageLineSet { - - protected final ImageInfo imgInfo; - private final boolean singleCursor; - private final int nlines, offset, step; - protected List imageLines; // null if single cursor - protected T imageLine; // null unless single cursor - protected int currentRow = -1; // only relevant (and not much) for cursor - - public ImageLineSetDefault(final ImageInfo imgInfo, final boolean singleCursor, final int nlinesx, final int noffsetx, final int stepx) { - this.imgInfo = imgInfo; - this.singleCursor = singleCursor; - if (singleCursor) { - this.nlines = 1; // we store only one line, no matter how many will be read - offset = 0; - this.step = 1;// don't matter - } else { - this.nlines = nlinesx; // note that it can also be 1 - offset = noffsetx; - this.step = stepx;// don't matter - } - createImageLines(); - } - - private void createImageLines() { - if (singleCursor) - imageLine = createImageLine(); - else { - imageLines = new ArrayList<>(); - for (int i = 0; i < nlines; i++) - imageLines.add(createImageLine()); - } - } - - protected abstract T createImageLine(); - - /** - * Retrieves the image line - *

- * Warning: the argument is the row number in the original image - *

- * If this is a cursor, no check is done, always the same row is returned - */ - @Override - public T getImageLine(final int n) { - currentRow = n; - if (singleCursor) - return imageLine; - else { - final int r = imageRowToMatrixRowStrict(n); - if (r < 0) - throw new PngjException("Invalid row number"); - return imageLines.get(r); - } - } - - /** - * does not check for valid range - */ - @Override - public T getImageLineRawNum(final int r) { - if (singleCursor) - return imageLine; - else - return imageLines.get(r); - } - - /** - * True if the set contains this image line - *

- * Warning: the argument is the row number in the original image - *

- * If this works as cursor, this returns true only if that is the number of - * its "current" line - */ - @Override - public boolean hasImageLine(final int n) { - return singleCursor ? currentRow == n : imageRowToMatrixRowStrict(n) >= 0; - } - - /** - * How many lines does this object contain? - */ - @Override - public int size() { - return nlines; - } - - /** - * Same as {@link #imageRowToMatrixRow(int)}, but returns negative if - * invalid - */ - public int imageRowToMatrixRowStrict(int imrow) { - imrow -= offset; - final int mrow = imrow >= 0 && (step == 1 || imrow % step == 0) ? imrow / step : -1; - return mrow < nlines ? mrow : -1; - } - - /** - * Converts from matrix row number (0 : nRows-1) to image row number - * - * @param mrow - * Matrix row number - * @return Image row number. Returns trash if mrow is invalid - */ - public int matrixRowToImageRow(final int mrow) { - return mrow * step + offset; - } - - /** - * Converts from real image row to this object row number. - *

- * Warning: this always returns a valid matrix row (clamping on 0 : nrows-1, - * and rounding down) - *

- * Eg: rowOffset=4,rowStep=2 imageRowToMatrixRow(17) returns 6 , - * imageRowToMatrixRow(1) returns 0 - */ - public int imageRowToMatrixRow(final int imrow) { - final int r = (imrow - offset) / step; - return r < 0 ? 0 : r < nlines ? r : nlines - 1; - } - - /** - * utility function, given a factory for one line, returns a factory for a - * set - */ - public static IImageLineSetFactory createImageLineSetFactoryFromImageLineFactory( - final IImageLineFactory ifactory) { // ugly method must have ugly name. don't let this intimidate you - return (iminfo, singleCursor, nlines, noffset, - step) -> new ImageLineSetDefault(iminfo, singleCursor, nlines, noffset, step) { - @Override - protected T createImageLine() { - return ifactory.createImageLine(iminfo); - } - }; - } - - /** utility function, returns default factory for {@link ImageLineInt} */ - public static IImageLineSetFactory getFactoryInt() { - return ImageLineSetDefault.createImageLineSetFactoryFromImageLineFactory(ImageLineInt.getFactory()); - } - - /** utility function, returns default factory for {@link ImageLineByte} */ - public static IImageLineSetFactory getFactoryByte() { - return ImageLineSetDefault.createImageLineSetFactoryFromImageLineFactory(ImageLineByte.getFactory()); - } -} diff --git a/teavm/src/main/java/ar/com/hjg/pngj/PngHelperInternal.java b/teavm/src/main/java/ar/com/hjg/pngj/PngHelperInternal.java deleted file mode 100644 index 79be507b..00000000 --- a/teavm/src/main/java/ar/com/hjg/pngj/PngHelperInternal.java +++ /dev/null @@ -1,332 +0,0 @@ -package ar.com.hjg.pngj; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.nio.charset.Charset; -import java.util.logging.Logger; - -/** - * Some utility static methods for internal use. - *

- * Client code should not normally use this class - *

- */ -public final class PngHelperInternal { - - public static final String KEY_LOGGER = "ar.com.pngj"; - public static final Logger LOGGER = Logger.getLogger(PngHelperInternal.KEY_LOGGER); - - /** - * Default charset, used internally by PNG for several things - */ - public static String charsetLatin1name = "UTF-8"; - public static Charset charsetLatin1 = Charset.forName(PngHelperInternal.charsetLatin1name); - /** - * UTF-8 is only used for some chunks - */ - public static String charsetUTF8name = "UTF-8"; - public static Charset charsetUTF8 = Charset.forName(PngHelperInternal.charsetUTF8name); - - private static ThreadLocal DEBUG = new ThreadLocal() { - @Override - protected Boolean initialValue() { - return Boolean.FALSE; - } - }; - - /** - * PNG magic bytes - */ - public static byte[] getPngIdSignature() { - return new byte[] { -119, 80, 78, 71, 13, 10, 26, 10 }; - } - - public static int doubleToInt100000(final double d) { - return (int) (d * 100000.0 + 0.5); - } - - public static double intToDouble100000(final int i) { - return i / 100000.0; - } - - public static int readByte(final InputStream is) { - try { - return is.read(); - } catch (final IOException e) { - throw new PngjInputException("error reading byte", e); - } - } - - /** - * -1 if eof - * - * PNG uses "network byte order" - */ - public static int readInt2(final InputStream is) { - try { - final int b1 = is.read(); - final int b2 = is.read(); - if (b1 == -1 || b2 == -1) - return -1; - return b1 << 8 | b2; - } catch (final IOException e) { - throw new PngjInputException("error reading Int2", e); - } - } - - /** - * -1 if eof - */ - public static int readInt4(final InputStream is) { - try { - final int b1 = is.read(); - final int b2 = is.read(); - final int b3 = is.read(); - final int b4 = is.read(); - if (b1 == -1 || b2 == -1 || b3 == -1 || b4 == -1) - return -1; - return b1 << 24 | b2 << 16 | (b3 << 8) + b4; - } catch (final IOException e) { - throw new PngjInputException("error reading Int4", e); - } - } - - public static int readInt1fromByte(final byte[] b, final int offset) { - return b[offset] & 0xff; - } - - public static int readInt2fromBytes(final byte[] b, final int offset) { - return (b[offset] & 0xff) << 8 | b[offset + 1] & 0xff; - } - - public static int readInt4fromBytes(final byte[] b, final int offset) { - return (b[offset] & 0xff) << 24 | (b[offset + 1] & 0xff) << 16 | (b[offset + 2] & 0xff) << 8 | b[offset + 3] & 0xff; - } - - public static void writeByte(final OutputStream os, final byte b) { - try { - os.write(b); - } catch (final IOException e) { - throw new PngjOutputException(e); - } - } - - public static void writeByte(final OutputStream os, final byte[] bs) { - try { - os.write(bs); - } catch (final IOException e) { - throw new PngjOutputException(e); - } - } - - public static void writeInt2(final OutputStream os, final int n) { - final byte[] temp = { (byte) (n >> 8 & 0xff), (byte) (n & 0xff) }; - PngHelperInternal.writeBytes(os, temp); - } - - public static void writeInt4(final OutputStream os, final int n) { - final byte[] temp = new byte[4]; - PngHelperInternal.writeInt4tobytes(n, temp, 0); - PngHelperInternal.writeBytes(os, temp); - } - - public static void writeInt2tobytes(final int n, final byte[] b, final int offset) { - b[offset] = (byte) (n >> 8 & 0xff); - b[offset + 1] = (byte) (n & 0xff); - } - - public static void writeInt4tobytes(final int n, final byte[] b, final int offset) { - b[offset] = (byte) (n >> 24 & 0xff); - b[offset + 1] = (byte) (n >> 16 & 0xff); - b[offset + 2] = (byte) (n >> 8 & 0xff); - b[offset + 3] = (byte) (n & 0xff); - } - - /** - * guaranteed to read exactly len bytes. throws error if it can't - */ - public static void readBytes(final InputStream is, final byte[] b, final int offset, final int len) { - if (len == 0) - return; - try { - int read = 0; - while (read < len) { - final int n = is.read(b, offset + read, len - read); - if (n < 1) - throw new PngjInputException("error reading bytes, " + n + " !=" + len); - read += n; - } - } catch (final IOException e) { - throw new PngjInputException("error reading", e); - } - } - - public static void skipBytes(final InputStream is, long len) { - try { - while (len > 0) { - final long n1 = is.skip(len); - if (n1 > 0) - len -= n1; - else if (n1 == 0) { // should we retry? lets read one byte - if (is.read() == -1) // EOF - break; - else - len--; - } else - // negative? this should never happen but... - throw new IOException("skip() returned a negative value ???"); - } - } catch (final IOException e) { - throw new PngjInputException(e); - } - } - - public static void writeBytes(final OutputStream os, final byte[] b) { - try { - os.write(b); - } catch (final IOException e) { - throw new PngjOutputException(e); - } - } - - public static void writeBytes(final OutputStream os, final byte[] b, final int offset, final int n) { - try { - os.write(b, offset, n); - } catch (final IOException e) { - throw new PngjOutputException(e); - } - } - - public static void logdebug(final String msg) { - if (PngHelperInternal.isDebug()) - System.err.println("logdebug: " + msg); - } - - // / filters - public static int filterRowNone(final int r) { - return r & 0xFF; - } - - public static int filterRowSub(final int r, final int left) { - return r - left & 0xFF; - } - - public static int filterRowUp(final int r, final int up) { - return r - up & 0xFF; - } - - public static int filterRowAverage(final int r, final int left, final int up) { - return r - (left + up) / 2 & 0xFF; - } - - public static int filterRowPaeth(final int r, final int left, final int up, final int upleft) { // a = left, b = above, c - // = upper left - return r - PngHelperInternal.filterPaethPredictor(left, up, upleft) & 0xFF; - } - - static int filterPaethPredictor(final int a, final int b, final int c) { // a = left, b = - // above, c = upper - // left - // from http://www.libpng.org/pub/png/spec/1.2/PNG-Filters.html - - final int p = a + b - c;// ; initial estimate - final int pa = p >= a ? p - a : a - p; - final int pb = p >= b ? p - b : b - p; - final int pc = p >= c ? p - c : c - p; - // ; return nearest of a,b,c, - // ; breaking ties in order a,b,c. - if (pa <= pb && pa <= pc) - return a; - else if (pb <= pc) - return b; - else - return c; - } - - /** - * Prits a debug message (prints class name, method and line number) - * - * @param obj - * : Object to print - */ - public static void debug(final Object obj) { - PngHelperInternal.debug(obj, 1, true); - } - - /** - * Prits a debug message (prints class name, method and line number) - * - * @param obj - * : Object to print - * @param offset - * : Offset N lines from stacktrace - */ - static void debug(final Object obj, final int offset) { - PngHelperInternal.debug(obj, offset, true); - } - - public static InputStream istreamFromFile(final File f) { - FileInputStream is; - try { - is = new FileInputStream(f); - } catch (final Exception e) { - throw new PngjInputException("Could not open " + f, e); - } - return is; - } - - static OutputStream ostreamFromFile(final File f) { - return PngHelperInternal.ostreamFromFile(f, true); - } - - static OutputStream ostreamFromFile(final File f, final boolean overwrite) { - return PngHelperInternal2.ostreamFromFile(f, overwrite); - } - - /** - * Prints a debug message (prints class name, method and line number) to - * stderr and logFile - * - * @param obj - * : Object to print - * @param offset - * : Offset N lines from stacktrace - * @param newLine - * : Print a newline char at the end ('\n') - */ - static void debug(final Object obj, final int offset, final boolean newLine) { - final StackTraceElement ste = new Exception().getStackTrace()[1 + offset]; - String steStr = ste.getClassName(); - final int ind = steStr.lastIndexOf('.'); - steStr = steStr.substring(ind + 1); - steStr += "." + ste.getMethodName() + "(" + ste.getLineNumber() + "): " + (obj == null ? null : obj.toString()); - System.err.println(steStr); - } - - /** - * Sets a global debug flag. This is bound to a thread. - */ - public static void setDebug(final boolean b) { - PngHelperInternal.DEBUG.set(b); - } - - public static boolean isDebug() { - return PngHelperInternal.DEBUG.get().booleanValue(); - } - - public static long getDigest(final PngReader pngr) { - return pngr.getSimpleDigest(); - } - - public static void initCrcForTests(final PngReader pngr) { - pngr.prepareSimpleDigestComputation(); - } - - public static long getRawIdatBytes(final PngReader r) { // in case of image with frames, returns the current one - return r.interlaced ? r.getChunkseq().getDeinterlacer().getTotalRawBytes() : r.getCurImgInfo().getTotalRawBytes(); - } - -} diff --git a/teavm/src/main/java/ar/com/hjg/pngj/PngHelperInternal2.java b/teavm/src/main/java/ar/com/hjg/pngj/PngHelperInternal2.java deleted file mode 100644 index 58e702a8..00000000 --- a/teavm/src/main/java/ar/com/hjg/pngj/PngHelperInternal2.java +++ /dev/null @@ -1,35 +0,0 @@ -package ar.com.hjg.pngj; - -import java.io.File; -import java.io.OutputStream; - -/** - * For organization purposes, this class is the onlt that uses classes not in - * GAE (Google App Engine) white list - *

- * You should not use this class in GAE - */ -final class PngHelperInternal2 { - - /** - * WARNING: this uses FileOutputStream which is not allowed in - * GoogleAppEngine - * - * In GAE, dont use this - * - * @param f - * @param allowoverwrite - * @return - */ - static OutputStream ostreamFromFile(final File f, final boolean allowoverwrite) { - java.io.FileOutputStream os = null; // this will fail in GAE! - if (f.exists() && !allowoverwrite) - throw new PngjOutputException("File already exists: " + f); - try { - os = new java.io.FileOutputStream(f); - } catch (final Exception e) { - throw new PngjInputException("Could not open for write" + f, e); - } - return os; - } -} diff --git a/teavm/src/main/java/ar/com/hjg/pngj/PngReader.java b/teavm/src/main/java/ar/com/hjg/pngj/PngReader.java deleted file mode 100644 index efb5e9ad..00000000 --- a/teavm/src/main/java/ar/com/hjg/pngj/PngReader.java +++ /dev/null @@ -1,647 +0,0 @@ -package ar.com.hjg.pngj; - -import java.io.File; -import java.io.InputStream; -import java.util.zip.Adler32; -import java.util.zip.CRC32; - -import ar.com.hjg.pngj.chunks.ChunkLoadBehaviour; -import ar.com.hjg.pngj.chunks.ChunksList; -import ar.com.hjg.pngj.chunks.PngChunkFCTL; -import ar.com.hjg.pngj.chunks.PngChunkFDAT; -import ar.com.hjg.pngj.chunks.PngChunkIDAT; -import ar.com.hjg.pngj.chunks.PngMetadata; - -/** - * Reads a PNG image (pixels and/or metadata) from a file or stream. - *

- * Each row is read as an {@link ImageLineInt} object (one int per sample), but - * this can be changed by setting a - * different ImageLineFactory - *

- * Internally, this wraps a {@link ChunkSeqReaderPng} with a - * {@link BufferedStreamFeeder} - *

- * The reading sequence is as follows:
- * 1. At construction time, the header and IHDR chunk are read (basic image - * info)
- * 2. Afterwards you can set some additional global options. Eg. - * {@link #setCrcCheckDisabled()}.
- * 3. Optional: If you call getMetadata() or getChunksLisk() before start - * reading the rows, all the chunks before IDAT - * are then loaded and available
- * 4a. The rows are read in order by calling {@link #readRow()}. You can also - * call {@link #readRow(int)} to skip rows - * -but you can't go backwards, at least not with this implementation. This - * method returns a {@link IImageLine} object - * which can be casted to the concrete class. This class returns by default a - * {@link ImageLineInt}, but this can be - * changed.
- * 4b. Alternatively, you can read all rows, or a subset, in a single call: - * {@link #readRows()}, - * {@link #readRows(int, int, int)} ,etc. In general this consumes more memory, - * but for interlaced images this is - * equally efficient, and more so if reading a small subset of rows.
- * 5. Reading of the last row automatically loads the trailing chunks, and ends - * the reader.
- * 6. end() also loads the trailing chunks, if not done, and finishes cleanly - * the reading and closes the stream. - *

- * See also {@link PngReaderInt} (esentially the same as this, and slightly - * preferred) and {@link PngReaderByte} (uses - * byte instead of int to store the samples). - */ -public class PngReader { - - // some performance/defensive limits - /** - * Defensive limit: refuse to read more than 900MB, can be changed with - * {@link #setMaxTotalBytesRead(long)} - */ - public static final long MAX_TOTAL_BYTES_READ_DEFAULT = 901001001L; // ~ 900MB - - /** - * Defensive limit: refuse to load more than 5MB of ancillary metadata, see - * {@link #setMaxBytesMetadata(long)} and - * also {@link #addChunkToSkip(String)} - */ - public static final long MAX_BYTES_METADATA_DEFAULT = 5024024; // for ancillary chunks - - /** - * Skip ancillary chunks greater than 2MB, see - * {@link #setSkipChunkMaxSize(long)} - */ - public static final long MAX_CHUNK_SIZE_SKIP = 2024024; // chunks exceeding this size will be skipped (nor even CRC - // checked) - - /** - * Basic image info - final and inmutable. - */ - public final ImageInfo imgInfo; // People always told me: be careful what you do, and don't go around declaring public - // fields... - /** - * flag: image was in interlaced format - */ - public final boolean interlaced; - - /** - * This object has most of the intelligence to parse the chunks and - * decompress the IDAT stream - */ - protected final ChunkSeqReaderPng chunkseq; - - /** - * Takes bytes from the InputStream and passes it to the ChunkSeqReaderPng. - * Never null. - */ - protected final BufferedStreamFeeder streamFeeder; - - /** - * @see #getMetadata() - */ - protected final PngMetadata metadata; // this a wrapper over chunks - - /** - * Current row number (reading or read), numbered from 0 - */ - protected int rowNum = -1; - - /** - * Represents the set of lines (rows) being read. Normally this works as a - * cursor, storing only one (the current) row. - * This stores several (perhaps all) rows only if calling - * {@link #readRows()} or for interlaced images (this later is - * transparent to the user) - */ - protected IImageLineSet imlinesSet; - - /** - * This factory decides the concrete type of the ImageLine that will be - * used. See {@link ImageLineSetDefault} for - * examples - */ - private IImageLineSetFactory imageLineSetFactory; - - CRC32 idatCrca;// for internal testing - Adler32 idatCrcb;// for internal testing - - /** - * Constructs a PngReader object from a stream, with default options. This - * reads the signature and the first IHDR - * chunk only. - *

- * Warning: In case of exception the stream is NOT closed. - *

- * Warning: By default the stream will be closed when this object is - * {@link #close()}d. See - * {@link #PngReader(InputStream,boolean)} or - * {@link #setShouldCloseStream(boolean)} - *

- * - * @param inputStream - * PNG stream - */ - public PngReader(final InputStream inputStream) { - this(inputStream, true); - } - - /** - * Same as {@link #PngReader(InputStream)} but allows to specify early if - * the stream must be closed - * - * @param inputStream - * @param shouldCloseStream - * The stream will be closed in case of exception (constructor - * included) or normal - * termination. - */ - public PngReader(final InputStream inputStream, final boolean shouldCloseStream) { - streamFeeder = new BufferedStreamFeeder(inputStream); - streamFeeder.setCloseStream(shouldCloseStream); - chunkseq = createChunkSeqReader(); - try { - streamFeeder.setFailIfNoFeed(true); - if (!streamFeeder.feedFixed(chunkseq, 36)) // 8+13+12=36 PNG signature+IHDR chunk - throw new PngjInputException("error reading first 21 bytes"); - imgInfo = chunkseq.getImageInfo(); - interlaced = chunkseq.getDeinterlacer() != null; - setMaxBytesMetadata(PngReader.MAX_BYTES_METADATA_DEFAULT); - setMaxTotalBytesRead(PngReader.MAX_TOTAL_BYTES_READ_DEFAULT); - setSkipChunkMaxSize(PngReader.MAX_CHUNK_SIZE_SKIP); - chunkseq.addChunkToSkip(PngChunkFDAT.ID);// default: skip fdAT chunks! - chunkseq.addChunkToSkip(PngChunkFCTL.ID);// default: skip fctl chunks! - metadata = new PngMetadata(chunkseq.chunksList); - // sets a default factory (with ImageLineInt), - // this can be overwriten by a extended constructor, or by a setter - setLineSetFactory(ImageLineSetDefault.getFactoryInt()); - rowNum = -1; - } catch (final RuntimeException e) { - streamFeeder.close(); - chunkseq.close(); - throw e; - } - } - - /** - * Constructs a PngReader opening a file. Sets - * shouldCloseStream=true, so that the stream will be closed with - * this object. - * - * @param file - * PNG image file - */ - public PngReader(final File file) { - this(PngHelperInternal.istreamFromFile(file), true); - } - - /** - * Reads chunks before first IDAT. Normally this is called automatically - *

- * Position before: after IDHR (crc included) Position after: just after the - * first IDAT chunk id - *

- * This can be called several times (tentatively), it does nothing if - * already run - *

- * (Note: when should this be called? in the constructor? hardly, because we - * loose the opportunity to call - * setChunkLoadBehaviour() and perhaps other settings before reading the - * first row? but sometimes we want to access - * some metadata (plte, phys) before. Because of this, this method can be - * called explicitly but is also called - * implicititly in some methods (getMetatada(), getChunksList()) - */ - protected void readFirstChunks() { - while (chunkseq.currentChunkGroup < ChunksList.CHUNK_GROUP_4_IDAT) - if (streamFeeder.feed(chunkseq) <= 0) - throw new PngjInputException("premature ending reading first chunks"); - } - - /** - * Determines which ancillary chunks (metadata) are to be loaded and which - * skipped. - *

- * Additional restrictions may apply. See also - * {@link #setChunksToSkip(String...)}, {@link #addChunkToSkip(String)}, - * {@link #setMaxBytesMetadata(long)}, {@link #setSkipChunkMaxSize(long)} - * - * @param chunkLoadBehaviour - * {@link ChunkLoadBehaviour} - */ - public void setChunkLoadBehaviour(final ChunkLoadBehaviour chunkLoadBehaviour) { - chunkseq.setChunkLoadBehaviour(chunkLoadBehaviour); - } - - /** - * All loaded chunks (metada). If we have not yet end reading the image, - * this will include only the chunks before the - * pixels data (IDAT) - *

- * Critical chunks are included, except that all IDAT chunks appearance are - * replaced by a single dummy-marker IDAT - * chunk. These might be copied to the PngWriter - *

- * - * @see #getMetadata() - */ - public ChunksList getChunksList() { - return getChunksList(true); - } - - public ChunksList getChunksList(final boolean forceLoadingOfFirstChunks) { - if (forceLoadingOfFirstChunks && chunkseq.firstChunksNotYetRead()) - readFirstChunks(); - return chunkseq.chunksList; - } - - int getCurrentChunkGroup() { - return chunkseq.currentChunkGroup; - } - - /** - * High level wrapper over chunksList - * - * @see #getChunksList() - */ - public PngMetadata getMetadata() { - if (chunkseq.firstChunksNotYetRead()) - readFirstChunks(); - return metadata; - } - - /** - * Reads next row. - * - * The caller must know that there are more rows to read. - * - * @return Never null. Throws PngInputException if no more - */ - public IImageLine readRow() { - return readRow(rowNum + 1); - } - - /** - * True if last row has not yet been read - */ - public boolean hasMoreRows() { - return rowNum < getCurImgInfo().rows - 1; - } - - /** - * The row number is mostly meant as a check, the rows must be called in - * ascending order (not necessarily consecutive) - */ - public IImageLine readRow(final int nrow) { - if (chunkseq.firstChunksNotYetRead()) - readFirstChunks(); - if (!interlaced) { - if (imlinesSet == null) - imlinesSet = createLineSet(true, -1, 0, 1); - final IImageLine line = imlinesSet.getImageLine(nrow); - if (nrow == rowNum) - return line; // already read?? - else if (nrow < rowNum) - throw new PngjInputException("rows must be read in increasing order: " + nrow); - while (rowNum < nrow) { - while (!chunkseq.getIdatSet().isRowReady()) - if (streamFeeder.feed(chunkseq) < 1) - throw new PngjInputException("premature ending"); - rowNum++; - chunkseq.getIdatSet().updateCrcs(idatCrca, idatCrcb); - if (rowNum == nrow) { - line.readFromPngRaw(chunkseq.getIdatSet().getUnfilteredRow(), getCurImgInfo().bytesPerRow + 1, 0, 1); - line.endReadFromPngRaw(); - } - chunkseq.getIdatSet().advanceToNextRow(); - } - return line; - } else { // and now, for something completely different (interlaced!) - if (imlinesSet == null) { - imlinesSet = createLineSet(false, getCurImgInfo().rows, 0, 1); - loadAllInterlaced(getCurImgInfo().rows, 0, 1); - } - rowNum = nrow; - return imlinesSet.getImageLine(nrow); - } - - } - - /** - * Reads all rows in a ImageLineSet This is handy, but less memory-efficient - * (except for interlaced) - */ - public IImageLineSet readRows() { - return readRows(getCurImgInfo().rows, 0, 1); - } - - /** - * Reads a subset of rows. - *

- * This method should called once, and not be mixed with {@link #readRow()} - * - * @param nRows - * how many rows to read (default: imageInfo.rows; negative: - * autocompute) - * @param rowOffset - * rows to skip (default:0) - * @param rowStep - * step between rows to load( default:1) - */ - public IImageLineSet readRows(int nRows, final int rowOffset, final int rowStep) { - if (chunkseq.firstChunksNotYetRead()) - readFirstChunks(); - if (nRows < 0) - nRows = (getCurImgInfo().rows - rowOffset) / rowStep; - if (rowStep < 1 || rowOffset < 0 || nRows == 0 || nRows * rowStep + rowOffset > getCurImgInfo().rows) - throw new PngjInputException("bad args"); - if (rowNum >= rowOffset) - throw new PngjInputException("readRows cannot be mixed with readRow"); - imlinesSet = createLineSet(false, nRows, rowOffset, rowStep); - if (!interlaced) { - int m = -1; // last row already read in - while (m < nRows - 1) { - while (!chunkseq.getIdatSet().isRowReady()) - if (streamFeeder.feed(chunkseq) < 1) - throw new PngjInputException("Premature ending"); - rowNum++; - chunkseq.getIdatSet().updateCrcs(idatCrca, idatCrcb); - m = (rowNum - rowOffset) / rowStep; - if (rowNum >= rowOffset && rowStep * m + rowOffset == rowNum) { - final IImageLine line = imlinesSet.getImageLine(rowNum); - line.readFromPngRaw(chunkseq.getIdatSet().getUnfilteredRow(), getCurImgInfo().bytesPerRow + 1, 0, 1); - line.endReadFromPngRaw(); - } - chunkseq.getIdatSet().advanceToNextRow(); - } - } else - loadAllInterlaced(nRows, rowOffset, rowStep); - chunkseq.getIdatSet().done(); - return imlinesSet; - } - - /** - * Sets the factory that creates the ImageLine. By default, this - * implementation uses ImageLineInt but this can be - * changed (at construction time or later) by calling this method. - *

- * See also {@link #createLineSet(boolean, int, int, int)} - * - * @param factory - */ - public void setLineSetFactory(final IImageLineSetFactory factory) { - imageLineSetFactory = factory; - } - - /** - * By default this uses the factory (which, by default creates - * ImageLineInt). You should rarely override this. - *

- * See doc in - * {@link IImageLineSetFactory#create(ImageInfo, boolean, int, int, int)} - */ - protected IImageLineSet createLineSet(final boolean singleCursor, final int nlines, - final int noffset, final int step) { - return imageLineSetFactory.create(getCurImgInfo(), singleCursor, nlines, noffset, step); - } - - protected void loadAllInterlaced(final int nRows, final int rowOffset, final int rowStep) { - final IdatSet idat = chunkseq.getIdatSet(); - int nread = 0; - do { - while (!chunkseq.getIdatSet().isRowReady()) - if (streamFeeder.feed(chunkseq) <= 0) - break; - if (!chunkseq.getIdatSet().isRowReady()) - throw new PngjInputException("Premature ending?"); - chunkseq.getIdatSet().updateCrcs(idatCrca, idatCrcb); - final int rowNumreal = idat.rowinfo.rowNreal; - final boolean inset = imlinesSet.hasImageLine(rowNumreal); - if (inset) { - imlinesSet.getImageLine(rowNumreal).readFromPngRaw(idat.getUnfilteredRow(), idat.rowinfo.buflen, idat.rowinfo.oX, idat.rowinfo.dX); - nread++; - } - idat.advanceToNextRow(); - } while (nread < nRows || !idat.isDone()); - idat.done(); - for (int i = 0, j = rowOffset; i < nRows; i++, j += rowStep) - imlinesSet.getImageLine(j).endReadFromPngRaw(); - } - - /** - * Reads all the (remaining) file, skipping the pixels data. This is much - * more efficient that calling - * {@link #readRow()}, specially for big files (about 10 times faster!), - * because it doesn't even decompress the IDAT - * stream and disables CRC check Use this if you are not interested in - * reading pixels,only metadata. - */ - public void readSkippingAllRows() { - chunkseq.addChunkToSkip(PngChunkIDAT.ID); - chunkseq.addChunkToSkip(PngChunkFDAT.ID); - if (chunkseq.firstChunksNotYetRead()) - readFirstChunks(); - end(); - } - - /** - * Set total maximum bytes to read (0: unlimited; default: 200MB).
- * These are the bytes read (not loaded) in the input stream. If exceeded, - * an exception will be thrown. - */ - public void setMaxTotalBytesRead(final long maxTotalBytesToRead) { - chunkseq.setMaxTotalBytesRead(maxTotalBytesToRead); - } - - /** - * Set total maximum bytes to load from ancillary chunks (0: unlimited; - * default: 5Mb).
- * If exceeded, some chunks will be skipped - */ - public void setMaxBytesMetadata(final long maxBytesMetadata) { - chunkseq.setMaxBytesMetadata(maxBytesMetadata); - } - - /** - * Set maximum size in bytes for individual ancillary chunks (0: unlimited; - * default: 2MB).
- * Chunks exceeding this length will be skipped (the CRC will not be - * checked) and the chunk will be saved as a - * PngChunkSkipped object. See also setSkipChunkIds - */ - public void setSkipChunkMaxSize(final long skipChunkMaxSize) { - chunkseq.setSkipChunkMaxSize(skipChunkMaxSize); - } - - /** - * Chunks ids to be skipped.
- * These chunks will be skipped (the CRC will not be checked) and the chunk - * will be saved as a PngChunkSkipped object. - * See also setSkipChunkMaxSize - */ - public void setChunksToSkip(final String... chunksToSkip) { - chunkseq.setChunksToSkip(chunksToSkip); - } - - public void addChunkToSkip(final String chunkToSkip) { - chunkseq.addChunkToSkip(chunkToSkip); - } - - public void dontSkipChunk(final String chunkToSkip) { - chunkseq.dontSkipChunk(chunkToSkip); - } - - /** - * if true, input stream will be closed after ending read - *

- * default=true - */ - public void setShouldCloseStream(final boolean shouldCloseStream) { - streamFeeder.setCloseStream(shouldCloseStream); - } - - /** - * Reads till end of PNG stream and call close() - * - * This should normally be called after reading the pixel data, to read the - * trailing chunks and close the stream. But - * it can be called at anytime. This will also read the first chunks if not - * still read, and skip pixels (IDAT) if - * still pending. - * - * If you want to read all metadata skipping pixels, readSkippingAllRows() - * is a little more efficient. - * - * If you want to abort immediately, call instead close() - */ - public void end() { - try { - if (chunkseq.firstChunksNotYetRead()) - readFirstChunks(); - if (chunkseq.getIdatSet() != null && !chunkseq.getIdatSet().isDone()) - chunkseq.getIdatSet().done(); - while (!chunkseq.isDone()) - if (streamFeeder.feed(chunkseq) <= 0) - break; - } finally { - close(); - } - } - - /** - * Releases resources, and closes stream if corresponds. Idempotent, secure, - * no exceptions. - * - * This can be also called for abort. It is recommended to call this in case - * of exceptions - */ - public void close() { - try { - if (chunkseq != null) - chunkseq.close(); - } catch (final Exception e) { - PngHelperInternal.LOGGER.warning("error closing chunk sequence:" + e.getMessage()); - } - if (streamFeeder != null) - streamFeeder.close(); - } - - /** - * Interlaced PNG is accepted -though not welcomed- now... - */ - public boolean isInterlaced() { - return interlaced; - } - - /** - * Disables the CRC integrity check in IDAT chunks and ancillary chunks, - * this gives a slight increase in reading speed - * for big files - */ - public void setCrcCheckDisabled() { - chunkseq.setCheckCrc(false); - } - - /** - * Gets wrapped {@link ChunkSeqReaderPng} object - */ - public ChunkSeqReaderPng getChunkseq() { - return chunkseq; - } - - /** - * called on construction time. Override if you want an alternative class - */ - protected ChunkSeqReaderPng createChunkSeqReader() { - return new ChunkSeqReaderPng(false); - } - - /** - * Enables and prepare the simple digest computation. Must be called before - * reading the pixels. See - * {@link #getSimpleDigestHex()} - */ - public void prepareSimpleDigestComputation() { - if (idatCrca == null) - idatCrca = new CRC32(); - else - idatCrca.reset(); - if (idatCrcb == null) - idatCrcb = new Adler32(); - else - idatCrcb.reset(); - imgInfo.updateCrc(idatCrca); - idatCrcb.update((byte) imgInfo.rows); // not important - } - - long getSimpleDigest() { - if (idatCrca == null) - return 0; - else - return idatCrca.getValue() ^ idatCrcb.getValue() << 31; - } - - /** - * Pseudo 64-bits digest computed over the basic image properties and the - * raw pixels data: it should coincide for - * equivalent images encoded with different filters and compressors; but - * will not coincide for - * interlaced/non-interlaced; also, this does not take into account the - * palette info. This will be valid only if - * {@link #prepareSimpleDigestComputation()} has been called, and all rows - * have been read. Not fool-proof, not - * cryptografically secure, only for informal testing and duplicates - * detection. - * - * @return A 64-digest in hexadecimal - */ - public String getSimpleDigestHex() { - return String.format("%016X", getSimpleDigest()); - } - - /** - * Basic info, for debugging. - */ - @Override - public String toString() { // basic info - return imgInfo.toString() + " interlaced=" + interlaced; - } - - /** - * Basic info, in a compact format, apt for scripting - * COLSxROWS[dBITDEPTH][a][p][g][i] ( the default dBITDEPTH='d8' is - * ommited) - * - */ - public String toStringCompact() { - return imgInfo.toStringBrief() + (interlaced ? "i" : ""); - } - - public ImageInfo getImgInfo() { - return imgInfo; - } - - public ImageInfo getCurImgInfo() { - return chunkseq.getCurImgInfo(); - } - -} diff --git a/teavm/src/main/java/ar/com/hjg/pngj/PngReaderApng.java b/teavm/src/main/java/ar/com/hjg/pngj/PngReaderApng.java deleted file mode 100644 index 14b54e76..00000000 --- a/teavm/src/main/java/ar/com/hjg/pngj/PngReaderApng.java +++ /dev/null @@ -1,211 +0,0 @@ -package ar.com.hjg.pngj; - -import java.io.File; -import java.io.InputStream; -import java.util.List; - -import ar.com.hjg.pngj.chunks.PngChunk; -import ar.com.hjg.pngj.chunks.PngChunkACTL; -import ar.com.hjg.pngj.chunks.PngChunkFCTL; -import ar.com.hjg.pngj.chunks.PngChunkFDAT; -import ar.com.hjg.pngj.chunks.PngChunkIDAT; - -/** - */ -public class PngReaderApng extends PngReaderByte { - - public PngReaderApng(final File file) { - super(file); - dontSkipChunk(PngChunkFCTL.ID); - } - - public PngReaderApng(final InputStream inputStream) { - super(inputStream); - dontSkipChunk(PngChunkFCTL.ID); - } - - private Boolean apngKind = null; - private boolean firsIdatApngFrame = false; - protected PngChunkACTL actlChunk; // null if not APNG - private PngChunkFCTL fctlChunk; // current (null for the pseudo still frame) - - /** - * Current frame number (reading or read). First animated frame is 0. Frame - * -1 represents the IDAT (default image) - * when it's not part of the animation - */ - protected int frameNum = -1; // incremented after each fctl finding - - public boolean isApng() { - if (apngKind == null) { - // this triggers the loading of first chunks; - actlChunk = (PngChunkACTL) getChunksList().getById1(PngChunkACTL.ID); // null if not apng - apngKind = actlChunk != null; - firsIdatApngFrame = fctlChunk != null; - - } - return apngKind.booleanValue(); - } - - public void advanceToFrame(final int frame) { - if (frame < frameNum) - throw new PngjInputException("Cannot go backwards"); - if (frame >= getApngNumFrames()) - throw new PngjInputException("Frame out of range " + frame); - if (frame > frameNum) { - addChunkToSkip(PngChunkIDAT.ID); - addChunkToSkip(PngChunkFDAT.ID); - if (chunkseq.getIdatSet() != null && !chunkseq.getIdatSet().isDone()) - chunkseq.getIdatSet().done(); // seems to be necessary sometimes (we should check this) - while (frameNum < frame & !chunkseq.isDone()) - if (streamFeeder.feed(chunkseq) <= 0) - break; - } - if (frame == frameNum) { // prepare to read rows. at this point we have a new - dontSkipChunk(PngChunkIDAT.ID); - dontSkipChunk(PngChunkFDAT.ID); - rowNum = -1; - imlinesSet = null;// force recreation (this is slightly dirty) - // seek the next IDAT/fDAT - TODO: set the expected sequence number - while (!chunkseq.isDone() && !chunkseq.getCurChunkReader().isFromDeflatedSet()) - if (streamFeeder.feed(chunkseq) <= 0) - break; - } else - throw new PngjInputException("unexpected error seeking from frame " + frame); - } - - /** - * True if it has a default image (IDAT) that is not part of the animation. - * In that case, we consider it as a - * pseudo-frame (number -1) - */ - public boolean hasExtraStillImage() { - return isApng() && !firsIdatApngFrame; - } - - /** - * Only counts true animation frames. - */ - public int getApngNumFrames() { - if (isApng()) - return actlChunk.getNumFrames(); - else - return 0; - } - - /** - * 0 if it's to been played infinitely. -1 if not APNG - */ - public int getApngNumPlays() { - if (isApng()) - return actlChunk.getNumPlays(); - else - return -1; - } - - @Override - public IImageLine readRow() { - // TODO Auto-generated method stub - return super.readRow(); - } - - @Override - public boolean hasMoreRows() { - // TODO Auto-generated method stub - return super.hasMoreRows(); - } - - @Override - public IImageLine readRow(final int nrow) { - // TODO Auto-generated method stub - return super.readRow(nrow); - } - - @Override - public IImageLineSet readRows() { - // TODO Auto-generated method stub - return super.readRows(); - } - - @Override - public IImageLineSet readRows(final int nRows, final int rowOffset, final int rowStep) { - // TODO Auto-generated method stub - return super.readRows(nRows, rowOffset, rowStep); - } - - @Override - public void readSkippingAllRows() { - // TODO Auto-generated method stub - super.readSkippingAllRows(); - } - - @Override - protected ChunkSeqReaderPng createChunkSeqReader() { - final ChunkSeqReaderPng cr = new ChunkSeqReaderPng(false) { - - @Override - public boolean shouldSkipContent(final int len, final String id) { - return super.shouldSkipContent(len, id); - } - - @Override - protected boolean isIdatKind(final String id) { - return id.equals(PngChunkIDAT.ID) || id.equals(PngChunkFDAT.ID); - } - - @Override - protected DeflatedChunksSet createIdatSet(final String id) { - final IdatSet ids = new IdatSet(id, getCurImgInfo(), deinterlacer); - ids.setCallbackMode(callbackMode); - return ids; - } - - @Override - protected void startNewChunk(final int len, final String id, final long offset) { - super.startNewChunk(len, id, offset); - } - - @Override - protected void postProcessChunk(final ChunkReader chunkR) { - super.postProcessChunk(chunkR); - if (chunkR.getChunkRaw().id.equals(PngChunkFCTL.ID)) { - frameNum++; - final List chunkslist = chunkseq.getChunks(); - fctlChunk = (PngChunkFCTL) chunkslist.get(chunkslist.size() - 1); - // as this is slightly dirty, we check - if (chunkR.getChunkRaw().getOffset() != fctlChunk.getRaw().getOffset()) - throw new PngjInputException("something went wrong"); - final ImageInfo frameInfo = fctlChunk.getEquivImageInfo(); - getChunkseq().updateCurImgInfo(frameInfo); - } - } - - @SuppressWarnings("unlikely-arg-type") - @Override - protected boolean countChunkTypeAsAncillary(final String id) { - // we don't count fdat as ancillary data - return super.countChunkTypeAsAncillary(id) && !id.equals(id.equals(PngChunkFDAT.ID)); - } - - }; - return cr; - } - - /** - * @see #frameNum - */ - public int getFrameNum() { - return frameNum; - } - - @Override - public void end() { - // TODO Auto-generated method stub - super.end(); - } - - public PngChunkFCTL getFctl() { - return fctlChunk; - } - -} diff --git a/teavm/src/main/java/ar/com/hjg/pngj/PngReaderByte.java b/teavm/src/main/java/ar/com/hjg/pngj/PngReaderByte.java deleted file mode 100644 index 08307047..00000000 --- a/teavm/src/main/java/ar/com/hjg/pngj/PngReaderByte.java +++ /dev/null @@ -1,32 +0,0 @@ -package ar.com.hjg.pngj; - -import java.io.File; -import java.io.InputStream; - -/** - * Trivial extension of {@link PngReader} that uses {@link ImageLineByte} - *

- * The factory is set at construction time. Remember that this could still be - * changed at runtime. - */ -public class PngReaderByte extends PngReader { - - public PngReaderByte(final File file) { - super(file); - setLineSetFactory(ImageLineSetDefault.getFactoryByte()); - } - - public PngReaderByte(final InputStream inputStream) { - super(inputStream); - setLineSetFactory(ImageLineSetDefault.getFactoryByte()); - } - - /** - * Utility method that casts {@link #readRow()} return to - * {@link ImageLineByte}. - */ - public ImageLineByte readRowByte() { - return (ImageLineByte) readRow(); - } - -} diff --git a/teavm/src/main/java/ar/com/hjg/pngj/PngReaderFilter.java b/teavm/src/main/java/ar/com/hjg/pngj/PngReaderFilter.java deleted file mode 100644 index b2341561..00000000 --- a/teavm/src/main/java/ar/com/hjg/pngj/PngReaderFilter.java +++ /dev/null @@ -1,101 +0,0 @@ -package ar.com.hjg.pngj; - -import java.io.FilterInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.List; - -import ar.com.hjg.pngj.chunks.PngChunk; - -/** - * This class allows to use a simple PNG reader as an input filter, wrapping a - * ChunkSeqReaderPng in callback mode. - * - * In this sample implementation, all IDAT chunks are skipped and the rest are - * stored. An example of use, that lets us - * grab the Metadata and let the pixels go towards a BufferedImage: - * - * - *

- * PngReaderFilter reader = new PngReaderFilter(new FileInputStream("image.png"));
- * BufferedImage image1 = ImageIO.read(reader);
- * reader.readUntilEndAndClose(); // in case ImageIO.read() does not read the traling chunks (it happens)
- * System.out.println(reader.getChunksList());
- * 
- * - */ -public class PngReaderFilter extends FilterInputStream { - - private final ChunkSeqReaderPng chunkseq; - - public PngReaderFilter(final InputStream arg0) { - super(arg0); - chunkseq = createChunkSequenceReader(); - } - - protected ChunkSeqReaderPng createChunkSequenceReader() { - return new ChunkSeqReaderPng(true) { - @Override - public boolean shouldSkipContent(final int len, final String id) { - return super.shouldSkipContent(len, id) || id.equals("IDAT"); - } - - @Override - protected boolean shouldCheckCrc(final int len, final String id) { - return false; - } - - @Override - protected void postProcessChunk(final ChunkReader chunkR) { - super.postProcessChunk(chunkR); - // System.out.println("processed chunk " + chunkR.getChunkRaw().id); - } - }; - } - - @Override - public void close() throws IOException { - super.close(); - chunkseq.close(); - } - - @Override - public int read() throws IOException { - final int r = super.read(); - if (r > 0) - chunkseq.feedAll(new byte[] { (byte) r }, 0, 1); - return r; - } - - @Override - public int read(final byte[] b, final int off, final int len) throws IOException { - final int res = super.read(b, off, len); - if (res > 0) - chunkseq.feedAll(b, off, res); - return res; - } - - @Override - public int read(final byte[] b) throws IOException { - final int res = super.read(b); - if (res > 0) - chunkseq.feedAll(b, 0, res); - return res; - } - - public void readUntilEndAndClose() throws IOException { - final BufferedStreamFeeder br = new BufferedStreamFeeder(in); - while (!chunkseq.isDone() && br.hasMoreToFeed()) - br.feed(chunkseq); - close(); - } - - public List getChunksList() { - return chunkseq.getChunks(); - } - - public ChunkSeqReaderPng getChunkseq() { - return chunkseq; - } - -} diff --git a/teavm/src/main/java/ar/com/hjg/pngj/PngReaderInt.java b/teavm/src/main/java/ar/com/hjg/pngj/PngReaderInt.java deleted file mode 100644 index aef62bc5..00000000 --- a/teavm/src/main/java/ar/com/hjg/pngj/PngReaderInt.java +++ /dev/null @@ -1,39 +0,0 @@ -package ar.com.hjg.pngj; - -import java.io.File; -import java.io.InputStream; - -/** - * Trivial extension of {@link PngReader} that uses {@link ImageLineInt}. - *

- * In the current implementation this is quite dummy/redundant, because (for - * backward compatibility) PngReader already - * uses a {@link ImageLineInt}. - *

- * The factory is set at construction time. Remember that this could still be - * changed at runtime. - */ -public class PngReaderInt extends PngReader { - - public PngReaderInt(final File file) { - super(file); // not necessary to set factory, PngReader already does that - } - - public PngReaderInt(final InputStream inputStream) { - super(inputStream); - } - - /** - * Utility method that casts the IImageLine to a ImageLineInt - * - * This only make sense for this concrete class - * - */ - public ImageLineInt readRowInt() { - final IImageLine line = readRow(); - if (line instanceof ImageLineInt) - return (ImageLineInt) line; - else - throw new PngjException("This is not a ImageLineInt : " + line.getClass()); - } -} diff --git a/teavm/src/main/java/ar/com/hjg/pngj/PngWriter.java b/teavm/src/main/java/ar/com/hjg/pngj/PngWriter.java deleted file mode 100644 index 2be57f39..00000000 --- a/teavm/src/main/java/ar/com/hjg/pngj/PngWriter.java +++ /dev/null @@ -1,453 +0,0 @@ -package ar.com.hjg.pngj; - -import java.io.File; -import java.io.OutputStream; -import java.util.List; - -import ar.com.hjg.pngj.chunks.ChunkCopyBehaviour; -import ar.com.hjg.pngj.chunks.ChunkPredicate; -import ar.com.hjg.pngj.chunks.ChunksList; -import ar.com.hjg.pngj.chunks.ChunksListForWrite; -import ar.com.hjg.pngj.chunks.PngChunk; -import ar.com.hjg.pngj.chunks.PngChunkIEND; -import ar.com.hjg.pngj.chunks.PngChunkIHDR; -import ar.com.hjg.pngj.chunks.PngChunkPLTE; -import ar.com.hjg.pngj.chunks.PngMetadata; -import ar.com.hjg.pngj.pixels.PixelsWriter; -import ar.com.hjg.pngj.pixels.PixelsWriterDefault; - -/** - * Writes a PNG image, line by line. - */ -public class PngWriter { - - public final ImageInfo imgInfo; - - /** - * last writen row number, starting from 0 - */ - protected int rowNum = -1; - - private final ChunksListForWrite chunksList; - - private final PngMetadata metadata; - - /** - * Current chunk grounp, (0-6) already written or currently writing (this is - * advanced when just starting to write the - * new group, not when finalizing the previous) - *

- * see {@link ChunksList} - */ - protected int currentChunkGroup = -1; - - private final int passes = 1; // Some writes might require two passes (NOT USED STILL) - private int currentpass = 0; // numbered from 1 - - private boolean shouldCloseStream = true; - - private int idatMaxSize = 0; // 0=use default (PngIDatChunkOutputStream 64k) - // private PngIDatChunkOutputStream datStream; - - protected PixelsWriter pixelsWriter; - - private final OutputStream os; - - private ChunkPredicate copyFromPredicate = null; - private ChunksList copyFromList = null; - - protected StringBuilder debuginfo = new StringBuilder(); - - /** - * Opens a file for writing. - *

- * Sets shouldCloseStream=true. For more info see - * {@link #PngWriter(OutputStream, ImageInfo)} - * - * @param file - * @param imgInfo - * @param allowoverwrite - * If false and file exists, an {@link PngjOutputException} is - * thrown - */ - public PngWriter(final File file, final ImageInfo imgInfo, final boolean allowoverwrite) { - this(PngHelperInternal.ostreamFromFile(file, allowoverwrite), imgInfo); - setShouldCloseStream(true); - } - - /** - * @see #PngWriter(File, ImageInfo, boolean) (overwrite=true) - */ - public PngWriter(final File file, final ImageInfo imgInfo) { - this(file, imgInfo, true); - } - - /** - * Constructs a new PngWriter from a output stream. After construction - * nothing is writen yet. You still can set some - * parameters (compression, filters) and queue chunks before start writing - * the pixels. - *

- * - * @param outputStream - * Open stream for binary writing - * @param imgInfo - * Basic image parameters - */ - public PngWriter(final OutputStream outputStream, final ImageInfo imgInfo) { - os = outputStream; - this.imgInfo = imgInfo; - // prealloc - chunksList = new ChunksListForWrite(imgInfo); - metadata = new PngMetadata(chunksList); - pixelsWriter = createPixelsWriter(imgInfo); - setCompLevel(9); - } - - private void initIdat() { // this triggers the writing of first chunks - pixelsWriter.setOs(os); - pixelsWriter.setIdatMaxSize(idatMaxSize); - writeSignatureAndIHDR(); - writeFirstChunks(); - } - - private void writeEndChunk() { - currentChunkGroup = ChunksList.CHUNK_GROUP_6_END; - final PngChunkIEND c = new PngChunkIEND(imgInfo); - c.createRawChunk().writeChunk(os); - chunksList.getChunks().add(c); - } - - private void writeFirstChunks() { - if (currentChunkGroup >= ChunksList.CHUNK_GROUP_4_IDAT) - return; - int nw = 0; - currentChunkGroup = ChunksList.CHUNK_GROUP_1_AFTERIDHR; - queueChunksFromOther(); - nw = chunksList.writeChunks(os, currentChunkGroup); - currentChunkGroup = ChunksList.CHUNK_GROUP_2_PLTE; - nw = chunksList.writeChunks(os, currentChunkGroup); - if (nw > 0 && imgInfo.greyscale) - throw new PngjOutputException("cannot write palette for this format"); - if (nw == 0 && imgInfo.indexed) - throw new PngjOutputException("missing palette"); - currentChunkGroup = ChunksList.CHUNK_GROUP_3_AFTERPLTE; - nw = chunksList.writeChunks(os, currentChunkGroup); - } - - private void writeLastChunks() { // not including end - currentChunkGroup = ChunksList.CHUNK_GROUP_5_AFTERIDAT; - queueChunksFromOther(); - chunksList.writeChunks(os, currentChunkGroup); - // should not be unwriten chunks - final List pending = chunksList.getQueuedChunks(); - if (!pending.isEmpty()) - throw new PngjOutputException(pending.size() + " chunks were not written! Eg: " + pending.get(0).toString()); - } - - /** - * Write id signature and also "IHDR" chunk - */ - private void writeSignatureAndIHDR() { - PngHelperInternal.writeBytes(os, PngHelperInternal.getPngIdSignature()); // signature - currentChunkGroup = ChunksList.CHUNK_GROUP_0_IDHR; - final PngChunkIHDR ihdr = new PngChunkIHDR(imgInfo); - // http://www.libpng.org/pub/png/spec/1.2/PNG-Chunks.html - ihdr.createRawChunk().writeChunk(os); - chunksList.getChunks().add(ihdr); - } - - private void queueChunksFromOther() { - if (copyFromList == null || copyFromPredicate == null) - return; - final boolean idatDone = currentChunkGroup >= ChunksList.CHUNK_GROUP_4_IDAT; // we assume this method is not either before - // or after the IDAT writing, not in the - // middle! - for (final PngChunk chunk : copyFromList.getChunks()) { - if (chunk.getRaw().data == null) - continue; // we cannot copy skipped chunks? - final int groupOri = chunk.getChunkGroup(); - if (groupOri <= ChunksList.CHUNK_GROUP_4_IDAT && idatDone) - continue; - if (groupOri >= ChunksList.CHUNK_GROUP_4_IDAT && !idatDone) - continue; - if (chunk.crit && !chunk.id.equals(PngChunkPLTE.ID)) - continue; // critical chunks (except perhaps PLTE) are never - // copied - final boolean copy = copyFromPredicate.match(chunk); - if (copy) - // but if the chunk is already queued or writen, it's ommited! - if (chunksList.getEquivalent(chunk).isEmpty() && chunksList.getQueuedEquivalent(chunk).isEmpty()) - chunksList.queue(chunk); - } - } - - /** - * Queues an ancillary chunk for writing. - *

- * If a "equivalent" chunk is already queued (see - * {@link ChunkHelper#equivalent(PngChunk, PngChunk)), this overwrites it. - *

- * The chunk will be written as late as possible, unless the priority is - * set. - * - * @param chunk - */ - public void queueChunk(final PngChunk chunk) { - for (final PngChunk other : chunksList.getQueuedEquivalent(chunk)) - getChunksList().removeChunk(other); - chunksList.queue(chunk); - } - - /** - * Sets an origin (typically from a {@link PngReader}) of Chunks to be - * copied. This should be called only once, before - * starting writing the rows. It doesn't matter the current state of the - * PngReader reading, this is a live object and - * what matters is that when the writer writes the pixels (IDAT) the reader - * has already read them, and that when the - * writer ends, the reader is already ended (all this is very natural). - *

- * Apart from the copyMask, there is some addional heuristics: - *

- * - The chunks will be queued, but will be written as late as possible - * (unless you explicitly set priority=true) - *

- * - The chunk will not be queued if an "equivalent" chunk was already - * queued explicitly. And it will be overwriten - * another is queued explicitly. - * - * @param chunks - * @param copyMask - * Some bitmask from {@link ChunkCopyBehaviour} - * - * @see #copyChunksFrom(ChunksList, ChunkPredicate) - */ - public void copyChunksFrom(final ChunksList chunks, final int copyMask) { - copyChunksFrom(chunks, ChunkCopyBehaviour.createPredicate(copyMask, imgInfo)); - } - - /** - * Copy all chunks from origin. See {@link #copyChunksFrom(ChunksList, int)} - * for more info - */ - public void copyChunksFrom(final ChunksList chunks) { - copyChunksFrom(chunks, ChunkCopyBehaviour.COPY_ALL); - } - - /** - * Copy chunks from origin depending on some {@link ChunkPredicate} - * - * @param chunks - * @param predicate - * The chunks (ancillary or PLTE) will be copied if and only if - * predicate matches - * - * @see #copyChunksFrom(ChunksList, int) for more info - */ - public void copyChunksFrom(final ChunksList chunks, final ChunkPredicate predicate) { - if (copyFromList != null && chunks != null) - PngHelperInternal.LOGGER.warning("copyChunksFrom should only be called once"); - if (predicate == null) - throw new PngjOutputException("copyChunksFrom requires a predicate"); - copyFromList = chunks; - copyFromPredicate = predicate; - } - - /** - * Computes compressed size/raw size, approximate. - *

- * Actually: compressed size = total size of IDAT data , raw size = - * uncompressed pixel bytes = rows * (bytesPerRow + - * 1). - * - * This must be called after pngw.end() - */ - public double computeCompressionRatio() { - if (currentChunkGroup < ChunksList.CHUNK_GROUP_5_AFTERIDAT) - throw new PngjOutputException("must be called after end()"); - return pixelsWriter.getCompression(); - } - - /** - * Finalizes all the steps and closes the stream. This must be called after - * writing the lines. Idempotent - */ - public void end() { - if (rowNum != imgInfo.rows - 1 || !pixelsWriter.isDone()) - throw new PngjOutputException("all rows have not been written"); - try { - if (pixelsWriter != null) - pixelsWriter.close(); - if (currentChunkGroup < ChunksList.CHUNK_GROUP_5_AFTERIDAT) - writeLastChunks(); - if (currentChunkGroup < ChunksList.CHUNK_GROUP_6_END) - writeEndChunk(); - } finally { - close(); - } - } - - /** - * Closes and releases resources - *

- * This is normally called internally from {@link #end()}, you should only - * call this for aborting the writing and - * release resources (close the stream). - *

- * Idempotent and secure - never throws exceptions - */ - public void close() { - if (pixelsWriter != null) - pixelsWriter.close(); - if (shouldCloseStream && os != null) - try { - os.close(); - } catch (final Exception e) { - PngHelperInternal.LOGGER.warning("Error closing writer " + e.toString()); - } - } - - /** - * returns the chunks list (queued and writen chunks) - */ - public ChunksListForWrite getChunksList() { - return chunksList; - } - - /** - * Retruns a high level wrapper over for metadata handling - */ - public PngMetadata getMetadata() { - return metadata; - } - - /** - * Sets internal prediction filter type, or strategy to choose it. - *

- * This must be called just after constructor, before starting writing. - *

- */ - public void setFilterType(final FilterType filterType) { - pixelsWriter.setFilterType(filterType); - } - - /** - * This is kept for backwards compatibility, now the PixelsWriter object - * should be used for setting - * compression/filtering options - * - * @see PixelsWriter#setCompressionFactor(double) - * @param compLevel - * between 0 (no compression, max speed) and 9 (max compression) - */ - public void setCompLevel(final int complevel) { - pixelsWriter.setDeflaterCompLevel(complevel); - } - - /** - * - */ - public void setFilterPreserve(final boolean filterPreserve) { - if (filterPreserve) - pixelsWriter.setFilterType(FilterType.FILTER_PRESERVE); - else if (pixelsWriter.getFilterType() == null) - pixelsWriter.setFilterType(FilterType.FILTER_DEFAULT); - } - - /** - * Sets maximum size of IDAT fragments. Incrementing this from the default - * has very little effect on compression and - * increments memory usage. You should rarely change this. - *

- * - * @param idatMaxSize - * default=0 : use defaultSize (32K) - */ - public void setIdatMaxSize(final int idatMaxSize) { - this.idatMaxSize = idatMaxSize; - } - - /** - * If true, output stream will be closed after ending write - *

- * default=true - */ - public void setShouldCloseStream(final boolean shouldCloseStream) { - this.shouldCloseStream = shouldCloseStream; - } - - /** - * Writes next row, does not check row number. - * - * @param imgline - */ - public void writeRow(final IImageLine imgline) { - writeRow(imgline, rowNum + 1); - } - - /** - * Writes the full set of row. The ImageLineSet should contain (allow to - * acces) imgInfo.rows - */ - public void writeRows(final IImageLineSet imglines) { - for (int i = 0; i < imgInfo.rows; i++) - writeRow(imglines.getImageLineRawNum(i)); - } - - public void writeRow(final IImageLine imgline, int rownumber) { - rowNum++; - if (rowNum == imgInfo.rows) - rowNum = 0; - if (rownumber == imgInfo.rows) - rownumber = 0; - if (rownumber >= 0 && rowNum != rownumber) - throw new PngjOutputException("rows must be written in order: expected:" + rowNum + " passed:" + rownumber); - if (rowNum == 0) - currentpass++; - if (rownumber == 0 && currentpass == passes) { - initIdat(); - currentChunkGroup = ChunksList.CHUNK_GROUP_4_IDAT; // we just begin writing IDAT - } - final byte[] rowb = pixelsWriter.getRowb(); - imgline.writeToPngRaw(rowb); - pixelsWriter.processRow(rowb); - - } - - /** - * Utility method, uses internaly a ImageLineInt - */ - public void writeRowInt(final int[] buf) { - writeRow(new ImageLineInt(imgInfo, buf)); - } - - /** - * Factory method for pixels writer. This will be called once at the moment - * at start writing a set of IDAT chunks - * (typically once in a normal PNG) - * - * This should be overriden if custom filtering strategies are desired. - * Remember to release this with close() - * - * @param imginfo - * Might be different than that of this object (eg: APNG with - * subimages) - * @param os - * Output stream - * @return new PixelsWriter. Don't forget to call close() when discarding it - */ - protected PixelsWriter createPixelsWriter(final ImageInfo imginfo) { - final PixelsWriterDefault pw = new PixelsWriterDefault(imginfo); - return pw; - } - - public final PixelsWriter getPixelsWriter() { - return pixelsWriter; - } - - public String getDebuginfo() { - return debuginfo.toString(); - } - -} diff --git a/teavm/src/main/java/ar/com/hjg/pngj/PngWriterHc.java b/teavm/src/main/java/ar/com/hjg/pngj/PngWriterHc.java deleted file mode 100644 index aec2140c..00000000 --- a/teavm/src/main/java/ar/com/hjg/pngj/PngWriterHc.java +++ /dev/null @@ -1,35 +0,0 @@ -package ar.com.hjg.pngj; - -import java.io.File; -import java.io.OutputStream; - -import ar.com.hjg.pngj.pixels.PixelsWriter; -import ar.com.hjg.pngj.pixels.PixelsWriterMultiple; - -/** Pngwriter with High compression EXPERIMENTAL */ -public class PngWriterHc extends PngWriter { - - public PngWriterHc(final File file, final ImageInfo imgInfo, final boolean allowoverwrite) { - super(file, imgInfo, allowoverwrite); - setFilterType(FilterType.FILTER_SUPER_ADAPTIVE); - } - - public PngWriterHc(final File file, final ImageInfo imgInfo) { - super(file, imgInfo); - } - - public PngWriterHc(final OutputStream outputStream, final ImageInfo imgInfo) { - super(outputStream, imgInfo); - } - - @Override - protected PixelsWriter createPixelsWriter(final ImageInfo imginfo) { - final PixelsWriterMultiple pw = new PixelsWriterMultiple(imginfo); - return pw; - } - - public PixelsWriterMultiple getPixelWriterMultiple() { - return (PixelsWriterMultiple) pixelsWriter; - } - -} diff --git a/teavm/src/main/java/ar/com/hjg/pngj/PngjBadCrcException.java b/teavm/src/main/java/ar/com/hjg/pngj/PngjBadCrcException.java deleted file mode 100644 index 1efbada2..00000000 --- a/teavm/src/main/java/ar/com/hjg/pngj/PngjBadCrcException.java +++ /dev/null @@ -1,20 +0,0 @@ -package ar.com.hjg.pngj; - -/** - * Exception thrown by bad CRC check - */ -public class PngjBadCrcException extends PngjInputException { - private static final long serialVersionUID = 1L; - - public PngjBadCrcException(final String message, final Throwable cause) { - super(message, cause); - } - - public PngjBadCrcException(final String message) { - super(message); - } - - public PngjBadCrcException(final Throwable cause) { - super(cause); - } -} diff --git a/teavm/src/main/java/ar/com/hjg/pngj/PngjException.java b/teavm/src/main/java/ar/com/hjg/pngj/PngjException.java deleted file mode 100644 index f99f1df5..00000000 --- a/teavm/src/main/java/ar/com/hjg/pngj/PngjException.java +++ /dev/null @@ -1,20 +0,0 @@ -package ar.com.hjg.pngj; - -/** - * Generic exception for this library. It's a RuntimeException (unchecked) - */ -public class PngjException extends RuntimeException { - private static final long serialVersionUID = 1L; - - public PngjException(final String message, final Throwable cause) { - super(message, cause); - } - - public PngjException(final String message) { - super(message); - } - - public PngjException(final Throwable cause) { - super(cause); - } -} diff --git a/teavm/src/main/java/ar/com/hjg/pngj/PngjExceptionInternal.java b/teavm/src/main/java/ar/com/hjg/pngj/PngjExceptionInternal.java deleted file mode 100644 index 5113ac0e..00000000 --- a/teavm/src/main/java/ar/com/hjg/pngj/PngjExceptionInternal.java +++ /dev/null @@ -1,24 +0,0 @@ -package ar.com.hjg.pngj; - -/** - * Exception for anomalous internal problems (sort of asserts) that point to - * some issue with the library - * - * @author Hernan J Gonzalez - * - */ -public class PngjExceptionInternal extends RuntimeException { - private static final long serialVersionUID = 1L; - - public PngjExceptionInternal(final String message, final Throwable cause) { - super(message, cause); - } - - public PngjExceptionInternal(final String message) { - super(message); - } - - public PngjExceptionInternal(final Throwable cause) { - super(cause); - } -} diff --git a/teavm/src/main/java/ar/com/hjg/pngj/PngjInputException.java b/teavm/src/main/java/ar/com/hjg/pngj/PngjInputException.java deleted file mode 100644 index 8dbe7459..00000000 --- a/teavm/src/main/java/ar/com/hjg/pngj/PngjInputException.java +++ /dev/null @@ -1,20 +0,0 @@ -package ar.com.hjg.pngj; - -/** - * Exception thrown when reading a PNG. - */ -public class PngjInputException extends PngjException { - private static final long serialVersionUID = 1L; - - public PngjInputException(final String message, final Throwable cause) { - super(message, cause); - } - - public PngjInputException(final String message) { - super(message); - } - - public PngjInputException(final Throwable cause) { - super(cause); - } -} diff --git a/teavm/src/main/java/ar/com/hjg/pngj/PngjOutputException.java b/teavm/src/main/java/ar/com/hjg/pngj/PngjOutputException.java deleted file mode 100644 index 3698c5b6..00000000 --- a/teavm/src/main/java/ar/com/hjg/pngj/PngjOutputException.java +++ /dev/null @@ -1,20 +0,0 @@ -package ar.com.hjg.pngj; - -/** - * Exception thrown by writing process - */ -public class PngjOutputException extends PngjException { - private static final long serialVersionUID = 1L; - - public PngjOutputException(final String message, final Throwable cause) { - super(message, cause); - } - - public PngjOutputException(final String message) { - super(message); - } - - public PngjOutputException(final Throwable cause) { - super(cause); - } -} diff --git a/teavm/src/main/java/ar/com/hjg/pngj/PngjUnsupportedException.java b/teavm/src/main/java/ar/com/hjg/pngj/PngjUnsupportedException.java deleted file mode 100644 index 2c2ae9b8..00000000 --- a/teavm/src/main/java/ar/com/hjg/pngj/PngjUnsupportedException.java +++ /dev/null @@ -1,25 +0,0 @@ -package ar.com.hjg.pngj; - -/** - * Exception thrown because of some valid feature of PNG standard that this - * library does not support. - */ -public class PngjUnsupportedException extends RuntimeException { - private static final long serialVersionUID = 1L; - - public PngjUnsupportedException() { - super(); - } - - public PngjUnsupportedException(final String message, final Throwable cause) { - super(message, cause); - } - - public PngjUnsupportedException(final String message) { - super(message); - } - - public PngjUnsupportedException(final Throwable cause) { - super(cause); - } -} diff --git a/teavm/src/main/java/ar/com/hjg/pngj/RowInfo.java b/teavm/src/main/java/ar/com/hjg/pngj/RowInfo.java deleted file mode 100644 index 33f78ef2..00000000 --- a/teavm/src/main/java/ar/com/hjg/pngj/RowInfo.java +++ /dev/null @@ -1,55 +0,0 @@ -package ar.com.hjg.pngj; - -/** - * Packs information of current row. Only used internally - */ -class RowInfo { - public final ImageInfo imgInfo; - public final Deinterlacer deinterlacer; - public final boolean imode; // Interlaced - int dY, dX, oY, oX; // current step and offset (in pixels) - int rowNseq; // row number (from 0) in sequential read order - int rowNreal; // row number in the real image - int rowNsubImg; // current row in the virtual subsampled image; this increments (by 1) from 0 to - // rows/dy 7 times - int rowsSubImg, colsSubImg; // size of current subimage , in pixels - int bytesRow; - int pass; // 1-7 - byte[] buf; // non-deep copy - int buflen; // valid bytes in buffer (include filter byte) - - public RowInfo(final ImageInfo imgInfo, final Deinterlacer deinterlacer) { - this.imgInfo = imgInfo; - this.deinterlacer = deinterlacer; - imode = deinterlacer != null; - } - - void update(final int rowseq) { - rowNseq = rowseq; - if (imode) { - pass = deinterlacer.getPass(); - dX = deinterlacer.dX; - dY = deinterlacer.dY; - oX = deinterlacer.oX; - oY = deinterlacer.oY; - rowNreal = deinterlacer.getCurrRowReal(); - rowNsubImg = deinterlacer.getCurrRowSubimg(); - rowsSubImg = deinterlacer.getRows(); - colsSubImg = deinterlacer.getCols(); - bytesRow = (imgInfo.bitspPixel * colsSubImg + 7) / 8; - } else { - pass = 1; - dX = dY = 1; - oX = oY = 0; - rowNreal = rowNsubImg = rowseq; - rowsSubImg = imgInfo.rows; - colsSubImg = imgInfo.cols; - bytesRow = imgInfo.bytesPerRow; - } - } - - void updateBuf(final byte[] buf, final int buflen) { - this.buf = buf; - this.buflen = buflen; - } -} diff --git a/teavm/src/main/java/ar/com/hjg/pngj/chunks/ChunkCopyBehaviour.java b/teavm/src/main/java/ar/com/hjg/pngj/chunks/ChunkCopyBehaviour.java deleted file mode 100644 index 95224556..00000000 --- a/teavm/src/main/java/ar/com/hjg/pngj/chunks/ChunkCopyBehaviour.java +++ /dev/null @@ -1,97 +0,0 @@ -package ar.com.hjg.pngj.chunks; - -import ar.com.hjg.pngj.ImageInfo; -import ar.com.hjg.pngj.PngReader; -import ar.com.hjg.pngj.PngWriter; - -/** - * Chunk copy policy to apply when copyng from a {@link PngReader} to a - * {@link PngWriter}. - *

- * The constants are bit-masks, they can be OR-ed - *

- * Reference: - * http://www.w3.org/TR/PNG/#14
- */ -public class ChunkCopyBehaviour { - - /** Don't copy anything */ - public static final int COPY_NONE = 0; - - /** copy the palette */ - public static final int COPY_PALETTE = 1; - - /** copy all 'safe to copy' chunks */ - public static final int COPY_ALL_SAFE = 1 << 2; - - /** - * copy all, including palette - */ - public static final int COPY_ALL = 1 << 3; // includes palette! - /** - * Copy PHYS chunk (physical resolution) - */ - public static final int COPY_PHYS = 1 << 4; // dpi - /** - * Copy al textual chunks. - */ - public static final int COPY_TEXTUAL = 1 << 5; // all textual types - /** - * Copy TRNS chunk - */ - public static final int COPY_TRANSPARENCY = 1 << 6; // - /** - * Copy unknown chunks (unknown by our factory) - */ - public static final int COPY_UNKNOWN = 1 << 7; // all unknown (by the factory!) - /** - * Copy almost all: excepts only HIST (histogram) TIME and TEXTUAL chunks - */ - public static final int COPY_ALMOSTALL = 1 << 8; - - private static boolean maskMatch(final int v, final int mask) { - return (v & mask) != 0; - } - - /** - * Creates a predicate equivalent to the copy mask - *

- * Given a copy mask (see static fields) and the ImageInfo of the target - * PNG, returns a predicate that tells if a - * chunk should be copied. - *

- * This is a handy helper method, you can also create and set your own - * predicate - */ - public static ChunkPredicate createPredicate(final int copyFromMask, final ImageInfo imgInfo) { - return chunk -> { - if (chunk.crit) { - if (chunk.id.equals(ChunkHelper.PLTE)) { - if (imgInfo.indexed && ChunkCopyBehaviour.maskMatch(copyFromMask, ChunkCopyBehaviour.COPY_PALETTE)) - return true; - if (!imgInfo.greyscale && ChunkCopyBehaviour.maskMatch(copyFromMask, ChunkCopyBehaviour.COPY_ALL)) - return true; - } - } else { // ancillary - final boolean text = chunk instanceof PngChunkTextVar; - final boolean safe = chunk.safe; - // notice that these if are not exclusive - if (ChunkCopyBehaviour.maskMatch(copyFromMask, ChunkCopyBehaviour.COPY_ALL)) - return true; - if (safe && ChunkCopyBehaviour.maskMatch(copyFromMask, ChunkCopyBehaviour.COPY_ALL_SAFE)) - return true; - if (chunk.id.equals(ChunkHelper.tRNS) && ChunkCopyBehaviour.maskMatch(copyFromMask, ChunkCopyBehaviour.COPY_TRANSPARENCY)) - return true; - if (chunk.id.equals(ChunkHelper.pHYs) && ChunkCopyBehaviour.maskMatch(copyFromMask, ChunkCopyBehaviour.COPY_PHYS)) - return true; - if (text && ChunkCopyBehaviour.maskMatch(copyFromMask, ChunkCopyBehaviour.COPY_TEXTUAL)) - return true; - if (ChunkCopyBehaviour.maskMatch(copyFromMask, ChunkCopyBehaviour.COPY_ALMOSTALL) && !(ChunkHelper.isUnknown(chunk) || text || chunk.id.equals(ChunkHelper.hIST) || chunk.id.equals(ChunkHelper.tIME))) - return true; - if (ChunkCopyBehaviour.maskMatch(copyFromMask, ChunkCopyBehaviour.COPY_UNKNOWN) && ChunkHelper.isUnknown(chunk)) - return true; - } - return false; - }; - } -} diff --git a/teavm/src/main/java/ar/com/hjg/pngj/chunks/ChunkFactory.java b/teavm/src/main/java/ar/com/hjg/pngj/chunks/ChunkFactory.java deleted file mode 100644 index 351980c3..00000000 --- a/teavm/src/main/java/ar/com/hjg/pngj/chunks/ChunkFactory.java +++ /dev/null @@ -1,112 +0,0 @@ -package ar.com.hjg.pngj.chunks; - -import ar.com.hjg.pngj.IChunkFactory; -import ar.com.hjg.pngj.ImageInfo; - -/** - * Default chunk factory. - *

- * The user that wants to parse custom chunks can extend - * {@link #createEmptyChunkExtended(String, ImageInfo)} - */ -public class ChunkFactory implements IChunkFactory { - - boolean parse; - - public ChunkFactory() { - this(true); - } - - public ChunkFactory(final boolean parse) { - this.parse = parse; - } - - @Override - public final PngChunk createChunk(final ChunkRaw chunkRaw, final ImageInfo imgInfo) { - PngChunk c = createEmptyChunkKnown(chunkRaw.id, imgInfo); - if (c == null) - c = createEmptyChunkExtended(chunkRaw.id, imgInfo); - if (c == null) - c = createEmptyChunkUnknown(chunkRaw.id, imgInfo); - c.setRaw(chunkRaw); - if (parse && chunkRaw.data != null) - c.parseFromRaw(chunkRaw); - return c; - } - - protected final PngChunk createEmptyChunkKnown(final String id, final ImageInfo imgInfo) { - if (id.equals(ChunkHelper.IDAT)) - return new PngChunkIDAT(imgInfo); - if (id.equals(ChunkHelper.IHDR)) - return new PngChunkIHDR(imgInfo); - if (id.equals(ChunkHelper.PLTE)) - return new PngChunkPLTE(imgInfo); - if (id.equals(ChunkHelper.IEND)) - return new PngChunkIEND(imgInfo); - if (id.equals(ChunkHelper.tEXt)) - return new PngChunkTEXT(imgInfo); - if (id.equals(ChunkHelper.iTXt)) - return new PngChunkITXT(imgInfo); - if (id.equals(ChunkHelper.zTXt)) - return new PngChunkZTXT(imgInfo); - if (id.equals(ChunkHelper.bKGD)) - return new PngChunkBKGD(imgInfo); - if (id.equals(ChunkHelper.gAMA)) - return new PngChunkGAMA(imgInfo); - if (id.equals(ChunkHelper.pHYs)) - return new PngChunkPHYS(imgInfo); - if (id.equals(ChunkHelper.iCCP)) - return new PngChunkICCP(imgInfo); - if (id.equals(ChunkHelper.tIME)) - return new PngChunkTIME(imgInfo); - if (id.equals(ChunkHelper.tRNS)) - return new PngChunkTRNS(imgInfo); - if (id.equals(ChunkHelper.cHRM)) - return new PngChunkCHRM(imgInfo); - if (id.equals(ChunkHelper.sBIT)) - return new PngChunkSBIT(imgInfo); - if (id.equals(ChunkHelper.sRGB)) - return new PngChunkSRGB(imgInfo); - if (id.equals(ChunkHelper.hIST)) - return new PngChunkHIST(imgInfo); - if (id.equals(ChunkHelper.sPLT)) - return new PngChunkSPLT(imgInfo); - // apng - if (id.equals(PngChunkFDAT.ID)) - return new PngChunkFDAT(imgInfo); - if (id.equals(PngChunkACTL.ID)) - return new PngChunkACTL(imgInfo); - if (id.equals(PngChunkFCTL.ID)) - return new PngChunkFCTL(imgInfo); - return null; - } - - /** - * This is used as last resort factory method. - *

- * It creates a {@link PngChunkUNKNOWN} chunk. - */ - protected final PngChunk createEmptyChunkUnknown(final String id, final ImageInfo imgInfo) { - return new PngChunkUNKNOWN(id, imgInfo); - } - - /** - * Factory for chunks that are not in the original PNG standard. This can be - * overriden (but dont forget to call this - * also) - * - * @param id - * Chunk id , 4 letters - * @param imgInfo - * Usually not needed - * @return null if chunk id not recognized - */ - protected PngChunk createEmptyChunkExtended(final String id, final ImageInfo imgInfo) { - if (id.equals(PngChunkOFFS.ID)) - return new PngChunkOFFS(imgInfo); - if (id.equals(PngChunkSTER.ID)) - return new PngChunkSTER(imgInfo); - return null; // extend! - } - -} diff --git a/teavm/src/main/java/ar/com/hjg/pngj/chunks/ChunkHelper.java b/teavm/src/main/java/ar/com/hjg/pngj/chunks/ChunkHelper.java deleted file mode 100644 index 90fa8510..00000000 --- a/teavm/src/main/java/ar/com/hjg/pngj/chunks/ChunkHelper.java +++ /dev/null @@ -1,291 +0,0 @@ -package ar.com.hjg.pngj.chunks; - -// see http://www.libpng.org/pub/png/spec/1.2/PNG-Chunks.html -// http://www.w3.org/TR/PNG/#5Chunk-naming-conventions -// http://www.w3.org/TR/PNG/#table53 -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.UnsupportedEncodingException; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; -import java.util.zip.DeflaterOutputStream; -import java.util.zip.InflaterInputStream; - -import ar.com.hjg.pngj.PngHelperInternal; -import ar.com.hjg.pngj.PngjException; - -/** - * Helper methods and constants related to Chunk processing. - *

- * This should only be of interest to developers doing special chunk processing - * or extending the ChunkFactory - */ -public class ChunkHelper { - ChunkHelper() {} - - public static final String IHDR = "IHDR"; - public static final String PLTE = "PLTE"; - public static final String IDAT = "IDAT"; - public static final String IEND = "IEND"; - public static final String cHRM = "cHRM"; - public static final String gAMA = "gAMA"; - public static final String iCCP = "iCCP"; - public static final String sBIT = "sBIT"; - public static final String sRGB = "sRGB"; - public static final String bKGD = "bKGD"; - public static final String hIST = "hIST"; - public static final String tRNS = "tRNS"; - public static final String pHYs = "pHYs"; - public static final String sPLT = "sPLT"; - public static final String tIME = "tIME"; - public static final String iTXt = "iTXt"; - public static final String tEXt = "tEXt"; - public static final String zTXt = "zTXt"; - - public static final byte[] b_IHDR = ChunkHelper.toBytes(ChunkHelper.IHDR); - public static final byte[] b_PLTE = ChunkHelper.toBytes(ChunkHelper.PLTE); - public static final byte[] b_IDAT = ChunkHelper.toBytes(ChunkHelper.IDAT); - public static final byte[] b_IEND = ChunkHelper.toBytes(ChunkHelper.IEND); - - /* - * static auxiliary buffer. any method that uses this should synchronize against this - */ - private static byte[] tmpbuffer = new byte[4096]; - - /** - * Converts to bytes using Latin1 (ISO-8859-1) - */ - public static byte[] toBytes(final String x) { - try { - return x.getBytes(PngHelperInternal.charsetLatin1name); - } catch (final UnsupportedEncodingException e) { - throw new PngBadCharsetException(e); - } - } - - /** - * Converts to String using Latin1 (ISO-8859-1) - */ - public static String toString(final byte[] x) { - try { - return new String(x, PngHelperInternal.charsetLatin1name); - } catch (final UnsupportedEncodingException e) { - throw new PngBadCharsetException(e); - } - } - - /** - * Converts to String using Latin1 (ISO-8859-1) - */ - public static String toString(final byte[] x, final int offset, final int len) { - try { - return new String(x, offset, len, PngHelperInternal.charsetLatin1name); - } catch (final UnsupportedEncodingException e) { - throw new PngBadCharsetException(e); - } - } - - /** - * Converts to bytes using UTF-8 - */ - public static byte[] toBytesUTF8(final String x) { - try { - return x.getBytes(PngHelperInternal.charsetUTF8name); - } catch (final UnsupportedEncodingException e) { - throw new PngBadCharsetException(e); - } - } - - /** - * Converts to string using UTF-8 - */ - public static String toStringUTF8(final byte[] x) { - try { - return new String(x, PngHelperInternal.charsetUTF8name); - } catch (final UnsupportedEncodingException e) { - throw new PngBadCharsetException(e); - } - } - - /** - * Converts to string using UTF-8 - */ - public static String toStringUTF8(final byte[] x, final int offset, final int len) { - try { - return new String(x, offset, len, PngHelperInternal.charsetUTF8name); - } catch (final UnsupportedEncodingException e) { - throw new PngBadCharsetException(e); - } - } - - /** - * critical chunk : first letter is uppercase - */ - public static boolean isCritical(final String id) { - return Character.isUpperCase(id.charAt(0)); - } - - /** - * public chunk: second letter is uppercase - */ - public static boolean isPublic(final String id) { // - return Character.isUpperCase(id.charAt(1)); - } - - /** - * Safe to copy chunk: fourth letter is lower case - */ - public static boolean isSafeToCopy(final String id) { - return !Character.isUpperCase(id.charAt(3)); - } - - /** - * "Unknown" just means that our chunk factory (even when it has been - * augmented by client code) did not recognize its - * id - */ - public static boolean isUnknown(final PngChunk c) { - return c instanceof PngChunkUNKNOWN; - } - - /** - * Finds position of null byte in array - * - * @param b - * @return -1 if not found - */ - public static int posNullByte(final byte[] b) { - for (int i = 0; i < b.length; i++) - if (b[i] == 0) - return i; - return -1; - } - - /** - * Decides if a chunk should be loaded, according to a ChunkLoadBehaviour - * - * @param id - * @param behav - * @return true/false - */ - public static boolean shouldLoad(final String id, final ChunkLoadBehaviour behav) { - if (ChunkHelper.isCritical(id)) - return true; - switch (behav) { - case LOAD_CHUNK_ALWAYS: - return true; - case LOAD_CHUNK_IF_SAFE: - return ChunkHelper.isSafeToCopy(id); - case LOAD_CHUNK_NEVER: - return false; - case LOAD_CHUNK_MOST_IMPORTANT: - return id.equals(PngChunkTRNS.ID); - } - return false; // should not reach here - } - - public final static byte[] compressBytes(final byte[] ori, final boolean compress) { - return ChunkHelper.compressBytes(ori, 0, ori.length, compress); - } - - public static byte[] compressBytes(final byte[] ori, final int offset, final int len, final boolean compress) { - try { - final ByteArrayInputStream inb = new ByteArrayInputStream(ori, offset, len); - final InputStream in = compress ? inb : new InflaterInputStream(inb); - final ByteArrayOutputStream outb = new ByteArrayOutputStream(); - final OutputStream out = compress ? new DeflaterOutputStream(outb) : outb; - ChunkHelper.shovelInToOut(in, out); - in.close(); - out.close(); - return outb.toByteArray(); - } catch (final Exception e) { - throw new PngjException(e); - } - } - - /** - * Shovels all data from an input stream to an output stream. - */ - private static void shovelInToOut(final InputStream in, final OutputStream out) throws IOException { - synchronized (ChunkHelper.tmpbuffer) { - int len; - while ((len = in.read(ChunkHelper.tmpbuffer)) > 0) - out.write(ChunkHelper.tmpbuffer, 0, len); - } - } - - /** - * Returns only the chunks that "match" the predicate - * - * See also trimList() - */ - public static List filterList(final List target, final ChunkPredicate predicateKeep) { - final List result = new ArrayList<>(); - for (final PngChunk element : target) - if (predicateKeep.match(element)) - result.add(element); - return result; - } - - /** - * Remove (in place) the chunks that "match" the predicate - * - * See also filterList - */ - public static int trimList(final List target, final ChunkPredicate predicateRemove) { - final Iterator it = target.iterator(); - int cont = 0; - while (it.hasNext()) { - final PngChunk c = it.next(); - if (predicateRemove.match(c)) { - it.remove(); - cont++; - } - } - return cont; - } - - /** - * Adhoc criteria: two ancillary chunks are "equivalent" ("practically same - * type") if they have same id and (perhaps, - * if multiple are allowed) if the match also in some "internal key" (eg: - * key for string values, palette for sPLT, - * etc) - * - * When we use this method, we implicitly assume that we don't allow/expect - * two "equivalent" chunks in a single PNG - * - * Notice that the use of this is optional, and that the PNG standard - * actually allows text chunks that have same key - * - * @return true if "equivalent" - */ - public static final boolean equivalent(final PngChunk c1, final PngChunk c2) { - if (c1 == c2) - return true; - if (c1 == null || c2 == null || !c1.id.equals(c2.id)) - return false; - if (c1.crit) - return false; - // same id - if (c1.getClass() != c2.getClass()) - return false; // should not happen - if (!c2.allowsMultiple()) - return true; - if (c1 instanceof PngChunkTextVar) - return ((PngChunkTextVar) c1).getKey().equals(((PngChunkTextVar) c2).getKey()); - if (c1 instanceof PngChunkSPLT) - return ((PngChunkSPLT) c1).getPalName().equals(((PngChunkSPLT) c2).getPalName()); - // unknown chunks that allow multiple? consider they don't match - return false; - } - - public static boolean isText(final PngChunk c) { - return c instanceof PngChunkTextVar; - } - -} diff --git a/teavm/src/main/java/ar/com/hjg/pngj/chunks/ChunkLoadBehaviour.java b/teavm/src/main/java/ar/com/hjg/pngj/chunks/ChunkLoadBehaviour.java deleted file mode 100644 index 855b8061..00000000 --- a/teavm/src/main/java/ar/com/hjg/pngj/chunks/ChunkLoadBehaviour.java +++ /dev/null @@ -1,27 +0,0 @@ -package ar.com.hjg.pngj.chunks; - -/** - * What to do with ancillary (non-critical) chunks when reading. - *

- * - */ -public enum ChunkLoadBehaviour { - /** - * All non-critical chunks are skipped - */ - LOAD_CHUNK_NEVER, - /** - * Load chunk if "safe to copy" - */ - LOAD_CHUNK_IF_SAFE, - /** - * Load only most important chunk: TRNS - */ - LOAD_CHUNK_MOST_IMPORTANT, - /** - * Load all chunks.
- * Notice that other restrictions might apply, see - * PngReader.skipChunkMaxSize PngReader.skipChunkIds - */ - LOAD_CHUNK_ALWAYS; -} diff --git a/teavm/src/main/java/ar/com/hjg/pngj/chunks/ChunkPredicate.java b/teavm/src/main/java/ar/com/hjg/pngj/chunks/ChunkPredicate.java deleted file mode 100644 index 2f9da63a..00000000 --- a/teavm/src/main/java/ar/com/hjg/pngj/chunks/ChunkPredicate.java +++ /dev/null @@ -1,14 +0,0 @@ -package ar.com.hjg.pngj.chunks; - -/** - * Decides if another chunk "matches", according to some criterion - */ -public interface ChunkPredicate { - /** - * The other chunk matches with this one - * - * @param chunk - * @return true if match - */ - boolean match(PngChunk chunk); -} diff --git a/teavm/src/main/java/ar/com/hjg/pngj/chunks/ChunkRaw.java b/teavm/src/main/java/ar/com/hjg/pngj/chunks/ChunkRaw.java deleted file mode 100644 index 7e23476d..00000000 --- a/teavm/src/main/java/ar/com/hjg/pngj/chunks/ChunkRaw.java +++ /dev/null @@ -1,175 +0,0 @@ -package ar.com.hjg.pngj.chunks; - -import java.io.ByteArrayInputStream; -import java.io.OutputStream; -import java.util.zip.CRC32; - -import ar.com.hjg.pngj.PngHelperInternal; -import ar.com.hjg.pngj.PngjBadCrcException; -import ar.com.hjg.pngj.PngjException; -import ar.com.hjg.pngj.PngjOutputException; - -/** - * Raw (physical) chunk. - *

- * Short lived object, to be created while serialing/deserializing Do not reuse - * it for different chunks.
- * See http://www.libpng.org/pub/png/spec/1.2/PNG-Structure.html - */ -public class ChunkRaw { - /** - * The length counts only the data field, not itself, the chunk type code, - * or the CRC. Zero is a valid length. - * Although encoders and decoders should treat the length as unsigned, its - * value must not exceed 231-1 bytes. - */ - public final int len; - - /** - * A 4-byte chunk type code. uppercase and lowercase ASCII letters - */ - public final byte[] idbytes; - public final String id; - - /** - * The data bytes appropriate to the chunk type, if any. This field can be - * of zero length. Does not include crc. If - * it's null, it means that the data is ot available - */ - public byte[] data = null; - /** - * @see ChunkRaw#getOffset() - */ - private long offset = 0; - - /** - * A 4-byte CRC (Cyclic Redundancy Check) calculated on the preceding bytes - * in the chunk, including the chunk type - * code and chunk data fields, but not including the length field. - */ - public byte[] crcval = new byte[4]; - - private CRC32 crcengine; // lazily instantiated - - public ChunkRaw(final int len, final String id, final boolean alloc) { - this.len = len; - this.id = id; - idbytes = ChunkHelper.toBytes(id); - for (int i = 0; i < 4; i++) - if (idbytes[i] < 65 || idbytes[i] > 122 || idbytes[i] > 90 && idbytes[i] < 97) - throw new PngjException("Bad id chunk: must be ascii letters " + id); - if (alloc) - allocData(); - } - - public ChunkRaw(final int len, final byte[] idbytes, final boolean alloc) { - this(len, ChunkHelper.toString(idbytes), alloc); - } - - public void allocData() { // TODO: not public - if (data == null || data.length < len) - data = new byte[len]; - } - - /** - * this is called after setting data, before writing to os - */ - private void computeCrcForWriting() { - crcengine = new CRC32(); - crcengine.update(idbytes, 0, 4); - if (len > 0) - crcengine.update(data, 0, len); // - PngHelperInternal.writeInt4tobytes((int) crcengine.getValue(), crcval, 0); - } - - /** - * Computes the CRC and writes to the stream. If error, a - * PngjOutputException is thrown - * - * Note that this is only used for non idat chunks - */ - public void writeChunk(final OutputStream os) { - writeChunkHeader(os); - if (len > 0) { - if (data == null) - throw new PngjOutputException("cannot write chunk, raw chunk data is null [" + id + "]"); - PngHelperInternal.writeBytes(os, data, 0, len); - } - computeCrcForWriting(); - writeChunkCrc(os); - } - - public void writeChunkHeader(final OutputStream os) { - if (idbytes.length != 4) - throw new PngjOutputException("bad chunkid [" + id + "]"); - PngHelperInternal.writeInt4(os, len); - PngHelperInternal.writeBytes(os, idbytes); - } - - public void writeChunkCrc(final OutputStream os) { - PngHelperInternal.writeBytes(os, crcval, 0, 4); - } - - public void checkCrc() { - final int crcComputed = (int) crcengine.getValue(); - final int crcExpected = PngHelperInternal.readInt4fromBytes(crcval, 0); - if (crcComputed != crcExpected) - throw new PngjBadCrcException("chunk: " + toString() + " expected=" + crcExpected + " read=" + crcComputed); - } - - public void updateCrc(final byte[] buf, final int off, final int len) { - if (crcengine == null) - crcengine = new CRC32(); - crcengine.update(buf, off, len); - } - - ByteArrayInputStream getAsByteStream() { // only the data - return new ByteArrayInputStream(data); - } - - /** - * offset in the full PNG stream, in bytes. only informational, for read - * chunks (0=NA) - */ - public long getOffset() { - return offset; - } - - public void setOffset(final long offset) { - this.offset = offset; - } - - @Override - public String toString() { - return "chunkid=" + ChunkHelper.toString(idbytes) + " len=" + len; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + (id == null ? 0 : id.hashCode()); - result = prime * result + (int) (offset ^ offset >>> 32); - return result; - } - - @Override - public boolean equals(final Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - final ChunkRaw other = (ChunkRaw) obj; - if (id == null) { - if (other.id != null) - return false; - } else if (!id.equals(other.id)) - return false; - if (offset != other.offset) - return false; - return true; - } - -} diff --git a/teavm/src/main/java/ar/com/hjg/pngj/chunks/ChunksList.java b/teavm/src/main/java/ar/com/hjg/pngj/chunks/ChunksList.java deleted file mode 100644 index 50159b79..00000000 --- a/teavm/src/main/java/ar/com/hjg/pngj/chunks/ChunksList.java +++ /dev/null @@ -1,160 +0,0 @@ -package ar.com.hjg.pngj.chunks; - -import java.util.ArrayList; -import java.util.List; - -import ar.com.hjg.pngj.ImageInfo; -import ar.com.hjg.pngj.PngjException; - -/** - * All chunks that form an image, read or to be written. - *

- * chunks include all chunks, but IDAT is a single pseudo chunk without data - **/ -public class ChunksList { - // ref: http://www.w3.org/TR/PNG/#table53 - public static final int CHUNK_GROUP_0_IDHR = 0; // required - single - public static final int CHUNK_GROUP_1_AFTERIDHR = 1; // optional - multiple - public static final int CHUNK_GROUP_2_PLTE = 2; // optional - single - public static final int CHUNK_GROUP_3_AFTERPLTE = 3; // optional - multple - public static final int CHUNK_GROUP_4_IDAT = 4; // required (single pseudo chunk) - public static final int CHUNK_GROUP_5_AFTERIDAT = 5; // optional - multple - public static final int CHUNK_GROUP_6_END = 6; // only 1 chunk - requried - - /** - * All chunks, read (or written) - * - * But IDAT is a single pseudo chunk without data - */ - List chunks = new ArrayList<>(); - // protected HashMap> chunksById = new HashMap>(); - // // does not include IDAT - - final ImageInfo imageInfo; // only required for writing - - boolean withPlte = false; - - public ChunksList(final ImageInfo imfinfo) { - imageInfo = imfinfo; - } - - /** - * WARNING: this does NOT return a copy, but the list itself. The called - * should not modify this directly! Don't use - * this to manipulate the chunks. - */ - public List getChunks() { - return chunks; - } - - protected static List getXById(final List list, final String id, final String innerid) { - if (innerid == null) - return ChunkHelper.filterList(list, c -> c.id.equals(id)); - else - return ChunkHelper.filterList(list, c -> { - if (!c.id.equals(id)) - return false; - if (c instanceof PngChunkTextVar && !((PngChunkTextVar) c).getKey().equals(innerid)) - return false; - if (c instanceof PngChunkSPLT && !((PngChunkSPLT) c).getPalName().equals(innerid)) - return false; - return true; - }); - } - - /** - * Adds chunk in next position. This is used onyl by the pngReader - */ - public void appendReadChunk(final PngChunk chunk, final int chunkGroup) { - chunk.setChunkGroup(chunkGroup); - chunks.add(chunk); - if (chunk.id.equals(PngChunkPLTE.ID)) - withPlte = true; - } - - /** - * All chunks with this ID - * - * @param id - * @return List, empty if none - */ - public List getById(final String id) { - return getById(id, null); - } - - /** - * If innerid!=null and the chunk is PngChunkTextVar or PngChunkSPLT, it's - * filtered by that id - * - * @param id - * @return innerid Only used for text and SPLT chunks - * @return List, empty if none - */ - public List getById(final String id, final String innerid) { - return ChunksList.getXById(chunks, id, innerid); - } - - /** - * Returns only one chunk - * - * @param id - * @return First chunk found, null if not found - */ - public PngChunk getById1(final String id) { - return getById1(id, false); - } - - /** - * Returns only one chunk or null if nothing found - does not include queued - *

- * If more than one chunk is found, then an exception is thrown - * (failifMultiple=true or chunk is single) or the last - * one is returned (failifMultiple=false) - **/ - public PngChunk getById1(final String id, final boolean failIfMultiple) { - return getById1(id, null, failIfMultiple); - } - - /** - * Returns only one chunk or null if nothing found - does not include queued - *

- * If more than one chunk (after filtering by inner id) is found, then an - * exception is thrown (failifMultiple=true or - * chunk is single) or the last one is returned (failifMultiple=false) - **/ - public PngChunk getById1(final String id, final String innerid, final boolean failIfMultiple) { - final List list = getById(id, innerid); - if (list.isEmpty()) - return null; - if (list.size() > 1 && (failIfMultiple || !list.get(0).allowsMultiple())) - throw new PngjException("unexpected multiple chunks id=" + id); - return list.get(list.size() - 1); - } - - /** - * Finds all chunks "equivalent" to this one - * - * @param c2 - * @return Empty if nothing found - */ - public List getEquivalent(final PngChunk c2) { - return ChunkHelper.filterList(chunks, c -> ChunkHelper.equivalent(c, c2)); - } - - @Override - public String toString() { - return "ChunkList: read: " + chunks.size(); - } - - /** - * for debugging - */ - public String toStringFull() { - final StringBuilder sb = new StringBuilder(toString()); - sb.append("\n Read:\n"); - for (final PngChunk chunk : chunks) - sb.append(chunk).append(" G=" + chunk.getChunkGroup() + "\n"); - return sb.toString(); - } - -} diff --git a/teavm/src/main/java/ar/com/hjg/pngj/chunks/ChunksListForWrite.java b/teavm/src/main/java/ar/com/hjg/pngj/chunks/ChunksListForWrite.java deleted file mode 100644 index ec2d8669..00000000 --- a/teavm/src/main/java/ar/com/hjg/pngj/chunks/ChunksListForWrite.java +++ /dev/null @@ -1,185 +0,0 @@ -package ar.com.hjg.pngj.chunks; - -import java.io.OutputStream; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; - -import ar.com.hjg.pngj.ImageInfo; -import ar.com.hjg.pngj.PngjException; -import ar.com.hjg.pngj.PngjOutputException; - -public class ChunksListForWrite extends ChunksList { - - /** - * chunks not yet writen - does not include IHDR, IDAT, END, perhaps yes - * PLTE - */ - private final List queuedChunks = new ArrayList<>(); - - // redundant, just for eficciency - private final HashMap alreadyWrittenKeys = new HashMap<>(); - - public ChunksListForWrite(final ImageInfo imfinfo) { - super(imfinfo); - } - - /** - * Same as getById(), but looking in the queued chunks - */ - public List getQueuedById(final String id) { - return getQueuedById(id, null); - } - - /** - * Same as getById(), but looking in the queued chunks - */ - public List getQueuedById(final String id, final String innerid) { - return ChunksList.getXById(queuedChunks, id, innerid); - } - - /** - * Same as getById1(), but looking in the queued chunks - **/ - public PngChunk getQueuedById1(final String id, final String innerid, final boolean failIfMultiple) { - final List list = getQueuedById(id, innerid); - if (list.isEmpty()) - return null; - if (list.size() > 1 && (failIfMultiple || !list.get(0).allowsMultiple())) - throw new PngjException("unexpected multiple chunks id=" + id); - return list.get(list.size() - 1); - } - - /** - * Same as getById1(), but looking in the queued chunks - **/ - public PngChunk getQueuedById1(final String id, final boolean failIfMultiple) { - return getQueuedById1(id, null, failIfMultiple); - } - - /** - * Same as getById1(), but looking in the queued chunks - **/ - public PngChunk getQueuedById1(final String id) { - return getQueuedById1(id, false); - } - - /** - * Finds all chunks "equivalent" to this one - * - * @param c2 - * @return Empty if nothing found - */ - public List getQueuedEquivalent(final PngChunk c2) { - return ChunkHelper.filterList(queuedChunks, c -> ChunkHelper.equivalent(c, c2)); - } - - /** - * Remove Chunk: only from queued - * - * WARNING: this depends on c.equals() implementation, which is - * straightforward for SingleChunks. For MultipleChunks, - * it will normally check for reference equality! - */ - public boolean removeChunk(final PngChunk c) { - if (c == null) - return false; - return queuedChunks.remove(c); - } - - /** - * Adds chunk to queue - * - * If there - * - * @param c - */ - public boolean queue(final PngChunk c) { - queuedChunks.add(c); - return true; - } - - /** - * this should be called only for ancillary chunks and PLTE (groups 1 - 3 - - * 5) - **/ - private static boolean shouldWrite(final PngChunk c, final int currentGroup) { - if (currentGroup == ChunksList.CHUNK_GROUP_2_PLTE) - return c.id.equals(ChunkHelper.PLTE); - if (currentGroup % 2 == 0) - throw new PngjOutputException("bad chunk group?"); - int minChunkGroup, maxChunkGroup; - if (c.getOrderingConstraint().mustGoBeforePLTE()) - minChunkGroup = maxChunkGroup = ChunksList.CHUNK_GROUP_1_AFTERIDHR; - else if (c.getOrderingConstraint().mustGoBeforeIDAT()) { - maxChunkGroup = ChunksList.CHUNK_GROUP_3_AFTERPLTE; - minChunkGroup = c.getOrderingConstraint().mustGoAfterPLTE() ? ChunksList.CHUNK_GROUP_3_AFTERPLTE : ChunksList.CHUNK_GROUP_1_AFTERIDHR; - } else { - maxChunkGroup = ChunksList.CHUNK_GROUP_5_AFTERIDAT; - minChunkGroup = ChunksList.CHUNK_GROUP_1_AFTERIDHR; - } - - int preferred = maxChunkGroup; - if (c.hasPriority()) - preferred = minChunkGroup; - if (ChunkHelper.isUnknown(c) && c.getChunkGroup() > 0) - preferred = c.getChunkGroup(); - if (currentGroup == preferred) - return true; - if (currentGroup > preferred && currentGroup <= maxChunkGroup) - return true; - return false; - } - - public int writeChunks(final OutputStream os, final int currentGroup) { - int cont = 0; - final Iterator it = queuedChunks.iterator(); - while (it.hasNext()) { - final PngChunk c = it.next(); - if (!ChunksListForWrite.shouldWrite(c, currentGroup)) - continue; - if (ChunkHelper.isCritical(c.id) && !c.id.equals(ChunkHelper.PLTE)) - throw new PngjOutputException("bad chunk queued: " + c); - if (alreadyWrittenKeys.containsKey(c.id) && !c.allowsMultiple()) - throw new PngjOutputException("duplicated chunk does not allow multiple: " + c); - c.write(os); - chunks.add(c); - alreadyWrittenKeys.put(c.id, alreadyWrittenKeys.containsKey(c.id) ? alreadyWrittenKeys.get(c.id) + 1 : 1); - c.setChunkGroup(currentGroup); - it.remove(); - cont++; - } - return cont; - } - - /** - * warning: this is NOT a copy, do not modify - */ - public List getQueuedChunks() { - return queuedChunks; - } - - @Override - public String toString() { - return "ChunkList: written: " + getChunks().size() + " queue: " + queuedChunks.size(); - } - - /** - * for debugging - */ - @Override - public String toStringFull() { - final StringBuilder sb = new StringBuilder(toString()); - sb.append("\n Written:\n"); - for (final PngChunk chunk : getChunks()) - sb.append(chunk).append(" G=" + chunk.getChunkGroup() + "\n"); - if (!queuedChunks.isEmpty()) { - sb.append(" Queued:\n"); - for (final PngChunk chunk : queuedChunks) - sb.append(chunk).append("\n"); - - } - return sb.toString(); - } -} diff --git a/teavm/src/main/java/ar/com/hjg/pngj/chunks/PngBadCharsetException.java b/teavm/src/main/java/ar/com/hjg/pngj/chunks/PngBadCharsetException.java deleted file mode 100644 index fe9df9e7..00000000 --- a/teavm/src/main/java/ar/com/hjg/pngj/chunks/PngBadCharsetException.java +++ /dev/null @@ -1,20 +0,0 @@ -package ar.com.hjg.pngj.chunks; - -import ar.com.hjg.pngj.PngjException; - -public class PngBadCharsetException extends PngjException { - private static final long serialVersionUID = 1L; - - public PngBadCharsetException(final String message, final Throwable cause) { - super(message, cause); - } - - public PngBadCharsetException(final String message) { - super(message); - } - - public PngBadCharsetException(final Throwable cause) { - super(cause); - } - -} diff --git a/teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunk.java b/teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunk.java deleted file mode 100644 index 2ffb837c..00000000 --- a/teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunk.java +++ /dev/null @@ -1,227 +0,0 @@ -package ar.com.hjg.pngj.chunks; - -import java.io.OutputStream; - -import ar.com.hjg.pngj.ImageInfo; -import ar.com.hjg.pngj.PngjExceptionInternal; - -/** - * Represents a instance of a PNG chunk. - *

- * See - * http://www - * .libpng.org/pub/png/spec/1.2/PNG-Chunks .html - *

- * Concrete classes should extend {@link PngChunkSingle} or - * {@link PngChunkMultiple} - *

- * Note that some methods/fields are type-specific (getOrderingConstraint(), - * allowsMultiple()),
- * some are 'almost' type-specific (id,crit,pub,safe; the exception is - * PngUKNOWN),
- * and the rest are instance-specific - */ -public abstract class PngChunk { - - /** - * Chunk-id: 4 letters - */ - public final String id; - /** - * Autocomputed at creation time - */ - public final boolean crit, pub, safe; - - protected final ImageInfo imgInfo; - - protected ChunkRaw raw; - - private boolean priority = false; // For writing. Queued chunks with high priority will be written - // as soon as - // possible - - protected int chunkGroup = -1; // chunk group where it was read or writen - - /** - * Possible ordering constraint for a PngChunk type -only relevant for - * ancillary chunks. Theoretically, there could be - * more general constraints, but these cover the constraints for standard - * chunks. - */ - public enum ChunkOrderingConstraint { - /** - * no ordering constraint - */ - NONE, - /** - * Must go before PLTE (and hence, also before IDAT) - */ - BEFORE_PLTE_AND_IDAT, - /** - * Must go after PLTE (if exists) but before IDAT - */ - AFTER_PLTE_BEFORE_IDAT, - /** - * Must go after PLTE (and it must exist) but before IDAT - */ - AFTER_PLTE_BEFORE_IDAT_PLTE_REQUIRED, - /** - * Must before IDAT (before or after PLTE) - */ - BEFORE_IDAT, - /** - * After IDAT (this restriction does not apply to the standard PNG - * chunks) - */ - AFTER_IDAT, - /** - * Does not apply - */ - NA; - - public boolean mustGoBeforePLTE() { - return this == BEFORE_PLTE_AND_IDAT; - } - - public boolean mustGoBeforeIDAT() { - return this == BEFORE_IDAT || this == BEFORE_PLTE_AND_IDAT || this == AFTER_PLTE_BEFORE_IDAT; - } - - /** - * after pallete, if exists - */ - public boolean mustGoAfterPLTE() { - return this == AFTER_PLTE_BEFORE_IDAT || this == AFTER_PLTE_BEFORE_IDAT_PLTE_REQUIRED; - } - - public boolean mustGoAfterIDAT() { - return this == AFTER_IDAT; - } - - public boolean isOk(final int currentChunkGroup, final boolean hasplte) { - if (this == NONE) - return true; - else if (this == BEFORE_IDAT) - return currentChunkGroup < ChunksList.CHUNK_GROUP_4_IDAT; - else if (this == BEFORE_PLTE_AND_IDAT) - return currentChunkGroup < ChunksList.CHUNK_GROUP_2_PLTE; - else if (this == AFTER_PLTE_BEFORE_IDAT) - return hasplte ? currentChunkGroup < ChunksList.CHUNK_GROUP_4_IDAT : currentChunkGroup < ChunksList.CHUNK_GROUP_4_IDAT && currentChunkGroup > ChunksList.CHUNK_GROUP_2_PLTE; - else if (this == AFTER_IDAT) - return currentChunkGroup > ChunksList.CHUNK_GROUP_4_IDAT; - return false; - } - } - - public PngChunk(final String id, final ImageInfo imgInfo) { - this.id = id; - this.imgInfo = imgInfo; - crit = ChunkHelper.isCritical(id); - pub = ChunkHelper.isPublic(id); - safe = ChunkHelper.isSafeToCopy(id); - } - - protected final ChunkRaw createEmptyChunk(final int len, final boolean alloc) { - final ChunkRaw c = new ChunkRaw(len, ChunkHelper.toBytes(id), alloc); - return c; - } - - /** - * In which "chunkGroup" (see {@link ChunksList}for definition) this chunks - * instance was read or written. - *

- * -1 if not read or written (eg, queued) - */ - final public int getChunkGroup() { - return chunkGroup; - } - - /** - * @see #getChunkGroup() - */ - final void setChunkGroup(final int chunkGroup) { - this.chunkGroup = chunkGroup; - } - - public boolean hasPriority() { - return priority; - } - - public void setPriority(final boolean priority) { - this.priority = priority; - } - - final void write(final OutputStream os) { - if (raw == null || raw.data == null) - raw = createRawChunk(); - if (raw == null) - throw new PngjExceptionInternal("null chunk ! creation failed for " + this); - raw.writeChunk(os); - } - - /** - * Creates the physical chunk. This is used when writing (serialization). - * Each particular chunk class implements its - * own logic. - * - * @return A newly allocated and filled raw chunk - */ - public abstract ChunkRaw createRawChunk(); - - /** - * Parses raw chunk and fill inside data. This is used when reading - * (deserialization). Each particular chunk class - * implements its own logic. - */ - protected abstract void parseFromRaw(ChunkRaw c); - - /** - * See {@link PngChunkMultiple} and {@link PngChunkSingle} - * - * @return true if PNG accepts multiple chunks of this class - */ - protected abstract boolean allowsMultiple(); - - public ChunkRaw getRaw() { - return raw; - } - - void setRaw(final ChunkRaw raw) { - this.raw = raw; - } - - /** - * @see ChunkRaw#len - */ - public int getLen() { - return raw != null ? raw.len : -1; - } - - /** - * @see ChunkRaw#getOffset() - */ - public long getOffset() { - return raw != null ? raw.getOffset() : -1; - } - - /** - * This signals that the raw chunk (serialized data) as invalid, so that - * it's regenerated on write. This should be - * called for the (infrequent) case of chunks that were copied from a - * PngReader and we want to manually modify it. - */ - public void invalidateRawData() { - raw = null; - } - - /** - * see {@link ChunkOrderingConstraint} - */ - public abstract ChunkOrderingConstraint getOrderingConstraint(); - - @Override - public String toString() { - return "chunk id= " + id + " (len=" + getLen() + " offset=" + getOffset() + ")"; - } - -} diff --git a/teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkACTL.java b/teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkACTL.java deleted file mode 100644 index 3d179f06..00000000 --- a/teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkACTL.java +++ /dev/null @@ -1,57 +0,0 @@ -package ar.com.hjg.pngj.chunks; - -import ar.com.hjg.pngj.ImageInfo; -import ar.com.hjg.pngj.PngHelperInternal; - -/** - * acTL chunk. For APGN, not PGN standard - *

- * see - * https://wiki.mozilla.org/APNG_Specification#.60acTL.60:_The_Animation_Control_Chunk - *

- */ -public class PngChunkACTL extends PngChunkSingle { - public final static String ID = "acTL"; - private int numFrames; - private int numPlays; - - public PngChunkACTL(final ImageInfo info) { - super(PngChunkACTL.ID, info); - } - - @Override - public ChunkOrderingConstraint getOrderingConstraint() { - return ChunkOrderingConstraint.BEFORE_IDAT; - } - - @Override - public ChunkRaw createRawChunk() { - final ChunkRaw c = createEmptyChunk(8, true); - PngHelperInternal.writeInt4tobytes(numFrames, c.data, 0); - PngHelperInternal.writeInt4tobytes(numPlays, c.data, 4); - return c; - } - - @Override - public void parseFromRaw(final ChunkRaw chunk) { - numFrames = PngHelperInternal.readInt4fromBytes(chunk.data, 0); - numPlays = PngHelperInternal.readInt4fromBytes(chunk.data, 4); - } - - public int getNumFrames() { - return numFrames; - } - - public void setNumFrames(final int numFrames) { - this.numFrames = numFrames; - } - - public int getNumPlays() { - return numPlays; - } - - public void setNumPlays(final int numPlays) { - this.numPlays = numPlays; - } - -} diff --git a/teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkBKGD.java b/teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkBKGD.java deleted file mode 100644 index 6486c189..00000000 --- a/teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkBKGD.java +++ /dev/null @@ -1,112 +0,0 @@ -package ar.com.hjg.pngj.chunks; - -import ar.com.hjg.pngj.ImageInfo; -import ar.com.hjg.pngj.PngHelperInternal; -import ar.com.hjg.pngj.PngjException; - -/** - * bKGD Chunk. - *

- * see {@link http://www.w3.org/TR/PNG/#11bKGD} - *

- * This chunk structure depends on the image type - */ -public class PngChunkBKGD extends PngChunkSingle { - public final static String ID = ChunkHelper.bKGD; - // only one of these is meaningful - private int gray; - private int red, green, blue; - private int paletteIndex; - - public PngChunkBKGD(final ImageInfo info) { - super(ChunkHelper.bKGD, info); - } - - @Override - public ChunkOrderingConstraint getOrderingConstraint() { - return ChunkOrderingConstraint.AFTER_PLTE_BEFORE_IDAT; - } - - @Override - public ChunkRaw createRawChunk() { - ChunkRaw c = null; - if (imgInfo.greyscale) { - c = createEmptyChunk(2, true); - PngHelperInternal.writeInt2tobytes(gray, c.data, 0); - } else if (imgInfo.indexed) { - c = createEmptyChunk(1, true); - c.data[0] = (byte) paletteIndex; - } else { - c = createEmptyChunk(6, true); - PngHelperInternal.writeInt2tobytes(red, c.data, 0); - PngHelperInternal.writeInt2tobytes(green, c.data, 0); - PngHelperInternal.writeInt2tobytes(blue, c.data, 0); - } - return c; - } - - @Override - public void parseFromRaw(final ChunkRaw c) { - if (imgInfo.greyscale) - gray = PngHelperInternal.readInt2fromBytes(c.data, 0); - else if (imgInfo.indexed) - paletteIndex = c.data[0] & 0xff; - else { - red = PngHelperInternal.readInt2fromBytes(c.data, 0); - green = PngHelperInternal.readInt2fromBytes(c.data, 2); - blue = PngHelperInternal.readInt2fromBytes(c.data, 4); - } - } - - /** - * Set gray value (0-255 if bitdept=8) - * - * @param gray - */ - public void setGray(final int gray) { - if (!imgInfo.greyscale) - throw new PngjException("only gray images support this"); - this.gray = gray; - } - - public int getGray() { - if (!imgInfo.greyscale) - throw new PngjException("only gray images support this"); - return gray; - } - - /** - * Set pallette index - * - */ - public void setPaletteIndex(final int i) { - if (!imgInfo.indexed) - throw new PngjException("only indexed (pallete) images support this"); - paletteIndex = i; - } - - public int getPaletteIndex() { - if (!imgInfo.indexed) - throw new PngjException("only indexed (pallete) images support this"); - return paletteIndex; - } - - /** - * Set rgb values - * - */ - public void setRGB(final int r, final int g, final int b) { - if (imgInfo.greyscale || imgInfo.indexed) - throw new PngjException("only rgb or rgba images support this"); - red = r; - green = g; - blue = b; - } - - public int[] getRGB() { - if (imgInfo.greyscale || imgInfo.indexed) - throw new PngjException("only rgb or rgba images support this"); - return new int[] { red, green, blue }; - } - -} diff --git a/teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkCHRM.java b/teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkCHRM.java deleted file mode 100644 index 6086a7a2..00000000 --- a/teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkCHRM.java +++ /dev/null @@ -1,75 +0,0 @@ -package ar.com.hjg.pngj.chunks; - -import ar.com.hjg.pngj.ImageInfo; -import ar.com.hjg.pngj.PngHelperInternal; -import ar.com.hjg.pngj.PngjException; - -/** - * cHRM chunk. - *

- * see http://www.w3.org/TR/PNG/#11cHRM - */ -public class PngChunkCHRM extends PngChunkSingle { - public final static String ID = ChunkHelper.cHRM; - - // http://www.w3.org/TR/PNG/#11cHRM - private double whitex, whitey; - private double redx, redy; - private double greenx, greeny; - private double bluex, bluey; - - public PngChunkCHRM(final ImageInfo info) { - super(PngChunkCHRM.ID, info); - } - - @Override - public ChunkOrderingConstraint getOrderingConstraint() { - return ChunkOrderingConstraint.AFTER_PLTE_BEFORE_IDAT; - } - - @Override - public ChunkRaw createRawChunk() { - ChunkRaw c = null; - c = createEmptyChunk(32, true); - PngHelperInternal.writeInt4tobytes(PngHelperInternal.doubleToInt100000(whitex), c.data, 0); - PngHelperInternal.writeInt4tobytes(PngHelperInternal.doubleToInt100000(whitey), c.data, 4); - PngHelperInternal.writeInt4tobytes(PngHelperInternal.doubleToInt100000(redx), c.data, 8); - PngHelperInternal.writeInt4tobytes(PngHelperInternal.doubleToInt100000(redy), c.data, 12); - PngHelperInternal.writeInt4tobytes(PngHelperInternal.doubleToInt100000(greenx), c.data, 16); - PngHelperInternal.writeInt4tobytes(PngHelperInternal.doubleToInt100000(greeny), c.data, 20); - PngHelperInternal.writeInt4tobytes(PngHelperInternal.doubleToInt100000(bluex), c.data, 24); - PngHelperInternal.writeInt4tobytes(PngHelperInternal.doubleToInt100000(bluey), c.data, 28); - return c; - } - - @Override - public void parseFromRaw(final ChunkRaw c) { - if (c.len != 32) - throw new PngjException("bad chunk " + c); - whitex = PngHelperInternal.intToDouble100000(PngHelperInternal.readInt4fromBytes(c.data, 0)); - whitey = PngHelperInternal.intToDouble100000(PngHelperInternal.readInt4fromBytes(c.data, 4)); - redx = PngHelperInternal.intToDouble100000(PngHelperInternal.readInt4fromBytes(c.data, 8)); - redy = PngHelperInternal.intToDouble100000(PngHelperInternal.readInt4fromBytes(c.data, 12)); - greenx = PngHelperInternal.intToDouble100000(PngHelperInternal.readInt4fromBytes(c.data, 16)); - greeny = PngHelperInternal.intToDouble100000(PngHelperInternal.readInt4fromBytes(c.data, 20)); - bluex = PngHelperInternal.intToDouble100000(PngHelperInternal.readInt4fromBytes(c.data, 24)); - bluey = PngHelperInternal.intToDouble100000(PngHelperInternal.readInt4fromBytes(c.data, 28)); - } - - public void setChromaticities(final double whitex, final double whitey, final double redx, final double redy, - final double greenx, final double greeny, final double bluex, final double bluey) { - this.whitex = whitex; - this.redx = redx; - this.greenx = greenx; - this.bluex = bluex; - this.whitey = whitey; - this.redy = redy; - this.greeny = greeny; - this.bluey = bluey; - } - - public double[] getChromaticities() { - return new double[] { whitex, whitey, redx, redy, greenx, greeny, bluex, bluey }; - } - -} diff --git a/teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkFCTL.java b/teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkFCTL.java deleted file mode 100644 index 4a8c8d1f..00000000 --- a/teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkFCTL.java +++ /dev/null @@ -1,158 +0,0 @@ -package ar.com.hjg.pngj.chunks; - -import ar.com.hjg.pngj.ImageInfo; -import ar.com.hjg.pngj.PngHelperInternal; - -/** - * fcTL chunk. For APGN, not PGN standard - *

- * see - * https://wiki.mozilla.org/APNG_Specification#.60fcTL.60:_The_Frame_Control_Chunk - *

- */ -public class PngChunkFCTL extends PngChunkMultiple { - public final static String ID = "fcTL"; - - public final static byte APNG_DISPOSE_OP_NONE = 0; - public final static byte APNG_DISPOSE_OP_BACKGROUND = 1; - public final static byte APNG_DISPOSE_OP_PREVIOUS = 2; - public final static byte APNG_BLEND_OP_SOURCE = 0; - public final static byte APNG_BLEND_OP_OVER = 1; - - private int seqNum; - private int width, height, xOff, yOff; - private int delayNum, delayDen; - private byte disposeOp, blendOp; - - public PngChunkFCTL(final ImageInfo info) { - super(PngChunkFCTL.ID, info); - } - - public ImageInfo getEquivImageInfo() { - return new ImageInfo(width, height, imgInfo.bitDepth, imgInfo.alpha, imgInfo.greyscale, imgInfo.indexed); - } - - @Override - public ChunkOrderingConstraint getOrderingConstraint() { - return ChunkOrderingConstraint.NONE; - } - - @Override - public ChunkRaw createRawChunk() { - final ChunkRaw c = createEmptyChunk(8, true); - int off = 0; - PngHelperInternal.writeInt4tobytes(seqNum, c.data, off); - off += 4; - PngHelperInternal.writeInt4tobytes(width, c.data, off); - off += 4; - PngHelperInternal.writeInt4tobytes(height, c.data, off); - off += 4; - PngHelperInternal.writeInt4tobytes(xOff, c.data, off); - off += 4; - PngHelperInternal.writeInt4tobytes(yOff, c.data, off); - off += 4; - PngHelperInternal.writeInt2tobytes(delayNum, c.data, off); - off += 2; - PngHelperInternal.writeInt2tobytes(delayDen, c.data, off); - off += 2; - c.data[off] = disposeOp; - off += 1; - c.data[off] = blendOp; - return c; - } - - @Override - public void parseFromRaw(final ChunkRaw chunk) { - int off = 0; - seqNum = PngHelperInternal.readInt4fromBytes(chunk.data, off); - off += 4; - width = PngHelperInternal.readInt4fromBytes(chunk.data, off); - off += 4; - height = PngHelperInternal.readInt4fromBytes(chunk.data, off); - off += 4; - xOff = PngHelperInternal.readInt4fromBytes(chunk.data, off); - off += 4; - yOff = PngHelperInternal.readInt4fromBytes(chunk.data, off); - off += 4; - delayNum = PngHelperInternal.readInt2fromBytes(chunk.data, off); - off += 2; - delayDen = PngHelperInternal.readInt2fromBytes(chunk.data, off); - off += 2; - disposeOp = chunk.data[off]; - off += 1; - blendOp = chunk.data[off]; - } - - public int getSeqNum() { - return seqNum; - } - - public void setSeqNum(final int seqNum) { - this.seqNum = seqNum; - } - - public int getWidth() { - return width; - } - - public void setWidth(final int width) { - this.width = width; - } - - public int getHeight() { - return height; - } - - public void setHeight(final int height) { - this.height = height; - } - - public int getxOff() { - return xOff; - } - - public void setxOff(final int xOff) { - this.xOff = xOff; - } - - public int getyOff() { - return yOff; - } - - public void setyOff(final int yOff) { - this.yOff = yOff; - } - - public int getDelayNum() { - return delayNum; - } - - public void setDelayNum(final int delayNum) { - this.delayNum = delayNum; - } - - public int getDelayDen() { - return delayDen; - } - - public void setDelayDen(final int delayDen) { - this.delayDen = delayDen; - } - - public byte getDisposeOp() { - return disposeOp; - } - - public void setDisposeOp(final byte disposeOp) { - this.disposeOp = disposeOp; - } - - public byte getBlendOp() { - return blendOp; - } - - public void setBlendOp(final byte blendOp) { - this.blendOp = blendOp; - } - -} diff --git a/teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkFDAT.java b/teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkFDAT.java deleted file mode 100644 index 47449a00..00000000 --- a/teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkFDAT.java +++ /dev/null @@ -1,72 +0,0 @@ -package ar.com.hjg.pngj.chunks; - -import ar.com.hjg.pngj.ImageInfo; -import ar.com.hjg.pngj.PngHelperInternal; -import ar.com.hjg.pngj.PngjException; - -/** - * fdAT chunk. For APGN, not PGN standard - *

- * see - * https://wiki.mozilla.org/APNG_Specification#.60fdAT.60:_The_Frame_Data_Chunk - *

- * This implementation does not support buffering, this should be not managed - * similar to a IDAT chunk - * - */ -public class PngChunkFDAT extends PngChunkMultiple { - public final static String ID = "fdAT"; - private int seqNum; - private byte[] buffer; // normally not allocated - if so, it's the raw data, so it includes the 4bytes seqNum - int datalen; // length of idat data, excluding seqNUm (= chunk.len-4) - - public PngChunkFDAT(final ImageInfo info) { - super(PngChunkFDAT.ID, info); - } - - @Override - public ChunkOrderingConstraint getOrderingConstraint() { - return ChunkOrderingConstraint.AFTER_IDAT; - } - - @Override - public ChunkRaw createRawChunk() { - if (buffer == null) - throw new PngjException("not buffered"); - final ChunkRaw c = createEmptyChunk(datalen + 4, false); - c.data = buffer; // shallow copy! - return c; - } - - @Override - public void parseFromRaw(final ChunkRaw chunk) { - seqNum = PngHelperInternal.readInt4fromBytes(chunk.data, 0); - datalen = chunk.len - 4; - buffer = chunk.data; - } - - public int getSeqNum() { - return seqNum; - } - - public void setSeqNum(final int seqNum) { - this.seqNum = seqNum; - } - - public byte[] getBuffer() { - return buffer; - } - - public void setBuffer(final byte[] buffer) { - this.buffer = buffer; - } - - public int getDatalen() { - return datalen; - } - - public void setDatalen(final int datalen) { - this.datalen = datalen; - } - -} diff --git a/teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkGAMA.java b/teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkGAMA.java deleted file mode 100644 index 3b70654b..00000000 --- a/teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkGAMA.java +++ /dev/null @@ -1,51 +0,0 @@ -package ar.com.hjg.pngj.chunks; - -import ar.com.hjg.pngj.ImageInfo; -import ar.com.hjg.pngj.PngHelperInternal; -import ar.com.hjg.pngj.PngjException; - -/** - * gAMA chunk. - *

- * see http://www.w3.org/TR/PNG/#11gAMA - */ -public class PngChunkGAMA extends PngChunkSingle { - public final static String ID = ChunkHelper.gAMA; - - // http://www.w3.org/TR/PNG/#11gAMA - private double gamma; - - public PngChunkGAMA(final ImageInfo info) { - super(PngChunkGAMA.ID, info); - } - - @Override - public ChunkOrderingConstraint getOrderingConstraint() { - return ChunkOrderingConstraint.BEFORE_PLTE_AND_IDAT; - } - - @Override - public ChunkRaw createRawChunk() { - final ChunkRaw c = createEmptyChunk(4, true); - final int g = (int) (gamma * 100000 + 0.5); - PngHelperInternal.writeInt4tobytes(g, c.data, 0); - return c; - } - - @Override - public void parseFromRaw(final ChunkRaw chunk) { - if (chunk.len != 4) - throw new PngjException("bad chunk " + chunk); - final int g = PngHelperInternal.readInt4fromBytes(chunk.data, 0); - gamma = g / 100000.0; - } - - public double getGamma() { - return gamma; - } - - public void setGamma(final double gamma) { - this.gamma = gamma; - } - -} diff --git a/teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkHIST.java b/teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkHIST.java deleted file mode 100644 index 7d84aed8..00000000 --- a/teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkHIST.java +++ /dev/null @@ -1,56 +0,0 @@ -package ar.com.hjg.pngj.chunks; - -import ar.com.hjg.pngj.ImageInfo; -import ar.com.hjg.pngj.PngHelperInternal; -import ar.com.hjg.pngj.PngjException; - -/** - * hIST chunk. - *

- * see http://www.w3.org/TR/PNG/#11hIST
- * only for palette images - */ -public class PngChunkHIST extends PngChunkSingle { - public final static String ID = ChunkHelper.hIST; - - private int[] hist = new int[0]; // should have same lenght as palette - - public PngChunkHIST(final ImageInfo info) { - super(PngChunkHIST.ID, info); - } - - @Override - public ChunkOrderingConstraint getOrderingConstraint() { - return ChunkOrderingConstraint.AFTER_PLTE_BEFORE_IDAT; - } - - @Override - public void parseFromRaw(final ChunkRaw c) { - if (!imgInfo.indexed) - throw new PngjException("only indexed images accept a HIST chunk"); - final int nentries = c.data.length / 2; - hist = new int[nentries]; - for (int i = 0; i < hist.length; i++) - hist[i] = PngHelperInternal.readInt2fromBytes(c.data, i * 2); - } - - @Override - public ChunkRaw createRawChunk() { - if (!imgInfo.indexed) - throw new PngjException("only indexed images accept a HIST chunk"); - ChunkRaw c = null; - c = createEmptyChunk(hist.length * 2, true); - for (int i = 0; i < hist.length; i++) - PngHelperInternal.writeInt2tobytes(hist[i], c.data, i * 2); - return c; - } - - public int[] getHist() { - return hist; - } - - public void setHist(final int[] hist) { - this.hist = hist; - } - -} diff --git a/teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkICCP.java b/teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkICCP.java deleted file mode 100644 index caa2de2a..00000000 --- a/teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkICCP.java +++ /dev/null @@ -1,76 +0,0 @@ -package ar.com.hjg.pngj.chunks; - -import ar.com.hjg.pngj.ImageInfo; -import ar.com.hjg.pngj.PngjException; - -/** - * iCCP chunk. - *

- * See {@link http://www.w3.org/TR/PNG/#11iCCP} - */ -public class PngChunkICCP extends PngChunkSingle { - public final static String ID = ChunkHelper.iCCP; - - // http://www.w3.org/TR/PNG/#11iCCP - private String profileName; - private byte[] compressedProfile; // copmression/decopmresion is done in getter/setter - - public PngChunkICCP(final ImageInfo info) { - super(PngChunkICCP.ID, info); - } - - @Override - public ChunkOrderingConstraint getOrderingConstraint() { - return ChunkOrderingConstraint.BEFORE_PLTE_AND_IDAT; - } - - @Override - public ChunkRaw createRawChunk() { - final ChunkRaw c = createEmptyChunk(profileName.length() + compressedProfile.length + 2, true); - System.arraycopy(ChunkHelper.toBytes(profileName), 0, c.data, 0, profileName.length()); - c.data[profileName.length()] = 0; - c.data[profileName.length() + 1] = 0; - System.arraycopy(compressedProfile, 0, c.data, profileName.length() + 2, compressedProfile.length); - return c; - } - - @Override - public void parseFromRaw(final ChunkRaw chunk) { - final int pos0 = ChunkHelper.posNullByte(chunk.data); - profileName = ChunkHelper.toString(chunk.data, 0, pos0); - final int comp = chunk.data[pos0 + 1] & 0xff; - if (comp != 0) - throw new PngjException("bad compression for ChunkTypeICCP"); - final int compdatasize = chunk.data.length - (pos0 + 2); - compressedProfile = new byte[compdatasize]; - System.arraycopy(chunk.data, pos0 + 2, compressedProfile, 0, compdatasize); - } - - /** - * The profile should be uncompressed bytes - */ - public void setProfileNameAndContent(final String name, final byte[] profile) { - profileName = name; - compressedProfile = ChunkHelper.compressBytes(profile, true); - } - - public void setProfileNameAndContent(final String name, final String profile) { - setProfileNameAndContent(name, ChunkHelper.toBytes(profile)); - } - - public String getProfileName() { - return profileName; - } - - /** - * uncompressed - **/ - public byte[] getProfile() { - return ChunkHelper.compressBytes(compressedProfile, false); - } - - public String getProfileAsString() { - return ChunkHelper.toString(getProfile()); - } - -} diff --git a/teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkIDAT.java b/teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkIDAT.java deleted file mode 100644 index febd1469..00000000 --- a/teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkIDAT.java +++ /dev/null @@ -1,35 +0,0 @@ -package ar.com.hjg.pngj.chunks; - -import ar.com.hjg.pngj.ImageInfo; - -/** - * IDAT chunk. - *

- * see http://www.w3.org/TR/PNG/#11IDAT - *

- * This is dummy placeholder - we write/read this chunk (actually several) by - * special code. - */ -public class PngChunkIDAT extends PngChunkMultiple { - public final static String ID = ChunkHelper.IDAT; - - // http://www.w3.org/TR/PNG/#11IDAT - public PngChunkIDAT(final ImageInfo i) { - super(PngChunkIDAT.ID, i); - } - - @Override - public ChunkOrderingConstraint getOrderingConstraint() { - return ChunkOrderingConstraint.NA; - } - - @Override - public ChunkRaw createRawChunk() {// does nothing - return null; - } - - @Override - public void parseFromRaw(final ChunkRaw c) { // does nothing - } - -} diff --git a/teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkIEND.java b/teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkIEND.java deleted file mode 100644 index 01fb6259..00000000 --- a/teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkIEND.java +++ /dev/null @@ -1,35 +0,0 @@ -package ar.com.hjg.pngj.chunks; - -import ar.com.hjg.pngj.ImageInfo; - -/** - * IEND chunk. - *

- * see http://www.w3.org/TR/PNG/#11IEND - */ -public class PngChunkIEND extends PngChunkSingle { - public final static String ID = ChunkHelper.IEND; - - // http://www.w3.org/TR/PNG/#11IEND - // this is a dummy placeholder - public PngChunkIEND(final ImageInfo info) { - super(PngChunkIEND.ID, info); - } - - @Override - public ChunkOrderingConstraint getOrderingConstraint() { - return ChunkOrderingConstraint.NA; - } - - @Override - public ChunkRaw createRawChunk() { - final ChunkRaw c = new ChunkRaw(0, ChunkHelper.b_IEND, false); - return c; - } - - @Override - public void parseFromRaw(final ChunkRaw c) { - // this is not used - } - -} diff --git a/teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkIHDR.java b/teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkIHDR.java deleted file mode 100644 index ddcbd5ec..00000000 --- a/teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkIHDR.java +++ /dev/null @@ -1,184 +0,0 @@ -package ar.com.hjg.pngj.chunks; - -import java.io.ByteArrayInputStream; - -import ar.com.hjg.pngj.ImageInfo; -import ar.com.hjg.pngj.PngHelperInternal; -import ar.com.hjg.pngj.PngjException; -import ar.com.hjg.pngj.PngjInputException; - -/** - * IHDR chunk. - *

- * see http://www.w3.org/TR/PNG/#11IHDR - *

- * This is a special critical Chunk. - */ -public class PngChunkIHDR extends PngChunkSingle { - public final static String ID = ChunkHelper.IHDR; - - private int cols; - private int rows; - private int bitspc; - private int colormodel; - private int compmeth; - private int filmeth; - private int interlaced; - - // http://www.w3.org/TR/PNG/#11IHDR - // - public PngChunkIHDR(final ImageInfo info) { // argument is normally null here, if not null is used to fill the fields - super(PngChunkIHDR.ID, info); - if (info != null) - fillFromInfo(info); - } - - @Override - public ChunkOrderingConstraint getOrderingConstraint() { - return ChunkOrderingConstraint.NA; - } - - @Override - public ChunkRaw createRawChunk() { - final ChunkRaw c = new ChunkRaw(13, ChunkHelper.b_IHDR, true); - int offset = 0; - PngHelperInternal.writeInt4tobytes(cols, c.data, offset); - offset += 4; - PngHelperInternal.writeInt4tobytes(rows, c.data, offset); - offset += 4; - c.data[offset++] = (byte) bitspc; - c.data[offset++] = (byte) colormodel; - c.data[offset++] = (byte) compmeth; - c.data[offset++] = (byte) filmeth; - c.data[offset++] = (byte) interlaced; - return c; - } - - @Override - public void parseFromRaw(final ChunkRaw c) { - if (c.len != 13) - throw new PngjException("Bad IDHR len " + c.len); - final ByteArrayInputStream st = c.getAsByteStream(); - cols = PngHelperInternal.readInt4(st); - rows = PngHelperInternal.readInt4(st); - // bit depth: number of bits per channel - bitspc = PngHelperInternal.readByte(st); - colormodel = PngHelperInternal.readByte(st); - compmeth = PngHelperInternal.readByte(st); - filmeth = PngHelperInternal.readByte(st); - interlaced = PngHelperInternal.readByte(st); - } - - public int getCols() { - return cols; - } - - public void setCols(final int cols) { - this.cols = cols; - } - - public int getRows() { - return rows; - } - - public void setRows(final int rows) { - this.rows = rows; - } - - public int getBitspc() { - return bitspc; - } - - public void setBitspc(final int bitspc) { - this.bitspc = bitspc; - } - - public int getColormodel() { - return colormodel; - } - - public void setColormodel(final int colormodel) { - this.colormodel = colormodel; - } - - public int getCompmeth() { - return compmeth; - } - - public void setCompmeth(final int compmeth) { - this.compmeth = compmeth; - } - - public int getFilmeth() { - return filmeth; - } - - public void setFilmeth(final int filmeth) { - this.filmeth = filmeth; - } - - public int getInterlaced() { - return interlaced; - } - - public void setInterlaced(final int interlaced) { - this.interlaced = interlaced; - } - - public boolean isInterlaced() { - return getInterlaced() == 1; - } - - public void fillFromInfo(final ImageInfo info) { - setCols(imgInfo.cols); - setRows(imgInfo.rows); - setBitspc(imgInfo.bitDepth); - int colormodel = 0; - if (imgInfo.alpha) - colormodel += 0x04; - if (imgInfo.indexed) - colormodel += 0x01; - if (!imgInfo.greyscale) - colormodel += 0x02; - setColormodel(colormodel); - setCompmeth(0); // compression method 0=deflate - setFilmeth(0); // filter method (0) - setInterlaced(0); // we never interlace - } - - /** throws PngInputException if unexpected values */ - public ImageInfo createImageInfo() { - check(); - final boolean alpha = (getColormodel() & 0x04) != 0; - final boolean palette = (getColormodel() & 0x01) != 0; - final boolean grayscale = getColormodel() == 0 || getColormodel() == 4; - // creates ImgInfo and imgLine, and allocates buffers - return new ImageInfo(getCols(), getRows(), getBitspc(), alpha, grayscale, palette); - } - - public void check() { - if (cols < 1 || rows < 1 || compmeth != 0 || filmeth != 0) - throw new PngjInputException("bad IHDR: col/row/compmethod/filmethod invalid"); - if (bitspc != 1 && bitspc != 2 && bitspc != 4 && bitspc != 8 && bitspc != 16) - throw new PngjInputException("bad IHDR: bitdepth invalid"); - if (interlaced < 0 || interlaced > 1) - throw new PngjInputException("bad IHDR: interlace invalid"); - switch (colormodel) { - case 0: - break; - case 3: - if (bitspc == 16) - throw new PngjInputException("bad IHDR: bitdepth invalid"); - break; - case 2: - case 4: - case 6: - if (bitspc != 8 && bitspc != 16) - throw new PngjInputException("bad IHDR: bitdepth invalid"); - break; - default: - throw new PngjInputException("bad IHDR: invalid colormodel"); - } - } - -} diff --git a/teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkITXT.java b/teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkITXT.java deleted file mode 100644 index d9d1bb83..00000000 --- a/teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkITXT.java +++ /dev/null @@ -1,108 +0,0 @@ -package ar.com.hjg.pngj.chunks; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; - -import ar.com.hjg.pngj.ImageInfo; -import ar.com.hjg.pngj.PngjException; - -/** - * iTXt chunk. - *

- * see http://www.w3.org/TR/PNG/#11iTXt - */ -public class PngChunkITXT extends PngChunkTextVar { - public final static String ID = ChunkHelper.iTXt; - - private boolean compressed = false; - private String langTag = ""; - private String translatedTag = ""; - - // http://www.w3.org/TR/PNG/#11iTXt - public PngChunkITXT(final ImageInfo info) { - super(PngChunkITXT.ID, info); - } - - @Override - public ChunkRaw createRawChunk() { - if (key == null || key.trim().length() == 0) - throw new PngjException("Text chunk key must be non empty"); - try { - final ByteArrayOutputStream ba = new ByteArrayOutputStream(); - ba.write(ChunkHelper.toBytes(key)); - ba.write(0); // separator - ba.write(compressed ? 1 : 0); - ba.write(0); // compression method (always 0) - ba.write(ChunkHelper.toBytes(langTag)); - ba.write(0); // separator - ba.write(ChunkHelper.toBytesUTF8(translatedTag)); - ba.write(0); // separator - byte[] textbytes = ChunkHelper.toBytesUTF8(val); - if (compressed) - textbytes = ChunkHelper.compressBytes(textbytes, true); - ba.write(textbytes); - final byte[] b = ba.toByteArray(); - final ChunkRaw chunk = createEmptyChunk(b.length, false); - chunk.data = b; - return chunk; - } catch (final IOException e) { - throw new PngjException(e); - } - } - - @Override - public void parseFromRaw(final ChunkRaw c) { - int nullsFound = 0; - final int[] nullsIdx = new int[3]; - for (int i = 0; i < c.data.length; i++) { - if (c.data[i] != 0) - continue; - nullsIdx[nullsFound] = i; - nullsFound++; - if (nullsFound == 1) - i += 2; - if (nullsFound == 3) - break; - } - if (nullsFound != 3) - throw new PngjException("Bad formed PngChunkITXT chunk"); - key = ChunkHelper.toString(c.data, 0, nullsIdx[0]); - int i = nullsIdx[0] + 1; - compressed = c.data[i] == 0 ? false : true; - i++; - if (compressed && c.data[i] != 0) - throw new PngjException("Bad formed PngChunkITXT chunk - bad compression method "); - langTag = ChunkHelper.toString(c.data, i, nullsIdx[1] - i); - translatedTag = ChunkHelper.toStringUTF8(c.data, nullsIdx[1] + 1, nullsIdx[2] - nullsIdx[1] - 1); - i = nullsIdx[2] + 1; - if (compressed) { - final byte[] bytes = ChunkHelper.compressBytes(c.data, i, c.data.length - i, false); - val = ChunkHelper.toStringUTF8(bytes); - } else - val = ChunkHelper.toStringUTF8(c.data, i, c.data.length - i); - } - - public boolean isCompressed() { - return compressed; - } - - public void setCompressed(final boolean compressed) { - this.compressed = compressed; - } - - public String getLangtag() { - return langTag; - } - - public void setLangtag(final String langtag) { - langTag = langtag; - } - - public String getTranslatedTag() { - return translatedTag; - } - - public void setTranslatedTag(final String translatedTag) { - this.translatedTag = translatedTag; - } -} diff --git a/teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkMultiple.java b/teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkMultiple.java deleted file mode 100644 index 0f822d24..00000000 --- a/teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkMultiple.java +++ /dev/null @@ -1,28 +0,0 @@ -package ar.com.hjg.pngj.chunks; - -import ar.com.hjg.pngj.ImageInfo; - -/** - * PNG chunk type (abstract) that allows multiple instances in same image. - */ -public abstract class PngChunkMultiple extends PngChunk { - - protected PngChunkMultiple(final String id, final ImageInfo imgInfo) { - super(id, imgInfo); - } - - @Override - public final boolean allowsMultiple() { - return true; - } - - /** - * NOTE: this chunk uses the default Object's equals() hashCode() - * implementation. - * - * This is the right thing to do, normally. - * - * This is important, eg see ChunkList.removeFromList() - */ - -} diff --git a/teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkOFFS.java b/teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkOFFS.java deleted file mode 100644 index 2f762a6c..00000000 --- a/teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkOFFS.java +++ /dev/null @@ -1,81 +0,0 @@ -package ar.com.hjg.pngj.chunks; - -import ar.com.hjg.pngj.ImageInfo; -import ar.com.hjg.pngj.PngHelperInternal; -import ar.com.hjg.pngj.PngjException; - -/** - * oFFs chunk. - *

- * see http://www.libpng.org/pub/png/spec/register/pngext-1.3.0-pdg.html#C.oFFs - */ -public class PngChunkOFFS extends PngChunkSingle { - public final static String ID = "oFFs"; - - // http://www.libpng.org/pub/png/spec/register/pngext-1.3.0-pdg.html#C.oFFs - private long posX; - private long posY; - private int units; // 0: pixel 1:micrometer - - public PngChunkOFFS(final ImageInfo info) { - super(PngChunkOFFS.ID, info); - } - - @Override - public ChunkOrderingConstraint getOrderingConstraint() { - return ChunkOrderingConstraint.BEFORE_IDAT; - } - - @Override - public ChunkRaw createRawChunk() { - final ChunkRaw c = createEmptyChunk(9, true); - PngHelperInternal.writeInt4tobytes((int) posX, c.data, 0); - PngHelperInternal.writeInt4tobytes((int) posY, c.data, 4); - c.data[8] = (byte) units; - return c; - } - - @Override - public void parseFromRaw(final ChunkRaw chunk) { - if (chunk.len != 9) - throw new PngjException("bad chunk length " + chunk); - posX = PngHelperInternal.readInt4fromBytes(chunk.data, 0); - if (posX < 0) - posX += 0x100000000L; - posY = PngHelperInternal.readInt4fromBytes(chunk.data, 4); - if (posY < 0) - posY += 0x100000000L; - units = PngHelperInternal.readInt1fromByte(chunk.data, 8); - } - - /** - * 0: pixel, 1:micrometer - */ - public int getUnits() { - return units; - } - - /** - * 0: pixel, 1:micrometer - */ - public void setUnits(final int units) { - this.units = units; - } - - public long getPosX() { - return posX; - } - - public void setPosX(final long posX) { - this.posX = posX; - } - - public long getPosY() { - return posY; - } - - public void setPosY(final long posY) { - this.posY = posY; - } - -} diff --git a/teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkPHYS.java b/teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkPHYS.java deleted file mode 100644 index acce8ed3..00000000 --- a/teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkPHYS.java +++ /dev/null @@ -1,107 +0,0 @@ -package ar.com.hjg.pngj.chunks; - -import ar.com.hjg.pngj.ImageInfo; -import ar.com.hjg.pngj.PngHelperInternal; -import ar.com.hjg.pngj.PngjException; - -/** - * pHYs chunk. - *

- * see http://www.w3.org/TR/PNG/#11pHYs - */ -public class PngChunkPHYS extends PngChunkSingle { - public final static String ID = ChunkHelper.pHYs; - - // http://www.w3.org/TR/PNG/#11pHYs - private long pixelsxUnitX; - private long pixelsxUnitY; - private int units; // 0: unknown 1:metre - - public PngChunkPHYS(final ImageInfo info) { - super(PngChunkPHYS.ID, info); - } - - @Override - public ChunkOrderingConstraint getOrderingConstraint() { - return ChunkOrderingConstraint.BEFORE_IDAT; - } - - @Override - public ChunkRaw createRawChunk() { - final ChunkRaw c = createEmptyChunk(9, true); - PngHelperInternal.writeInt4tobytes((int) pixelsxUnitX, c.data, 0); - PngHelperInternal.writeInt4tobytes((int) pixelsxUnitY, c.data, 4); - c.data[8] = (byte) units; - return c; - } - - @Override - public void parseFromRaw(final ChunkRaw chunk) { - if (chunk.len != 9) - throw new PngjException("bad chunk length " + chunk); - pixelsxUnitX = PngHelperInternal.readInt4fromBytes(chunk.data, 0); - if (pixelsxUnitX < 0) - pixelsxUnitX += 0x100000000L; - pixelsxUnitY = PngHelperInternal.readInt4fromBytes(chunk.data, 4); - if (pixelsxUnitY < 0) - pixelsxUnitY += 0x100000000L; - units = PngHelperInternal.readInt1fromByte(chunk.data, 8); - } - - public long getPixelsxUnitX() { - return pixelsxUnitX; - } - - public void setPixelsxUnitX(final long pixelsxUnitX) { - this.pixelsxUnitX = pixelsxUnitX; - } - - public long getPixelsxUnitY() { - return pixelsxUnitY; - } - - public void setPixelsxUnitY(final long pixelsxUnitY) { - this.pixelsxUnitY = pixelsxUnitY; - } - - public int getUnits() { - return units; - } - - public void setUnits(final int units) { - this.units = units; - } - - // special getters / setters - - /** - * returns -1 if the physicial unit is unknown, or X-Y are not equal - */ - public double getAsDpi() { - if (units != 1 || pixelsxUnitX != pixelsxUnitY) - return -1; - return pixelsxUnitX * 0.0254; - } - - /** - * returns -1 if the physicial unit is unknown - */ - public double[] getAsDpi2() { - if (units != 1) - return new double[] { -1, -1 }; - return new double[] { pixelsxUnitX * 0.0254, pixelsxUnitY * 0.0254 }; - } - - public void setAsDpi(final double dpi) { - units = 1; - pixelsxUnitX = (long) (dpi / 0.0254 + 0.5); - pixelsxUnitY = pixelsxUnitX; - } - - public void setAsDpi2(final double dpix, final double dpiy) { - units = 1; - pixelsxUnitX = (long) (dpix / 0.0254 + 0.5); - pixelsxUnitY = (long) (dpiy / 0.0254 + 0.5); - } - -} diff --git a/teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkPLTE.java b/teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkPLTE.java deleted file mode 100644 index a0cfe734..00000000 --- a/teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkPLTE.java +++ /dev/null @@ -1,95 +0,0 @@ -package ar.com.hjg.pngj.chunks; - -import ar.com.hjg.pngj.ImageInfo; -import ar.com.hjg.pngj.PngjException; - -/** - * PLTE chunk. - *

- * see http://www.w3.org/TR/PNG/#11PLTE - *

- * Critical chunk - */ -public class PngChunkPLTE extends PngChunkSingle { - public final static String ID = ChunkHelper.PLTE; - - // http://www.w3.org/TR/PNG/#11PLTE - private int nentries = 0; - /** - * RGB8 packed in one integer - */ - private int[] entries; - - public PngChunkPLTE(final ImageInfo info) { - super(PngChunkPLTE.ID, info); - } - - @Override - public ChunkOrderingConstraint getOrderingConstraint() { - return ChunkOrderingConstraint.NA; - } - - @Override - public ChunkRaw createRawChunk() { - final int len = 3 * nentries; - final int[] rgb = new int[3]; - final ChunkRaw c = createEmptyChunk(len, true); - for (int n = 0, i = 0; n < nentries; n++) { - getEntryRgb(n, rgb); - c.data[i++] = (byte) rgb[0]; - c.data[i++] = (byte) rgb[1]; - c.data[i++] = (byte) rgb[2]; - } - return c; - } - - @Override - public void parseFromRaw(final ChunkRaw chunk) { - setNentries(chunk.len / 3); - for (int n = 0, i = 0; n < nentries; n++) - setEntry(n, chunk.data[i++] & 0xff, chunk.data[i++] & 0xff, chunk.data[i++] & 0xff); - } - - public void setNentries(final int n) { - nentries = n; - if (nentries < 1 || nentries > 256) - throw new PngjException("invalid pallette - nentries=" + nentries); - if (entries == null || entries.length != nentries) - entries = new int[nentries]; - } - - public int getNentries() { - return nentries; - } - - public void setEntry(final int n, final int r, final int g, final int b) { - entries[n] = r << 16 | g << 8 | b; - } - - public int getEntry(final int n) { - return entries[n]; - } - - public void getEntryRgb(final int n, final int[] rgb) { - getEntryRgb(n, rgb, 0); - } - - public void getEntryRgb(final int n, final int[] rgb, final int offset) { - final int v = entries[n]; - rgb[offset + 0] = (v & 0xff0000) >> 16; - rgb[offset + 1] = (v & 0xff00) >> 8; - rgb[offset + 2] = v & 0xff; - } - - public int minBitDepth() { - if (nentries <= 2) - return 1; - else if (nentries <= 4) - return 2; - else if (nentries <= 16) - return 4; - else - return 8; - } - -} diff --git a/teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkSBIT.java b/teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkSBIT.java deleted file mode 100644 index f782b0ea..00000000 --- a/teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkSBIT.java +++ /dev/null @@ -1,114 +0,0 @@ -package ar.com.hjg.pngj.chunks; - -import ar.com.hjg.pngj.ImageInfo; -import ar.com.hjg.pngj.PngHelperInternal; -import ar.com.hjg.pngj.PngjException; - -/** - * sBIT chunk. - *

- * see http://www.w3.org/TR/PNG/#11sBIT - *

- * this chunk structure depends on the image type - */ -public class PngChunkSBIT extends PngChunkSingle { - public final static String ID = ChunkHelper.sBIT; - // http://www.w3.org/TR/PNG/#11sBIT - - // significant bits - private int graysb, alphasb; - private int redsb, greensb, bluesb; - - public PngChunkSBIT(final ImageInfo info) { - super(PngChunkSBIT.ID, info); - } - - @Override - public ChunkOrderingConstraint getOrderingConstraint() { - return ChunkOrderingConstraint.BEFORE_PLTE_AND_IDAT; - } - - private int getCLen() { - int len = imgInfo.greyscale ? 1 : 3; - if (imgInfo.alpha) - len += 1; - return len; - } - - @Override - public void parseFromRaw(final ChunkRaw c) { - if (c.len != getCLen()) - throw new PngjException("bad chunk length " + c); - if (imgInfo.greyscale) { - graysb = PngHelperInternal.readInt1fromByte(c.data, 0); - if (imgInfo.alpha) - alphasb = PngHelperInternal.readInt1fromByte(c.data, 1); - } else { - redsb = PngHelperInternal.readInt1fromByte(c.data, 0); - greensb = PngHelperInternal.readInt1fromByte(c.data, 1); - bluesb = PngHelperInternal.readInt1fromByte(c.data, 2); - if (imgInfo.alpha) - alphasb = PngHelperInternal.readInt1fromByte(c.data, 3); - } - } - - @Override - public ChunkRaw createRawChunk() { - ChunkRaw c = null; - c = createEmptyChunk(getCLen(), true); - if (imgInfo.greyscale) { - c.data[0] = (byte) graysb; - if (imgInfo.alpha) - c.data[1] = (byte) alphasb; - } else { - c.data[0] = (byte) redsb; - c.data[1] = (byte) greensb; - c.data[2] = (byte) bluesb; - if (imgInfo.alpha) - c.data[3] = (byte) alphasb; - } - return c; - } - - public void setGraysb(final int gray) { - if (!imgInfo.greyscale) - throw new PngjException("only greyscale images support this"); - graysb = gray; - } - - public int getGraysb() { - if (!imgInfo.greyscale) - throw new PngjException("only greyscale images support this"); - return graysb; - } - - public void setAlphasb(final int a) { - if (!imgInfo.alpha) - throw new PngjException("only images with alpha support this"); - alphasb = a; - } - - public int getAlphasb() { - if (!imgInfo.alpha) - throw new PngjException("only images with alpha support this"); - return alphasb; - } - - /** - * Set rgb values - * - */ - public void setRGB(final int r, final int g, final int b) { - if (imgInfo.greyscale || imgInfo.indexed) - throw new PngjException("only rgb or rgba images support this"); - redsb = r; - greensb = g; - bluesb = b; - } - - public int[] getRGB() { - if (imgInfo.greyscale || imgInfo.indexed) - throw new PngjException("only rgb or rgba images support this"); - return new int[] { redsb, greensb, bluesb }; - } -} diff --git a/teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkSPLT.java b/teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkSPLT.java deleted file mode 100644 index 423b41ff..00000000 --- a/teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkSPLT.java +++ /dev/null @@ -1,129 +0,0 @@ -package ar.com.hjg.pngj.chunks; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; - -import ar.com.hjg.pngj.ImageInfo; -import ar.com.hjg.pngj.PngHelperInternal; -import ar.com.hjg.pngj.PngjException; - -/** - * sPLT chunk. - *

- * see http://www.w3.org/TR/PNG/#11sPLT - */ -public class PngChunkSPLT extends PngChunkMultiple { - public final static String ID = ChunkHelper.sPLT; - - // http://www.w3.org/TR/PNG/#11sPLT - - private String palName; - private int sampledepth; // 8/16 - private int[] palette; // 5 elements per entry - - public PngChunkSPLT(final ImageInfo info) { - super(PngChunkSPLT.ID, info); - } - - @Override - public ChunkOrderingConstraint getOrderingConstraint() { - return ChunkOrderingConstraint.BEFORE_IDAT; - } - - @Override - public ChunkRaw createRawChunk() { - try { - final ByteArrayOutputStream ba = new ByteArrayOutputStream(); - ba.write(ChunkHelper.toBytes(palName)); - ba.write(0); // separator - ba.write((byte) sampledepth); - final int nentries = getNentries(); - for (int n = 0; n < nentries; n++) { - for (int i = 0; i < 4; i++) - if (sampledepth == 8) - PngHelperInternal.writeByte(ba, (byte) palette[n * 5 + i]); - else - PngHelperInternal.writeInt2(ba, palette[n * 5 + i]); - PngHelperInternal.writeInt2(ba, palette[n * 5 + 4]); - } - final byte[] b = ba.toByteArray(); - final ChunkRaw chunk = createEmptyChunk(b.length, false); - chunk.data = b; - return chunk; - } catch (final IOException e) { - throw new PngjException(e); - } - } - - @Override - public void parseFromRaw(final ChunkRaw c) { - int t = -1; - for (int i = 0; i < c.data.length; i++) - if (c.data[i] == 0) { - t = i; - break; - } - if (t <= 0 || t > c.data.length - 2) - throw new PngjException("bad sPLT chunk: no separator found"); - palName = ChunkHelper.toString(c.data, 0, t); - sampledepth = PngHelperInternal.readInt1fromByte(c.data, t + 1); - t += 2; - final int nentries = (c.data.length - t) / (sampledepth == 8 ? 6 : 10); - palette = new int[nentries * 5]; - int r, g, b, a, f, ne; - ne = 0; - for (int i = 0; i < nentries; i++) { - if (sampledepth == 8) { - r = PngHelperInternal.readInt1fromByte(c.data, t++); - g = PngHelperInternal.readInt1fromByte(c.data, t++); - b = PngHelperInternal.readInt1fromByte(c.data, t++); - a = PngHelperInternal.readInt1fromByte(c.data, t++); - } else { - r = PngHelperInternal.readInt2fromBytes(c.data, t); - t += 2; - g = PngHelperInternal.readInt2fromBytes(c.data, t); - t += 2; - b = PngHelperInternal.readInt2fromBytes(c.data, t); - t += 2; - a = PngHelperInternal.readInt2fromBytes(c.data, t); - t += 2; - } - f = PngHelperInternal.readInt2fromBytes(c.data, t); - t += 2; - palette[ne++] = r; - palette[ne++] = g; - palette[ne++] = b; - palette[ne++] = a; - palette[ne++] = f; - } - } - - public int getNentries() { - return palette.length / 5; - } - - public String getPalName() { - return palName; - } - - public void setPalName(final String palName) { - this.palName = palName; - } - - public int getSampledepth() { - return sampledepth; - } - - public void setSampledepth(final int sampledepth) { - this.sampledepth = sampledepth; - } - - public int[] getPalette() { - return palette; - } - - public void setPalette(final int[] palette) { - this.palette = palette; - } - -} diff --git a/teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkSRGB.java b/teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkSRGB.java deleted file mode 100644 index de261e79..00000000 --- a/teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkSRGB.java +++ /dev/null @@ -1,55 +0,0 @@ -package ar.com.hjg.pngj.chunks; - -import ar.com.hjg.pngj.ImageInfo; -import ar.com.hjg.pngj.PngHelperInternal; -import ar.com.hjg.pngj.PngjException; - -/** - * sRGB chunk. - *

- * see http://www.w3.org/TR/PNG/#11sRGB - */ -public class PngChunkSRGB extends PngChunkSingle { - public final static String ID = ChunkHelper.sRGB; - - // http://www.w3.org/TR/PNG/#11sRGB - - public static final int RENDER_INTENT_Perceptual = 0; - public static final int RENDER_INTENT_Relative_colorimetric = 1; - public static final int RENDER_INTENT_Saturation = 2; - public static final int RENDER_INTENT_Absolute_colorimetric = 3; - - private int intent; - - public PngChunkSRGB(final ImageInfo info) { - super(PngChunkSRGB.ID, info); - } - - @Override - public ChunkOrderingConstraint getOrderingConstraint() { - return ChunkOrderingConstraint.BEFORE_PLTE_AND_IDAT; - } - - @Override - public void parseFromRaw(final ChunkRaw c) { - if (c.len != 1) - throw new PngjException("bad chunk length " + c); - intent = PngHelperInternal.readInt1fromByte(c.data, 0); - } - - @Override - public ChunkRaw createRawChunk() { - ChunkRaw c = null; - c = createEmptyChunk(1, true); - c.data[0] = (byte) intent; - return c; - } - - public int getIntent() { - return intent; - } - - public void setIntent(final int intent) { - this.intent = intent; - } -} diff --git a/teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkSTER.java b/teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkSTER.java deleted file mode 100644 index fe135767..00000000 --- a/teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkSTER.java +++ /dev/null @@ -1,54 +0,0 @@ -package ar.com.hjg.pngj.chunks; - -import ar.com.hjg.pngj.ImageInfo; -import ar.com.hjg.pngj.PngjException; - -/** - * sTER chunk. - *

- * see http://www.libpng.org/pub/png/spec/register/pngext-1.3.0-pdg.html#C.sTER - */ -public class PngChunkSTER extends PngChunkSingle { - public final static String ID = "sTER"; - - // http://www.libpng.org/pub/png/spec/register/pngext-1.3.0-pdg.html#C.sTER - private byte mode; // 0: cross-fuse layout 1: diverging-fuse layout - - public PngChunkSTER(final ImageInfo info) { - super(PngChunkSTER.ID, info); - } - - @Override - public ChunkOrderingConstraint getOrderingConstraint() { - return ChunkOrderingConstraint.BEFORE_IDAT; - } - - @Override - public ChunkRaw createRawChunk() { - final ChunkRaw c = createEmptyChunk(1, true); - c.data[0] = mode; - return c; - } - - @Override - public void parseFromRaw(final ChunkRaw chunk) { - if (chunk.len != 1) - throw new PngjException("bad chunk length " + chunk); - mode = chunk.data[0]; - } - - /** - * 0: cross-fuse layout 1: diverging-fuse layout - */ - public byte getMode() { - return mode; - } - - /** - * 0: cross-fuse layout 1: diverging-fuse layout - */ - public void setMode(final byte mode) { - this.mode = mode; - } - -} diff --git a/teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkSingle.java b/teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkSingle.java deleted file mode 100644 index a98df4e8..00000000 --- a/teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkSingle.java +++ /dev/null @@ -1,45 +0,0 @@ -package ar.com.hjg.pngj.chunks; - -import ar.com.hjg.pngj.ImageInfo; - -/** - * PNG chunk type (abstract) that does not allow multiple instances in same - * image. - */ -public abstract class PngChunkSingle extends PngChunk { - - protected PngChunkSingle(final String id, final ImageInfo imgInfo) { - super(id, imgInfo); - } - - @Override - public final boolean allowsMultiple() { - return false; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + (id == null ? 0 : id.hashCode()); - return result; - } - - @Override - public boolean equals(final Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - final PngChunkSingle other = (PngChunkSingle) obj; - if (id == null) { - if (other.id != null) - return false; - } else if (!id.equals(other.id)) - return false; - return true; - } - -} diff --git a/teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkTEXT.java b/teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkTEXT.java deleted file mode 100644 index efaa62d4..00000000 --- a/teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkTEXT.java +++ /dev/null @@ -1,44 +0,0 @@ -package ar.com.hjg.pngj.chunks; - -import ar.com.hjg.pngj.ImageInfo; -import ar.com.hjg.pngj.PngjException; - -/** - * tEXt chunk. - *

- * see http://www.w3.org/TR/PNG/#11tEXt - */ -public class PngChunkTEXT extends PngChunkTextVar { - public final static String ID = ChunkHelper.tEXt; - - public PngChunkTEXT(final ImageInfo info) { - super(PngChunkTEXT.ID, info); - } - - public PngChunkTEXT(final ImageInfo info, final String key, final String val) { - super(PngChunkTEXT.ID, info); - setKeyVal(key, val); - } - - @Override - public ChunkRaw createRawChunk() { - if (key == null || key.trim().length() == 0) - throw new PngjException("Text chunk key must be non empty"); - final byte[] b = ChunkHelper.toBytes(key + "\0" + val); - final ChunkRaw chunk = createEmptyChunk(b.length, false); - chunk.data = b; - return chunk; - } - - @Override - public void parseFromRaw(final ChunkRaw c) { - int i; - for (i = 0; i < c.data.length; i++) - if (c.data[i] == 0) - break; - key = ChunkHelper.toString(c.data, 0, i); - i++; - val = i < c.data.length ? ChunkHelper.toString(c.data, i, c.data.length - i) : ""; - } - -} diff --git a/teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkTIME.java b/teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkTIME.java deleted file mode 100644 index 37fab915..00000000 --- a/teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkTIME.java +++ /dev/null @@ -1,83 +0,0 @@ -package ar.com.hjg.pngj.chunks; - -import java.util.Calendar; - -import ar.com.hjg.pngj.ImageInfo; -import ar.com.hjg.pngj.PngHelperInternal; -import ar.com.hjg.pngj.PngjException; - -/** - * tIME chunk. - *

- * see http://www.w3.org/TR/PNG/#11tIME - */ -public class PngChunkTIME extends PngChunkSingle { - public final static String ID = ChunkHelper.tIME; - - // http://www.w3.org/TR/PNG/#11tIME - private int year, mon, day, hour, min, sec; - - public PngChunkTIME(final ImageInfo info) { - super(PngChunkTIME.ID, info); - } - - @Override - public ChunkOrderingConstraint getOrderingConstraint() { - return ChunkOrderingConstraint.NONE; - } - - @Override - public ChunkRaw createRawChunk() { - final ChunkRaw c = createEmptyChunk(7, true); - PngHelperInternal.writeInt2tobytes(year, c.data, 0); - c.data[2] = (byte) mon; - c.data[3] = (byte) day; - c.data[4] = (byte) hour; - c.data[5] = (byte) min; - c.data[6] = (byte) sec; - return c; - } - - @Override - public void parseFromRaw(final ChunkRaw chunk) { - if (chunk.len != 7) - throw new PngjException("bad chunk " + chunk); - year = PngHelperInternal.readInt2fromBytes(chunk.data, 0); - mon = PngHelperInternal.readInt1fromByte(chunk.data, 2); - day = PngHelperInternal.readInt1fromByte(chunk.data, 3); - hour = PngHelperInternal.readInt1fromByte(chunk.data, 4); - min = PngHelperInternal.readInt1fromByte(chunk.data, 5); - sec = PngHelperInternal.readInt1fromByte(chunk.data, 6); - } - - public void setNow(final int secsAgo) { - final Calendar d = Calendar.getInstance(); - d.setTimeInMillis(System.currentTimeMillis() - 1000 * (long) secsAgo); - year = d.get(Calendar.YEAR); - mon = d.get(Calendar.MONTH) + 1; - day = d.get(Calendar.DAY_OF_MONTH); - hour = d.get(Calendar.HOUR_OF_DAY); - min = d.get(Calendar.MINUTE); - sec = d.get(Calendar.SECOND); - } - - public void setYMDHMS(final int yearx, final int monx, final int dayx, final int hourx, final int minx, - final int secx) { - year = yearx; - mon = monx; - day = dayx; - hour = hourx; - min = minx; - sec = secx; - } - - public int[] getYMDHMS() { - return new int[] { year, mon, day, hour, min, sec }; - } - - /** format YYYY/MM/DD HH:mm:SS */ - public String getAsString() { - return String.format("%04d/%02d/%02d %02d:%02d:%02d", year, mon, day, hour, min, sec); - } - -} diff --git a/teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkTRNS.java b/teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkTRNS.java deleted file mode 100644 index dd507304..00000000 --- a/teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkTRNS.java +++ /dev/null @@ -1,150 +0,0 @@ -package ar.com.hjg.pngj.chunks; - -import ar.com.hjg.pngj.ImageInfo; -import ar.com.hjg.pngj.PngHelperInternal; -import ar.com.hjg.pngj.PngjException; - -/** - * tRNS chunk. - *

- * see http://www.w3.org/TR/PNG/#11tRNS - *

- * this chunk structure depends on the image type - */ -public class PngChunkTRNS extends PngChunkSingle { - public final static String ID = ChunkHelper.tRNS; - - // http://www.w3.org/TR/PNG/#11tRNS - - // only one of these is meaningful, depending on the image type - private int gray; - private int red, green, blue; - private int[] paletteAlpha = new int[] {}; - - public PngChunkTRNS(final ImageInfo info) { - super(PngChunkTRNS.ID, info); - } - - @Override - public ChunkOrderingConstraint getOrderingConstraint() { - return ChunkOrderingConstraint.AFTER_PLTE_BEFORE_IDAT; - } - - @Override - public ChunkRaw createRawChunk() { - ChunkRaw c = null; - if (imgInfo.greyscale) { - c = createEmptyChunk(2, true); - PngHelperInternal.writeInt2tobytes(gray, c.data, 0); - } else if (imgInfo.indexed) { - c = createEmptyChunk(paletteAlpha.length, true); - for (int n = 0; n < c.len; n++) - c.data[n] = (byte) paletteAlpha[n]; - } else { - c = createEmptyChunk(6, true); - PngHelperInternal.writeInt2tobytes(red, c.data, 0); - PngHelperInternal.writeInt2tobytes(green, c.data, 0); - PngHelperInternal.writeInt2tobytes(blue, c.data, 0); - } - return c; - } - - @Override - public void parseFromRaw(final ChunkRaw c) { - if (imgInfo.greyscale) - gray = PngHelperInternal.readInt2fromBytes(c.data, 0); - else if (imgInfo.indexed) { - final int nentries = c.data.length; - paletteAlpha = new int[nentries]; - for (int n = 0; n < nentries; n++) - paletteAlpha[n] = c.data[n] & 0xff; - } else { - red = PngHelperInternal.readInt2fromBytes(c.data, 0); - green = PngHelperInternal.readInt2fromBytes(c.data, 2); - blue = PngHelperInternal.readInt2fromBytes(c.data, 4); - } - } - - /** - * Set rgb values - * - */ - public void setRGB(final int r, final int g, final int b) { - if (imgInfo.greyscale || imgInfo.indexed) - throw new PngjException("only rgb or rgba images support this"); - red = r; - green = g; - blue = b; - } - - public int[] getRGB() { - if (imgInfo.greyscale || imgInfo.indexed) - throw new PngjException("only rgb or rgba images support this"); - return new int[] { red, green, blue }; - } - - public int getRGB888() { - if (imgInfo.greyscale || imgInfo.indexed) - throw new PngjException("only rgb or rgba images support this"); - return red << 16 | green << 8 | blue; - } - - public void setGray(final int g) { - if (!imgInfo.greyscale) - throw new PngjException("only grayscale images support this"); - gray = g; - } - - public int getGray() { - if (!imgInfo.greyscale) - throw new PngjException("only grayscale images support this"); - return gray; - } - - /** - * Sets the length of the palette alpha. This should be followed by - * #setNentriesPalAlpha - * - * @param idx - * index inside the table - * @param val - * alpha value (0-255) - */ - public void setEntryPalAlpha(final int idx, final int val) { - paletteAlpha[idx] = val; - } - - public void setNentriesPalAlpha(final int len) { - paletteAlpha = new int[len]; - } - - /** - * WARNING: non deep copy. See also {@link #setNentriesPalAlpha(int)} - * {@link #setEntryPalAlpha(int, int)} - */ - public void setPalAlpha(final int[] palAlpha) { - if (!imgInfo.indexed) - throw new PngjException("only indexed images support this"); - paletteAlpha = palAlpha; - } - - /** - * WARNING: non deep copy - */ - public int[] getPalletteAlpha() { - return paletteAlpha; - } - - /** - * to use when only one pallete index is set as totally transparent - */ - public void setIndexEntryAsTransparent(final int palAlphaIndex) { - if (!imgInfo.indexed) - throw new PngjException("only indexed images support this"); - paletteAlpha = new int[] { palAlphaIndex + 1 }; - for (int i = 0; i < palAlphaIndex; i++) - paletteAlpha[i] = 255; - paletteAlpha[palAlphaIndex] = 0; - } - -} diff --git a/teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkTextVar.java b/teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkTextVar.java deleted file mode 100644 index 1591011a..00000000 --- a/teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkTextVar.java +++ /dev/null @@ -1,60 +0,0 @@ -package ar.com.hjg.pngj.chunks; - -import ar.com.hjg.pngj.ImageInfo; - -/** - * Superclass (abstract) for three textual chunks (TEXT, ITXT, ZTXT) - */ -public abstract class PngChunkTextVar extends PngChunkMultiple { - protected String key; // key/val: only for tEXt. lazy computed - protected String val; - - // http://www.w3.org/TR/PNG/#11keywords - public final static String KEY_Title = "Title"; // Short (one line) title or caption for image - public final static String KEY_Author = "Author"; // Name of image's creator - public final static String KEY_Description = "Description"; // Description of image (possibly - // long) - public final static String KEY_Copyright = "Copyright"; // Copyright notice - public final static String KEY_Creation_Time = "Creation Time"; // Time of original image creation - public final static String KEY_Software = "Software"; // Software used to create the image - public final static String KEY_Disclaimer = "Disclaimer"; // Legal disclaimer - public final static String KEY_Warning = "Warning"; // Warning of nature of content - public final static String KEY_Source = "Source"; // Device used to create the image - public final static String KEY_Comment = "Comment"; // Miscellaneous comment - - protected PngChunkTextVar(final String id, final ImageInfo info) { - super(id, info); - } - - @Override - public ChunkOrderingConstraint getOrderingConstraint() { - return ChunkOrderingConstraint.NONE; - } - - public static class PngTxtInfo { - public String title; - public String author; - public String description; - public String creation_time;// = (new Date()).toString(); - public String software; - public String disclaimer; - public String warning; - public String source; - public String comment; - - } - - public String getKey() { - return key; - } - - public String getVal() { - return val; - } - - public void setKeyVal(final String key, final String val) { - this.key = key; - this.val = val; - } - -} diff --git a/teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkUNKNOWN.java b/teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkUNKNOWN.java deleted file mode 100644 index dbc8ad08..00000000 --- a/teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkUNKNOWN.java +++ /dev/null @@ -1,40 +0,0 @@ -package ar.com.hjg.pngj.chunks; - -import ar.com.hjg.pngj.ImageInfo; - -/** - * Placeholder for UNKNOWN (custom or not) chunks. - *

- * For PngReader, a chunk is unknown if it's not registered in the chunk factory - */ -public class PngChunkUNKNOWN extends PngChunkMultiple { // unkown, custom or not - - public PngChunkUNKNOWN(final String id, final ImageInfo info) { - super(id, info); - } - - @Override - public ChunkOrderingConstraint getOrderingConstraint() { - return ChunkOrderingConstraint.NONE; - } - - @Override - public ChunkRaw createRawChunk() { - return raw; - } - - @Override - public void parseFromRaw(final ChunkRaw c) { - - } - - /* does not do deep copy! */ - public byte[] getData() { - return raw.data; - } - - /* does not do deep copy! */ - public void setData(final byte[] data) { - raw.data = data; - } -} diff --git a/teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkZTXT.java b/teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkZTXT.java deleted file mode 100644 index 7ad9e69b..00000000 --- a/teavm/src/main/java/ar/com/hjg/pngj/chunks/PngChunkZTXT.java +++ /dev/null @@ -1,61 +0,0 @@ -package ar.com.hjg.pngj.chunks; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; - -import ar.com.hjg.pngj.ImageInfo; -import ar.com.hjg.pngj.PngjException; - -/** - * zTXt chunk. - *

- * see http://www.w3.org/TR/PNG/#11zTXt - */ -public class PngChunkZTXT extends PngChunkTextVar { - public final static String ID = ChunkHelper.zTXt; - - // http://www.w3.org/TR/PNG/#11zTXt - public PngChunkZTXT(final ImageInfo info) { - super(PngChunkZTXT.ID, info); - } - - @Override - public ChunkRaw createRawChunk() { - if (key == null || key.trim().length() == 0) - throw new PngjException("Text chunk key must be non empty"); - try { - final ByteArrayOutputStream ba = new ByteArrayOutputStream(); - ba.write(ChunkHelper.toBytes(key)); - ba.write(0); // separator - ba.write(0); // compression method: 0 - final byte[] textbytes = ChunkHelper.compressBytes(ChunkHelper.toBytes(val), true); - ba.write(textbytes); - final byte[] b = ba.toByteArray(); - final ChunkRaw chunk = createEmptyChunk(b.length, false); - chunk.data = b; - return chunk; - } catch (final IOException e) { - throw new PngjException(e); - } - } - - @Override - public void parseFromRaw(final ChunkRaw c) { - int nullsep = -1; - for (int i = 0; i < c.data.length; i++) { // look for first zero - if (c.data[i] != 0) - continue; - nullsep = i; - break; - } - if (nullsep < 0 || nullsep > c.data.length - 2) - throw new PngjException("bad zTXt chunk: no separator found"); - key = ChunkHelper.toString(c.data, 0, nullsep); - final int compmet = c.data[nullsep + 1]; - if (compmet != 0) - throw new PngjException("bad zTXt chunk: unknown compression method"); - final byte[] uncomp = ChunkHelper.compressBytes(c.data, nullsep + 2, c.data.length - nullsep - 2, false); // uncompress - val = ChunkHelper.toString(uncomp); - } - -} diff --git a/teavm/src/main/java/ar/com/hjg/pngj/chunks/PngMetadata.java b/teavm/src/main/java/ar/com/hjg/pngj/chunks/PngMetadata.java deleted file mode 100644 index 46c30c92..00000000 --- a/teavm/src/main/java/ar/com/hjg/pngj/chunks/PngMetadata.java +++ /dev/null @@ -1,235 +0,0 @@ -package ar.com.hjg.pngj.chunks; - -import java.util.ArrayList; -import java.util.List; - -import ar.com.hjg.pngj.PngjException; - -/** - * We consider "image metadata" every info inside the image except for the most - * basic image info (IHDR chunk - ImageInfo - * class) and the pixels values. - *

- * This includes the palette (if present) and all the ancillary chunks - *

- * This class provides a wrapper over the collection of chunks of a image (read - * or to write) and provides some high - * level methods to access them - */ -public class PngMetadata { - private final ChunksList chunkList; - private final boolean readonly; - - public PngMetadata(final ChunksList chunks) { - chunkList = chunks; - if (chunks instanceof ChunksListForWrite) - readonly = false; - else - readonly = true; - } - - /** - * Queues the chunk at the writer - *

- * lazyOverwrite: if true, checks if there is a queued "equivalent" chunk - * and if so, overwrites it. However if that - * not check for already written chunks. - */ - public void queueChunk(final PngChunk c, final boolean lazyOverwrite) { - final ChunksListForWrite cl = getChunkListW(); - if (readonly) - throw new PngjException("cannot set chunk : readonly metadata"); - if (lazyOverwrite) - ChunkHelper.trimList(cl.getQueuedChunks(), c2 -> ChunkHelper.equivalent(c, c2)); - cl.queue(c); - } - - public void queueChunk(final PngChunk c) { - queueChunk(c, true); - } - - private ChunksListForWrite getChunkListW() { - return (ChunksListForWrite) chunkList; - } - - // ///// high level utility methods follow //////////// - - // //////////// DPI - - /** - * returns -1 if not found or dimension unknown - */ - public double[] getDpi() { - final PngChunk c = chunkList.getById1(ChunkHelper.pHYs, true); - if (c == null) - return new double[] { -1, -1 }; - else - return ((PngChunkPHYS) c).getAsDpi2(); - } - - public void setDpi(final double x) { - setDpi(x, x); - } - - public void setDpi(final double x, final double y) { - final PngChunkPHYS c = new PngChunkPHYS(chunkList.imageInfo); - c.setAsDpi2(x, y); - queueChunk(c); - } - - // //////////// TIME - - /** - * Creates a time chunk with current time, less secsAgo seconds - *

- * - * @return Returns the created-queued chunk, just in case you want to - * examine or modify it - */ - public PngChunkTIME setTimeNow(final int secsAgo) { - final PngChunkTIME c = new PngChunkTIME(chunkList.imageInfo); - c.setNow(secsAgo); - queueChunk(c); - return c; - } - - public PngChunkTIME setTimeNow() { - return setTimeNow(0); - } - - /** - * Creates a time chunk with diven date-time - *

- * - * @return Returns the created-queued chunk, just in case you want to - * examine or modify it - */ - public PngChunkTIME setTimeYMDHMS(final int yearx, final int monx, final int dayx, final int hourx, final int minx, - final int secx) { - final PngChunkTIME c = new PngChunkTIME(chunkList.imageInfo); - c.setYMDHMS(yearx, monx, dayx, hourx, minx, secx); - queueChunk(c, true); - return c; - } - - /** - * null if not found - */ - public PngChunkTIME getTime() { - return (PngChunkTIME) chunkList.getById1(ChunkHelper.tIME); - } - - public String getTimeAsString() { - final PngChunkTIME c = getTime(); - return c == null ? "" : c.getAsString(); - } - - // //////////// TEXT - - /** - * Creates a text chunk and queue it. - *

- * - * @param k - * : key (latin1) - * @param val - * (arbitrary, should be latin1 if useLatin1) - * @param useLatin1 - * @param compress - * @return Returns the created-queued chunks, just in case you want to - * examine, touch it - */ - public PngChunkTextVar setText(final String k, final String val, final boolean useLatin1, final boolean compress) { - if (compress && !useLatin1) - throw new PngjException("cannot compress non latin text"); - PngChunkTextVar c; - if (useLatin1) { - if (compress) - c = new PngChunkZTXT(chunkList.imageInfo); - else - c = new PngChunkTEXT(chunkList.imageInfo); - } else { - c = new PngChunkITXT(chunkList.imageInfo); - ((PngChunkITXT) c).setLangtag(k); // we use the same orig tag (this is not quite right) - } - c.setKeyVal(k, val); - queueChunk(c, true); - return c; - } - - public PngChunkTextVar setText(final String k, final String val) { - return setText(k, val, false, false); - } - - /** - * gets all text chunks with a given key - *

- * returns null if not found - *

- * Warning: this does not check the "lang" key of iTxt - */ - @SuppressWarnings("unchecked") - public List getTxtsForKey(final String k) { - @SuppressWarnings("rawtypes") - final List c = new ArrayList(); - c.addAll(chunkList.getById(ChunkHelper.tEXt, k)); - c.addAll(chunkList.getById(ChunkHelper.zTXt, k)); - c.addAll(chunkList.getById(ChunkHelper.iTXt, k)); - return c; - } - - /** - * Returns empty if not found, concatenated (with newlines) if multiple! - - * and trimmed - *

- * Use getTxtsForKey() if you don't want this behaviour - */ - public String getTxtForKey(final String k) { - final List li = getTxtsForKey(k); - if (li.isEmpty()) - return ""; - final StringBuilder t = new StringBuilder(); - for (final PngChunkTextVar c : li) - t.append(c.getVal()).append("\n"); - return t.toString().trim(); - } - - /** - * Returns the palette chunk, if present - * - * @return null if not present - */ - public PngChunkPLTE getPLTE() { - return (PngChunkPLTE) chunkList.getById1(PngChunkPLTE.ID); - } - - /** - * Creates a new empty palette chunk, queues it for write and return it to - * the caller, who should fill its entries - */ - public PngChunkPLTE createPLTEChunk() { - final PngChunkPLTE plte = new PngChunkPLTE(chunkList.imageInfo); - queueChunk(plte); - return plte; - } - - /** - * Returns the TRNS chunk, if present - * - * @return null if not present - */ - public PngChunkTRNS getTRNS() { - return (PngChunkTRNS) chunkList.getById1(PngChunkTRNS.ID); - } - - /** - * Creates a new empty TRNS chunk, queues it for write and return it to the - * caller, who should fill its entries - */ - public PngChunkTRNS createTRNSChunk() { - final PngChunkTRNS trns = new PngChunkTRNS(chunkList.imageInfo); - queueChunk(trns); - return trns; - } - -} diff --git a/teavm/src/main/java/ar/com/hjg/pngj/chunks/package.html b/teavm/src/main/java/ar/com/hjg/pngj/chunks/package.html deleted file mode 100644 index 13740669..00000000 --- a/teavm/src/main/java/ar/com/hjg/pngj/chunks/package.html +++ /dev/null @@ -1,9 +0,0 @@ - - -

-Contains the code related to chunk management for the PNGJ library.

-

-Only needed by client code if some special chunk handling is required. -

- - diff --git a/teavm/src/main/java/ar/com/hjg/pngj/package.html b/teavm/src/main/java/ar/com/hjg/pngj/package.html deleted file mode 100644 index a417d56b..00000000 --- a/teavm/src/main/java/ar/com/hjg/pngj/package.html +++ /dev/null @@ -1,49 +0,0 @@ - - -

-PNGJ main package -

-

-Users of this library should rarely need more than the public members of this package.
-Newcomers: start with PngReader and PngWriter. -

-

-Example of use: this code reads a true colour PNG image (RGB8 or RGBA8) -and reduces the red channel by half, increasing the green by 20. -It copies all the "safe" metadata from the original image, and adds a textual metadata. - -

-  public static void convert(String origFilename, String destFilename) {
-    // you can also use PngReader (esentially the same) or PngReaderByte 
-    PngReaderInt pngr = new PngReaderInt(new File(origFilename));  
-    System.out.println(pngr.toString());
-    int channels = pngr.imgInfo.channels;
-    if (channels < 3 || pngr.imgInfo.bitDepth != 8)
-       throw new RuntimeException("For simplicity this supports only RGB8/RGBA8 images");
-    // writer with same image properties as original
-    PngWriter pngw = new PngWriter(new File(destFilename), pngr.imgInfo, true);
-    // instruct the writer to grab all ancillary chunks from the original
-    pngw.copyChunksFrom(pngr.getChunksList(), ChunkCopyBehaviour.COPY_ALL_SAFE);
-    // add a textual chunk to writer
-    pngw.getMetadata().setText(PngChunkTextVar.KEY_Description, "Decreased red and increased green");
-    // also: while(pngr.hasMoreRows())
-    for (int row = 0; row < pngr.imgInfo.rows; row++) {  
-       ImageLineInt l1 = pngr.readRowInt(); // each element is a sample
-       int[] scanline = l1.getScanline(); // to save typing
-       for (int j = 0; j < pngr.imgInfo.cols; j++) {
-          scanline[j * channels] /= 2;
-          scanline[j * channels + 1] = ImageLineHelper.clampTo_0_255(scanline[j * channels + 1] + 20);
-       }
-       pngw.writeRow(l1);
-    }
-    pngr.end(); // it's recommended to end the reader first, in case there are trailing chunks to read
-    pngw.end();
- }
-
-
- -For more examples, see the tests and samples. - -

- - diff --git a/teavm/src/main/java/ar/com/hjg/pngj/pixels/CompressorStream.java b/teavm/src/main/java/ar/com/hjg/pngj/pixels/CompressorStream.java deleted file mode 100644 index 7c7425af..00000000 --- a/teavm/src/main/java/ar/com/hjg/pngj/pixels/CompressorStream.java +++ /dev/null @@ -1,168 +0,0 @@ -package ar.com.hjg.pngj.pixels; - -import java.io.OutputStream; - -import ar.com.hjg.pngj.IDatChunkWriter; - -/** - * This is an OutputStream that compresses (via Deflater or a deflater-like - * object), and optionally passes the - * compressed stream to another output stream. - * - * It allows to compute in/out/ratio stats. - * - * It works as a stream (similar to DeflaterOutputStream), but it's peculiar in - * that it expects that each writes has a - * fixed length (other lenghts are accepted, but it's less efficient) and that - * the total amount of bytes is known (so it - * can close itself, but it can also be closed on demand) In PNGJ use, the block - * is typically a row (including filter - * byte). - * - * We use this to do the real compression (with Deflate) but also to compute - * tentative estimators - * - * If not closed, it can be recicled via reset() - * - * - */ -public abstract class CompressorStream extends OutputStream { - - protected IDatChunkWriter idatChunkWriter; - public final int blockLen; - public final long totalbytes; - - boolean closed = false; - protected boolean done = false; - protected long bytesIn = 0; - protected long bytesOut = 0; - protected int block = -1; - - /** optionally stores the first byte of each block (row) */ - private byte[] firstBytes; - protected boolean storeFirstByte = false; - - /** - * - * @param idatCw - * Can be null (if we are only interested in compute compression - * ratio) - * @param blockLen - * Estimated maximum block length. If unknown, use -1. - * @param totalbytes - * Expected total bytes to be fed. If unknown, use -1. - */ - public CompressorStream(final IDatChunkWriter idatCw, int blockLen, long totalbytes) { - idatChunkWriter = idatCw; - if (blockLen < 0) - blockLen = 4096; - if (totalbytes < 0) - totalbytes = Long.MAX_VALUE; - if (blockLen < 1 || totalbytes < 1) - throw new RuntimeException(" maxBlockLen or totalLen invalid"); - this.blockLen = blockLen; - this.totalbytes = totalbytes; - } - - /** Releases resources. Idempotent. */ - @Override - public void close() { - done(); - if (idatChunkWriter != null) - idatChunkWriter.close(); - closed = true; - } - - /** - * Will be called automatically when the number of bytes reaches the total - * expected Can be also be called from - * outside. This should set the flag done=true - */ - public abstract void done(); - - @Override - public final void write(final byte[] data) { - write(data, 0, data.length); - } - - @Override - public final void write(final byte[] data, int off, int len) { - block++; - if (len <= blockLen) { // normal case - mywrite(data, off, len); - if (storeFirstByte && block < firstBytes.length) - firstBytes[block] = data[off]; // only makes sense in this case - } else - while (len > 0) { - mywrite(data, off, blockLen); - off += blockLen; - len -= blockLen; - } - if (bytesIn >= totalbytes) - done(); - - } - - /** - * same as write, but guarantedd to not exceed blockLen The implementation - * should update bytesOut and bytesInt but not - * check for totalBytes - */ - public abstract void mywrite(byte[] data, int off, int len); - - /** - * compressed/raw. This should be called only when done - */ - public final double getCompressionRatio() { - return bytesOut == 0 ? 1.0 : bytesOut / (double) bytesIn; - } - - /** - * raw (input) bytes. This should be called only when done - */ - public final long getBytesRaw() { - return bytesIn; - } - - /** - * compressed (out) bytes. This should be called only when done - */ - public final long getBytesCompressed() { - return bytesOut; - } - - public boolean isClosed() { - return closed; - } - - public boolean isDone() { - return done; - } - - public byte[] getFirstBytes() { - return firstBytes; - } - - public void setStoreFirstByte(final boolean storeFirstByte, final int nblocks) { - this.storeFirstByte = storeFirstByte; - if (this.storeFirstByte) { - if (firstBytes == null || firstBytes.length < nblocks) - firstBytes = new byte[nblocks]; - } else - firstBytes = null; - } - - public void reset() { - done(); - bytesIn = 0; - bytesOut = 0; - block = -1; - done = false; - } - - @Override - public void write(final int i) { // should not be used - write(new byte[] { (byte) i }); - } - -} diff --git a/teavm/src/main/java/ar/com/hjg/pngj/pixels/CompressorStreamDeflater.java b/teavm/src/main/java/ar/com/hjg/pngj/pixels/CompressorStreamDeflater.java deleted file mode 100644 index 291bf4d6..00000000 --- a/teavm/src/main/java/ar/com/hjg/pngj/pixels/CompressorStreamDeflater.java +++ /dev/null @@ -1,105 +0,0 @@ -package ar.com.hjg.pngj.pixels; - -import java.util.zip.Deflater; - -import ar.com.hjg.pngj.IDatChunkWriter; -import ar.com.hjg.pngj.PngjOutputException; - -/** - * CompressorStream backed by a Deflater. - * - * Note that the Deflater is not disposed after done, you should either recycle - * this with reset() or dispose it with - * close() - * - */ -public class CompressorStreamDeflater extends CompressorStream { - - protected Deflater deflater; - protected byte[] buf1; // temporary storage of compressed bytes: only used if idatWriter is null - protected boolean deflaterIsOwn = true; - - /** - * if a deflater is passed, it must be already reset. It will not be - * released on close - */ - public CompressorStreamDeflater(final IDatChunkWriter idatCw, final int maxBlockLen, final long totalLen, final Deflater def) { - super(idatCw, maxBlockLen, totalLen); - deflater = def == null ? new Deflater() : def; - deflaterIsOwn = def == null; - } - - public CompressorStreamDeflater(final IDatChunkWriter idatCw, final int maxBlockLen, final long totalLen) { - this(idatCw, maxBlockLen, totalLen, null); - } - - public CompressorStreamDeflater(final IDatChunkWriter idatCw, final int maxBlockLen, final long totalLen, final int deflaterCompLevel, final int deflaterStrategy) { - this(idatCw, maxBlockLen, totalLen, new Deflater(deflaterCompLevel)); - deflaterIsOwn = true; - deflater.setStrategy(deflaterStrategy); - } - - @Override - public void mywrite(final byte[] data, final int off, final int len) { - if (deflater.finished() || done || closed) - throw new PngjOutputException("write beyond end of stream"); - deflater.setInput(data, off, len); - bytesIn += len; - while (!deflater.needsInput()) - deflate(); - } - - protected void deflate() { - byte[] buf; - int off, n; - if (idatChunkWriter != null) { - buf = idatChunkWriter.getBuf(); - off = idatChunkWriter.getOffset(); - n = idatChunkWriter.getAvailLen(); - } else { - if (buf1 == null) - buf1 = new byte[4096]; - buf = buf1; - off = 0; - n = buf1.length; - } - final int len = deflater.deflate(buf, off, n); - if (len > 0) { - if (idatChunkWriter != null) - idatChunkWriter.incrementOffset(len); - bytesOut += len; - } - } - - /** automatically called when done */ - @Override - public void done() { - if (done) - return; - if (!deflater.finished()) { - deflater.finish(); - while (!deflater.finished()) - deflate(); - } - done = true; - if (idatChunkWriter != null) - idatChunkWriter.close(); - } - - @Override - public void close() { - done(); - try { - if (deflaterIsOwn) - deflater.end(); - } catch (final Exception e) {} - super.close(); - } - - @Override - public void reset() { - deflater.reset(); - super.reset(); - } - -} diff --git a/teavm/src/main/java/ar/com/hjg/pngj/pixels/CompressorStreamLz4.java b/teavm/src/main/java/ar/com/hjg/pngj/pixels/CompressorStreamLz4.java deleted file mode 100644 index 318918f2..00000000 --- a/teavm/src/main/java/ar/com/hjg/pngj/pixels/CompressorStreamLz4.java +++ /dev/null @@ -1,94 +0,0 @@ -package ar.com.hjg.pngj.pixels; - -import java.util.zip.Deflater; - -import ar.com.hjg.pngj.IDatChunkWriter; -import ar.com.hjg.pngj.PngjOutputException; - -/** - * This class uses a quick compressor to get a rough estimate of deflate - * compression ratio. - * - * This just ignores the outputStream, and the deflater related parameters - */ -public class CompressorStreamLz4 extends CompressorStream { - - private final DeflaterEstimatorLz4 lz4; - - private byte[] buf; // lazily allocated, only if needed - private final int buffer_size; - // bufpos=bytes in buffer yet not compressed (bytesIn include this) - private int inbuf = 0; - - private static final int MAX_BUFFER_SIZE = 16000; - - public CompressorStreamLz4(final IDatChunkWriter os, final int maxBlockLen, final long totalLen) { - super(os, maxBlockLen, totalLen); - lz4 = new DeflaterEstimatorLz4(); - buffer_size = (int) (totalLen > CompressorStreamLz4.MAX_BUFFER_SIZE ? CompressorStreamLz4.MAX_BUFFER_SIZE : totalLen); - } - - public CompressorStreamLz4(final IDatChunkWriter os, final int maxBlockLen, final long totalLen, final Deflater def) { - this(os, maxBlockLen, totalLen);// edlfater ignored - } - - public CompressorStreamLz4(final IDatChunkWriter os, final int maxBlockLen, final long totalLen, final int deflaterCompLevel, final int deflaterStrategy) { - this(os, maxBlockLen, totalLen); // paramters ignored - } - - @Override - public void mywrite(final byte[] b, int off, int len) { - if (len == 0) - return; - if (done || closed) - throw new PngjOutputException("write beyond end of stream"); - bytesIn += len; - while (len > 0) - if (inbuf == 0 && (len >= CompressorStreamLz4.MAX_BUFFER_SIZE || bytesIn == totalbytes)) { - // direct copy (buffer might be null or empty) - bytesOut += lz4.compressEstim(b, off, len); - len = 0; - } else { - if (buf == null) - buf = new byte[buffer_size]; - final int len1 = inbuf + len <= buffer_size ? len : buffer_size - inbuf; // to copy - if (len1 > 0) - System.arraycopy(b, off, buf, inbuf, len1); - inbuf += len1; - len -= len1; - off += len1; - if (inbuf == buffer_size) - compressFromBuffer(); - } - } - - void compressFromBuffer() { - if (inbuf > 0) { - bytesOut += lz4.compressEstim(buf, 0, inbuf); - inbuf = 0; - } - } - - @Override - public void done() { - if (!done) { - compressFromBuffer(); - done = true; - } - } - - @Override - public void close() { - done(); - if (!closed) { - super.close(); - buf = null; - } - } - - @Override - public void reset() { - super.reset(); - } - -} diff --git a/teavm/src/main/java/ar/com/hjg/pngj/pixels/DeflaterEstimatorHjg.java b/teavm/src/main/java/ar/com/hjg/pngj/pixels/DeflaterEstimatorHjg.java deleted file mode 100644 index 4f8fab67..00000000 --- a/teavm/src/main/java/ar/com/hjg/pngj/pixels/DeflaterEstimatorHjg.java +++ /dev/null @@ -1,248 +0,0 @@ -package ar.com.hjg.pngj.pixels; - -final public class DeflaterEstimatorHjg { - - /** - * This object is stateless, it's thread safe and can be reused - */ - public DeflaterEstimatorHjg() {} - - /** - * Estimates the length of the compressed bytes, as compressed by Lz4 - * WARNING: if larger than LZ4_64K_LIMIT it cuts it - * in fragments - * - * WARNING: if some part of the input is discarded, this should return the - * proportional (so that - * returnValue/srcLen=compressionRatio) - * - * @param src - * @param srcOff - * @param srcLen - * @return length of the compressed bytes - */ - public int compressEstim(final byte[] src, int srcOff, final int srcLen) { - if (srcLen < 10) - return srcLen; // too small - int stride = DeflaterEstimatorHjg.LZ4_64K_LIMIT - 1; - final int segments = (srcLen + stride - 1) / stride; - stride = srcLen / segments; - if (stride >= DeflaterEstimatorHjg.LZ4_64K_LIMIT - 1 || stride * segments > srcLen || segments < 1 || stride < 1) - throw new RuntimeException("?? " + srcLen); - int bytesIn = 0; - int bytesOut = 0; - int len = srcLen; - while (len > 0) { - if (len > stride) - len = stride; - bytesOut += DeflaterEstimatorHjg.compress64k(src, srcOff, len); - srcOff += len; - bytesIn += len; - len = srcLen - bytesIn; - } - final double ratio = bytesOut / (double) bytesIn; - return bytesIn == srcLen ? bytesOut : (int) (ratio * srcLen + 0.5); - } - - public int compressEstim(final byte[] src) { - return compressEstim(src, 0, src.length); - } - - static final int MEMORY_USAGE = 14; - static final int NOT_COMPRESSIBLE_DETECTION_LEVEL = 6; // see SKIP_STRENGTH - - static final int MIN_MATCH = 4; - - static final int HASH_LOG = DeflaterEstimatorHjg.MEMORY_USAGE - 2; - static final int HASH_TABLE_SIZE = 1 << DeflaterEstimatorHjg.HASH_LOG; - - static final int SKIP_STRENGTH = Math.max(DeflaterEstimatorHjg.NOT_COMPRESSIBLE_DETECTION_LEVEL, 2); // 6 findMatchAttempts = - // 2^SKIP_STRENGTH+3 - static final int COPY_LENGTH = 8; - static final int LAST_LITERALS = 5; - static final int MF_LIMIT = DeflaterEstimatorHjg.COPY_LENGTH + DeflaterEstimatorHjg.MIN_MATCH; - static final int MIN_LENGTH = DeflaterEstimatorHjg.MF_LIMIT + 1; - - static final int MAX_DISTANCE = 1 << 16; - - static final int ML_BITS = 4; - static final int ML_MASK = (1 << DeflaterEstimatorHjg.ML_BITS) - 1; - static final int RUN_BITS = 8 - DeflaterEstimatorHjg.ML_BITS; - static final int RUN_MASK = (1 << DeflaterEstimatorHjg.RUN_BITS) - 1; - - static final int LZ4_64K_LIMIT = (1 << 16) + DeflaterEstimatorHjg.MF_LIMIT - 1; - static final int HASH_LOG_64K = DeflaterEstimatorHjg.HASH_LOG + 1; - static final int HASH_TABLE_SIZE_64K = 1 << DeflaterEstimatorHjg.HASH_LOG_64K; - - static final int HASH_LOG_HC = 15; - static final int HASH_TABLE_SIZE_HC = 1 << DeflaterEstimatorHjg.HASH_LOG_HC; - static final int OPTIMAL_ML = DeflaterEstimatorHjg.ML_MASK - 1 + DeflaterEstimatorHjg.MIN_MATCH; - - static int compress64k(final byte[] src, final int srcOff, final int srcLen) { - final int srcEnd = srcOff + srcLen; - final int srcLimit = srcEnd - DeflaterEstimatorHjg.LAST_LITERALS; - final int mflimit = srcEnd - DeflaterEstimatorHjg.MF_LIMIT; - - int sOff = srcOff, dOff = 0; - - int anchor = sOff; - - if (srcLen >= DeflaterEstimatorHjg.MIN_LENGTH) { - - final short[] hashTable = new short[DeflaterEstimatorHjg.HASH_TABLE_SIZE_64K]; - - ++sOff; - - main: while (true) { - - // find a match - int forwardOff = sOff; - - int ref; - int findMatchAttempts1 = (1 << DeflaterEstimatorHjg.SKIP_STRENGTH) + 3; // 64+3=67 - do { - sOff = forwardOff; - forwardOff += findMatchAttempts1++ >>> DeflaterEstimatorHjg.SKIP_STRENGTH; - - if (forwardOff > mflimit) - break main; // ends all - - final int h = DeflaterEstimatorHjg.hash64k(DeflaterEstimatorHjg.readInt(src, sOff)); - ref = srcOff + DeflaterEstimatorHjg.readShort(hashTable, h); - DeflaterEstimatorHjg.writeShort(hashTable, h, sOff - srcOff); - } while (!DeflaterEstimatorHjg.readIntEquals(src, ref, sOff)); - - // catch up - final int excess = DeflaterEstimatorHjg.commonBytesBackward(src, ref, sOff, srcOff, anchor); - sOff -= excess; - ref -= excess; - // sequence == refsequence - final int runLen = sOff - anchor; - dOff++; - - if (runLen >= DeflaterEstimatorHjg.RUN_MASK) { - if (runLen > DeflaterEstimatorHjg.RUN_MASK) - dOff += (runLen - DeflaterEstimatorHjg.RUN_MASK) / 0xFF; - dOff++; - } - dOff += runLen; - while (true) { - // encode offset - dOff += 2; - // count nb matches - sOff += DeflaterEstimatorHjg.MIN_MATCH; - ref += DeflaterEstimatorHjg.MIN_MATCH; - final int matchLen = DeflaterEstimatorHjg.commonBytes(src, ref, sOff, srcLimit); - sOff += matchLen; - // encode match len - if (matchLen >= DeflaterEstimatorHjg.ML_MASK) { - if (matchLen >= DeflaterEstimatorHjg.ML_MASK + 0xFF) - dOff += (matchLen - DeflaterEstimatorHjg.ML_MASK) / 0xFF; - dOff++; - } - // test end of chunk - if (sOff > mflimit) { - anchor = sOff; - break main; - } - // fill table - DeflaterEstimatorHjg.writeShort(hashTable, DeflaterEstimatorHjg.hash64k(DeflaterEstimatorHjg.readInt(src, sOff - 2)), sOff - 2 - srcOff); - // test next position - final int h = DeflaterEstimatorHjg.hash64k(DeflaterEstimatorHjg.readInt(src, sOff)); - ref = srcOff + DeflaterEstimatorHjg.readShort(hashTable, h); - DeflaterEstimatorHjg.writeShort(hashTable, h, sOff - srcOff); - if (!DeflaterEstimatorHjg.readIntEquals(src, sOff, ref)) - break; - dOff++; - } - // prepare next loop - anchor = sOff++; - } - } - final int runLen = srcEnd - anchor; - if (runLen >= DeflaterEstimatorHjg.RUN_MASK + 0xFF) - dOff += (runLen - DeflaterEstimatorHjg.RUN_MASK) / 0xFF; - dOff++; - dOff += runLen; - return dOff; - } - - static int maxCompressedLength(final int length) { - if (length < 0) - throw new IllegalArgumentException("length must be >= 0, got " + length); - return length + length / 255 + 16; - } - - static int hash(final int i) { - return i * -1640531535 >>> DeflaterEstimatorHjg.MIN_MATCH * 8 - DeflaterEstimatorHjg.HASH_LOG; - } - - static int hash64k(final int i) { - return i * -1640531535 >>> DeflaterEstimatorHjg.MIN_MATCH * 8 - DeflaterEstimatorHjg.HASH_LOG_64K; - } - - static int readShortLittleEndian(final byte[] buf, final int i) { - return buf[i] & 0xFF | (buf[i + 1] & 0xFF) << 8; - } - - static boolean readIntEquals(final byte[] buf, final int i, final int j) { - return buf[i] == buf[j] && buf[i + 1] == buf[j + 1] && buf[i + 2] == buf[j + 2] && buf[i + 3] == buf[j + 3]; - } - - static int commonBytes(final byte[] b, int o1, int o2, final int limit) { - int count = 0; - while (o2 < limit && b[o1++] == b[o2++]) - ++count; - return count; - } - - static int commonBytesBackward(final byte[] b, int o1, int o2, final int l1, final int l2) { - int count = 0; - while (o1 > l1 && o2 > l2 && b[--o1] == b[--o2]) - ++count; - return count; - } - - static int readShort(final short[] buf, final int off) { - return buf[off] & 0xFFFF; - } - - static byte readByte(final byte[] buf, final int i) { - return buf[i]; - } - - static void checkRange(final byte[] buf, final int off) { - if (off < 0 || off >= buf.length) - throw new ArrayIndexOutOfBoundsException(off); - } - - static void checkRange(final byte[] buf, final int off, final int len) { - DeflaterEstimatorHjg.checkLength(len); - if (len > 0) { - DeflaterEstimatorHjg.checkRange(buf, off); - DeflaterEstimatorHjg.checkRange(buf, off + len - 1); - } - } - - static void checkLength(final int len) { - if (len < 0) - throw new IllegalArgumentException("lengths must be >= 0"); - } - - static int readIntBE(final byte[] buf, final int i) { - return (buf[i] & 0xFF) << 24 | (buf[i + 1] & 0xFF) << 16 | (buf[i + 2] & 0xFF) << 8 | buf[i + 3] & 0xFF; - } - - static int readIntLE(final byte[] buf, final int i) { - return buf[i] & 0xFF | (buf[i + 1] & 0xFF) << 8 | (buf[i + 2] & 0xFF) << 16 | (buf[i + 3] & 0xFF) << 24; - } - - static int readInt(final byte[] buf, final int i) { - return DeflaterEstimatorHjg.readIntBE(buf, i); - } - - static void writeShort(final short[] buf, final int off, final int v) { - buf[off] = (short) v; - } - -} diff --git a/teavm/src/main/java/ar/com/hjg/pngj/pixels/DeflaterEstimatorLz4.java b/teavm/src/main/java/ar/com/hjg/pngj/pixels/DeflaterEstimatorLz4.java deleted file mode 100644 index 74d544c6..00000000 --- a/teavm/src/main/java/ar/com/hjg/pngj/pixels/DeflaterEstimatorLz4.java +++ /dev/null @@ -1,265 +0,0 @@ -package ar.com.hjg.pngj.pixels; - -import java.nio.ByteOrder; - -/** - * This estimator actually uses the LZ4 compression algorithm, and hopes that - * it's well correlated with Deflater. It's - * about 3 to 4 times faster than Deflater. - * - * This is a modified heavily trimmed version of the - * net.jpountz.lz4.LZ4JavaSafeCompressor class plus some methods from - * other classes from LZ4 Java library: https://github.com/jpountz/lz4-java , - * originally licensed under the Apache - * License 2.0 - */ -final public class DeflaterEstimatorLz4 { - - /** - * This object is stateless, it's thread safe and can be reused - */ - public DeflaterEstimatorLz4() {} - - /** - * Estimates the length of the compressed bytes, as compressed by Lz4 - * WARNING: if larger than LZ4_64K_LIMIT it cuts it - * in fragments - * - * WARNING: if some part of the input is discarded, this should return the - * proportional (so that - * returnValue/srcLen=compressionRatio) - * - * @param src - * @param srcOff - * @param srcLen - * @return length of the compressed bytes - */ - public int compressEstim(final byte[] src, int srcOff, final int srcLen) { - if (srcLen < 10) - return srcLen; // too small - int stride = DeflaterEstimatorLz4.LZ4_64K_LIMIT - 1; - final int segments = (srcLen + stride - 1) / stride; - stride = srcLen / segments; - if (stride >= DeflaterEstimatorLz4.LZ4_64K_LIMIT - 1 || stride * segments > srcLen || segments < 1 || stride < 1) - throw new RuntimeException("?? " + srcLen); - int bytesIn = 0; - int bytesOut = 0; - int len = srcLen; - while (len > 0) { - if (len > stride) - len = stride; - bytesOut += DeflaterEstimatorLz4.compress64k(src, srcOff, len); - srcOff += len; - bytesIn += len; - len = srcLen - bytesIn; - } - final double ratio = bytesOut / (double) bytesIn; - return bytesIn == srcLen ? bytesOut : (int) (ratio * srcLen + 0.5); - } - - public int compressEstim(final byte[] src) { - return compressEstim(src, 0, src.length); - } - - static final ByteOrder NATIVE_BYTE_ORDER = ByteOrder.nativeOrder(); - - static final int MEMORY_USAGE = 14; - static final int NOT_COMPRESSIBLE_DETECTION_LEVEL = 6; - - static final int MIN_MATCH = 4; - - static final int HASH_LOG = DeflaterEstimatorLz4.MEMORY_USAGE - 2; - static final int HASH_TABLE_SIZE = 1 << DeflaterEstimatorLz4.HASH_LOG; - - static final int SKIP_STRENGTH = Math.max(DeflaterEstimatorLz4.NOT_COMPRESSIBLE_DETECTION_LEVEL, 2); - static final int COPY_LENGTH = 8; - static final int LAST_LITERALS = 5; - static final int MF_LIMIT = DeflaterEstimatorLz4.COPY_LENGTH + DeflaterEstimatorLz4.MIN_MATCH; - static final int MIN_LENGTH = DeflaterEstimatorLz4.MF_LIMIT + 1; - - static final int MAX_DISTANCE = 1 << 16; - - static final int ML_BITS = 4; - static final int ML_MASK = (1 << DeflaterEstimatorLz4.ML_BITS) - 1; - static final int RUN_BITS = 8 - DeflaterEstimatorLz4.ML_BITS; - static final int RUN_MASK = (1 << DeflaterEstimatorLz4.RUN_BITS) - 1; - - static final int LZ4_64K_LIMIT = (1 << 16) + DeflaterEstimatorLz4.MF_LIMIT - 1; - static final int HASH_LOG_64K = DeflaterEstimatorLz4.HASH_LOG + 1; - static final int HASH_TABLE_SIZE_64K = 1 << DeflaterEstimatorLz4.HASH_LOG_64K; - - static final int HASH_LOG_HC = 15; - static final int HASH_TABLE_SIZE_HC = 1 << DeflaterEstimatorLz4.HASH_LOG_HC; - static final int OPTIMAL_ML = DeflaterEstimatorLz4.ML_MASK - 1 + DeflaterEstimatorLz4.MIN_MATCH; - - static int compress64k(final byte[] src, final int srcOff, final int srcLen) { - final int srcEnd = srcOff + srcLen; - final int srcLimit = srcEnd - DeflaterEstimatorLz4.LAST_LITERALS; - final int mflimit = srcEnd - DeflaterEstimatorLz4.MF_LIMIT; - - int sOff = srcOff, dOff = 0; - - int anchor = sOff; - - if (srcLen >= DeflaterEstimatorLz4.MIN_LENGTH) { - - final short[] hashTable = new short[DeflaterEstimatorLz4.HASH_TABLE_SIZE_64K]; - - ++sOff; - - main: while (true) { - - // find a match - int forwardOff = sOff; - - int ref; - int findMatchAttempts = (1 << DeflaterEstimatorLz4.SKIP_STRENGTH) + 3; - do { - sOff = forwardOff; - forwardOff += findMatchAttempts++ >>> DeflaterEstimatorLz4.SKIP_STRENGTH; - - if (forwardOff > mflimit) - break main; - - final int h = DeflaterEstimatorLz4.hash64k(DeflaterEstimatorLz4.readInt(src, sOff)); - ref = srcOff + DeflaterEstimatorLz4.readShort(hashTable, h); - DeflaterEstimatorLz4.writeShort(hashTable, h, sOff - srcOff); - } while (!DeflaterEstimatorLz4.readIntEquals(src, ref, sOff)); - - // catch up - final int excess = DeflaterEstimatorLz4.commonBytesBackward(src, ref, sOff, srcOff, anchor); - sOff -= excess; - ref -= excess; - // sequence == refsequence - final int runLen = sOff - anchor; - dOff++; - - if (runLen >= DeflaterEstimatorLz4.RUN_MASK) { - if (runLen > DeflaterEstimatorLz4.RUN_MASK) - dOff += (runLen - DeflaterEstimatorLz4.RUN_MASK) / 0xFF; - dOff++; - } - dOff += runLen; - while (true) { - // encode offset - dOff += 2; - // count nb matches - sOff += DeflaterEstimatorLz4.MIN_MATCH; - ref += DeflaterEstimatorLz4.MIN_MATCH; - final int matchLen = DeflaterEstimatorLz4.commonBytes(src, ref, sOff, srcLimit); - sOff += matchLen; - // encode match len - if (matchLen >= DeflaterEstimatorLz4.ML_MASK) { - if (matchLen >= DeflaterEstimatorLz4.ML_MASK + 0xFF) - dOff += (matchLen - DeflaterEstimatorLz4.ML_MASK) / 0xFF; - dOff++; - } - // test end of chunk - if (sOff > mflimit) { - anchor = sOff; - break main; - } - // fill table - DeflaterEstimatorLz4.writeShort(hashTable, DeflaterEstimatorLz4.hash64k(DeflaterEstimatorLz4.readInt(src, sOff - 2)), sOff - 2 - srcOff); - // test next position - final int h = DeflaterEstimatorLz4.hash64k(DeflaterEstimatorLz4.readInt(src, sOff)); - ref = srcOff + DeflaterEstimatorLz4.readShort(hashTable, h); - DeflaterEstimatorLz4.writeShort(hashTable, h, sOff - srcOff); - if (!DeflaterEstimatorLz4.readIntEquals(src, sOff, ref)) - break; - dOff++; - } - // prepare next loop - anchor = sOff++; - } - } - final int runLen = srcEnd - anchor; - if (runLen >= DeflaterEstimatorLz4.RUN_MASK + 0xFF) - dOff += (runLen - DeflaterEstimatorLz4.RUN_MASK) / 0xFF; - dOff++; - dOff += runLen; - return dOff; - } - - static int maxCompressedLength(final int length) { - if (length < 0) - throw new IllegalArgumentException("length must be >= 0, got " + length); - return length + length / 255 + 16; - } - - static int hash(final int i) { - return i * -1640531535 >>> DeflaterEstimatorLz4.MIN_MATCH * 8 - DeflaterEstimatorLz4.HASH_LOG; - } - - static int hash64k(final int i) { - return i * -1640531535 >>> DeflaterEstimatorLz4.MIN_MATCH * 8 - DeflaterEstimatorLz4.HASH_LOG_64K; - } - - static int readShortLittleEndian(final byte[] buf, final int i) { - return buf[i] & 0xFF | (buf[i + 1] & 0xFF) << 8; - } - - static boolean readIntEquals(final byte[] buf, final int i, final int j) { - return buf[i] == buf[j] && buf[i + 1] == buf[j + 1] && buf[i + 2] == buf[j + 2] && buf[i + 3] == buf[j + 3]; - } - - static int commonBytes(final byte[] b, int o1, int o2, final int limit) { - int count = 0; - while (o2 < limit && b[o1++] == b[o2++]) - ++count; - return count; - } - - static int commonBytesBackward(final byte[] b, int o1, int o2, final int l1, final int l2) { - int count = 0; - while (o1 > l1 && o2 > l2 && b[--o1] == b[--o2]) - ++count; - return count; - } - - static int readShort(final short[] buf, final int off) { - return buf[off] & 0xFFFF; - } - - static byte readByte(final byte[] buf, final int i) { - return buf[i]; - } - - static void checkRange(final byte[] buf, final int off) { - if (off < 0 || off >= buf.length) - throw new ArrayIndexOutOfBoundsException(off); - } - - static void checkRange(final byte[] buf, final int off, final int len) { - DeflaterEstimatorLz4.checkLength(len); - if (len > 0) { - DeflaterEstimatorLz4.checkRange(buf, off); - DeflaterEstimatorLz4.checkRange(buf, off + len - 1); - } - } - - static void checkLength(final int len) { - if (len < 0) - throw new IllegalArgumentException("lengths must be >= 0"); - } - - static int readIntBE(final byte[] buf, final int i) { - return (buf[i] & 0xFF) << 24 | (buf[i + 1] & 0xFF) << 16 | (buf[i + 2] & 0xFF) << 8 | buf[i + 3] & 0xFF; - } - - static int readIntLE(final byte[] buf, final int i) { - return buf[i] & 0xFF | (buf[i + 1] & 0xFF) << 8 | (buf[i + 2] & 0xFF) << 16 | (buf[i + 3] & 0xFF) << 24; - } - - static int readInt(final byte[] buf, final int i) { - if (DeflaterEstimatorLz4.NATIVE_BYTE_ORDER == ByteOrder.BIG_ENDIAN) - return DeflaterEstimatorLz4.readIntBE(buf, i); - else - return DeflaterEstimatorLz4.readIntLE(buf, i); - } - - static void writeShort(final short[] buf, final int off, final int v) { - buf[off] = (short) v; - } - -} diff --git a/teavm/src/main/java/ar/com/hjg/pngj/pixels/FiltersPerformance.java b/teavm/src/main/java/ar/com/hjg/pngj/pixels/FiltersPerformance.java deleted file mode 100644 index bf35aa42..00000000 --- a/teavm/src/main/java/ar/com/hjg/pngj/pixels/FiltersPerformance.java +++ /dev/null @@ -1,208 +0,0 @@ -package ar.com.hjg.pngj.pixels; - -import java.util.Arrays; - -import ar.com.hjg.pngj.FilterType; -import ar.com.hjg.pngj.ImageInfo; -import ar.com.hjg.pngj.PngHelperInternal; -import ar.com.hjg.pngj.PngjExceptionInternal; - -/** for use in adaptative strategy */ -public class FiltersPerformance { - - private final ImageInfo iminfo; - private double memoryA = 0.7; // empirical (not very critical: 0.72) - private int lastrow = -1; - private final double[] absum = new double[5];// depending on the strategy not all values might be - // computed for all - private final double[] entropy = new double[5]; - private final double[] cost = new double[5]; - private final int[] histog = new int[256]; // temporary, not normalized - private int lastprefered = -1; - private boolean initdone = false; - private double preferenceForNone = 1.0; // higher gives more preference to NONE - - // this values are empirical (montecarlo), for RGB8 images with entropy estimator for NONE and - // memory=0.7 - // DONT MODIFY THIS - public static final double[] FILTER_WEIGHTS_DEFAULT = { 0.73, 1.03, 0.97, 1.11, 1.22 }; // lower is - // better! - - private final double[] filter_weights = new double[] { -1, -1, -1, -1, -1 }; - - private final static double LOG2NI = -1.0 / Math.log(2.0); - - public FiltersPerformance(final ImageInfo imgInfo) { - iminfo = imgInfo; - } - - private void init() { - if (filter_weights[0] < 0) {// has not been set from outside - System.arraycopy(FiltersPerformance.FILTER_WEIGHTS_DEFAULT, 0, filter_weights, 0, 5); - double wNone = filter_weights[0]; - if (iminfo.bitDepth == 16) - wNone = 1.2; - else if (iminfo.alpha) - wNone = 0.8; - else if (iminfo.indexed || iminfo.bitDepth < 8) - wNone = 0.4; // we prefer NONE strongly - wNone /= preferenceForNone; - filter_weights[0] = wNone; - } - Arrays.fill(cost, 1.0); - initdone = true; - } - - public void updateFromFiltered(final FilterType ftype, final byte[] rowff, final int rown) { - updateFromRawOrFiltered(ftype, rowff, null, null, rown); - } - - /** alternative: computes statistic without filtering */ - public void updateFromRaw(final FilterType ftype, final byte[] rowb, final byte[] rowbprev, final int rown) { - updateFromRawOrFiltered(ftype, null, rowb, rowbprev, rown); - } - - private void updateFromRawOrFiltered(final FilterType ftype, final byte[] rowff, final byte[] rowb, - final byte[] rowbprev, final int rown) { - if (!initdone) - init(); - if (rown != lastrow) { - Arrays.fill(absum, Double.NaN); - Arrays.fill(entropy, Double.NaN); - } - lastrow = rown; - if (rowff != null) - computeHistogram(rowff); - else - computeHistogramForFilter(ftype, rowb, rowbprev); - if (ftype == FilterType.FILTER_NONE) - entropy[ftype.val] = computeEntropyFromHistogram(); - else - absum[ftype.val] = computeAbsFromHistogram(); - } - - /* WARNING: this is not idempotent, call it just once per cycle (sigh) */ - public FilterType getPreferred() { - int fi = 0; - double vali = Double.MAX_VALUE, val = 0; // lower wins - for (int i = 0; i < 5; i++) { - if (!Double.isNaN(absum[i])) - val = absum[i]; - else if (!Double.isNaN(entropy[i])) - val = (Math.pow(2.0, entropy[i]) - 1.0) * 0.5; - else - continue; - val *= filter_weights[i]; - val = cost[i] * memoryA + (1 - memoryA) * val; - cost[i] = val; - if (val < vali) { - vali = val; - fi = i; - } - } - lastprefered = fi; - return FilterType.getByVal(lastprefered); - } - - public final void computeHistogramForFilter(final FilterType filterType, final byte[] rowb, final byte[] rowbprev) { - Arrays.fill(histog, 0); - int i, j; - final int imax = iminfo.bytesPerRow; - switch (filterType) { - case FILTER_NONE: - for (i = 1; i <= imax; i++) - histog[rowb[i] & 0xFF]++; - break; - case FILTER_PAETH: - for (i = 1; i <= imax; i++) - histog[PngHelperInternal.filterRowPaeth(rowb[i], 0, rowbprev[i] & 0xFF, 0)]++; - for (j = 1, i = iminfo.bytesPixel + 1; i <= imax; i++, j++) - histog[PngHelperInternal.filterRowPaeth(rowb[i], rowb[j] & 0xFF, rowbprev[i] & 0xFF, rowbprev[j] & 0xFF)]++; - break; - case FILTER_SUB: - for (i = 1; i <= iminfo.bytesPixel; i++) - histog[rowb[i] & 0xFF]++; - for (j = 1, i = iminfo.bytesPixel + 1; i <= imax; i++, j++) - histog[rowb[i] - rowb[j] & 0xFF]++; - break; - case FILTER_UP: - for (i = 1; i <= iminfo.bytesPerRow; i++) - histog[rowb[i] - rowbprev[i] & 0xFF]++; - break; - case FILTER_AVERAGE: - for (i = 1; i <= iminfo.bytesPixel; i++) - histog[(rowb[i] & 0xFF) - (rowbprev[i] & 0xFF) / 2 & 0xFF]++; - for (j = 1, i = iminfo.bytesPixel + 1; i <= imax; i++, j++) - histog[(rowb[i] & 0xFF) - ((rowbprev[i] & 0xFF) + (rowb[j] & 0xFF)) / 2 & 0xFF]++; - break; - default: - throw new PngjExceptionInternal("Bad filter:" + filterType); - } - } - - public void computeHistogram(final byte[] rowff) { - Arrays.fill(histog, 0); - for (int i = 1; i < iminfo.bytesPerRow; i++) - histog[rowff[i] & 0xFF]++; - } - - public double computeAbsFromHistogram() { - int s = 0; - for (int i = 1; i < 128; i++) - s += histog[i] * i; - for (int i = 128, j = 128; j > 0; i++, j--) - s += histog[i] * j; - return s / (double) iminfo.bytesPerRow; - } - - public final double computeEntropyFromHistogram() { - final double s = 1.0 / iminfo.bytesPerRow; - final double ls = Math.log(s); - - double h = 0; - for (final int x : histog) - if (x > 0) - h += (Math.log(x) + ls) * x; - h *= s * FiltersPerformance.LOG2NI; - if (h < 0.0) - h = 0.0; - return h; - } - - /** - * If larger than 1.0, NONE will be more prefered. This must be called - * before init - * - * @param preferenceForNone - * around 1.0 (default: 1.0) - */ - public void setPreferenceForNone(final double preferenceForNone) { - this.preferenceForNone = preferenceForNone; - } - - /** - * Values greater than 1.0 (towards infinite) increase the memory towards 1. - * Values smaller than 1.0 (towards zero) - * decreases the memory . - * - */ - public void tuneMemory(final double m) { - if (m == 0) - memoryA = 0.0; - else - memoryA = Math.pow(memoryA, 1.0 / m); - } - - /** - * To set manually the filter weights. This is not recommended, unless you - * know what you are doing. Setting this - * ignores preferenceForNone and omits some heuristics - * - * @param weights - * Five doubles around 1.0, one for each filter type. Lower is - * preferered - */ - public void setFilterWeights(final double[] weights) { - System.arraycopy(weights, 0, filter_weights, 0, 5); - } -} diff --git a/teavm/src/main/java/ar/com/hjg/pngj/pixels/PixelsWriter.java b/teavm/src/main/java/ar/com/hjg/pngj/pixels/PixelsWriter.java deleted file mode 100644 index 9ab95115..00000000 --- a/teavm/src/main/java/ar/com/hjg/pngj/pixels/PixelsWriter.java +++ /dev/null @@ -1,272 +0,0 @@ -package ar.com.hjg.pngj.pixels; - -import java.io.OutputStream; -import java.util.zip.Deflater; - -import ar.com.hjg.pngj.FilterType; -import ar.com.hjg.pngj.IDatChunkWriter; -import ar.com.hjg.pngj.ImageInfo; -import ar.com.hjg.pngj.PngHelperInternal; -import ar.com.hjg.pngj.PngjOutputException; - -/** - * Encodes a set of rows (pixels) as a continuous deflated stream (does not know - * about IDAT chunk segmentation). - *

- * This includes the filter selection strategy, plus the filtering itself and - * the deflating. Only supports fixed length - * rows (no interlaced writing). - *

- * Typically an instance of this is hold by a PngWriter - but more instances - * could be used (for APGN) - */ -public abstract class PixelsWriter { - - private static final int IDAT_MAX_SIZE_DEFAULT = 32000; - - protected final ImageInfo imgInfo; - /** - * row buffer length, including filter byte (imgInfo.bytesPerRow + 1) - */ - protected final int buflen; - - protected final int bytesPixel; - protected final int bytesRow; - - private CompressorStream compressorStream; // to compress the idat stream - - protected int deflaterCompLevel = 6; - protected int deflaterStrategy = Deflater.DEFAULT_STRATEGY; - - protected boolean initdone = false; - - /** - * This is the globally configured filter type - it can be a concrete type - * or a pseudo type (hint or strategy) - */ - protected FilterType filterType; - - // counts the filters used - just for stats - private final int[] filtersUsed = new int[5]; - - // this is the raw underlying os (shared with the PngWriter) - private OutputStream os; - - private int idatMaxSize = PixelsWriter.IDAT_MAX_SIZE_DEFAULT; - - /** - * row being processed, couting from zero - */ - protected int currentRow; - - public PixelsWriter(final ImageInfo imgInfo) { - this.imgInfo = imgInfo; - bytesRow = imgInfo.bytesPerRow; - buflen = bytesRow + 1; - bytesPixel = imgInfo.bytesPixel; - currentRow = -1; - filterType = FilterType.FILTER_DEFAULT; - } - - /** - * main internal point for external call. It does the lazy initializion if - * necessary, sets current row, and call - * {@link #filterAndWrite(byte[])} - */ - public final void processRow(final byte[] rowb) { - if (!initdone) - init(); - currentRow++; - filterAndWrite(rowb); - } - - protected void sendToCompressedStream(final byte[] rowf) { - compressorStream.write(rowf, 0, rowf.length); - filtersUsed[rowf[0]]++; - } - - /** - * This does the filtering and send to stream. Typically should decide the - * filtering, call - * {@link #filterRowWithFilterType(FilterType, byte[], byte[], byte[])} and - * and - * {@link #sendToCompressedStream(byte[])} - * - * @param rowb - */ - protected abstract void filterAndWrite(final byte[] rowb); - - /** - * Does the real filtering. This must be called with the real (standard) - * filterType. This should rarely be overriden. - *

- * WARNING: look out the contract - * - * @param _filterType - * @param _rowb - * current row (the first byte might be modified) - * @param _rowbprev - * previous row (should be all zero the first time) - * @param _rowf - * tentative buffer to store the filtered bytes. might not be - * used! - * @return normally _rowf, but eventually _rowb. This MUST NOT BE MODIFIED - * nor reused by caller - */ - final protected byte[] filterRowWithFilterType(final FilterType _filterType, final byte[] _rowb, - final byte[] _rowbprev, byte[] _rowf) { - // warning: some filters rely on: "previous row" (rowbprev) it must be initialized to 0 the - // first time - if (_filterType == FilterType.FILTER_NONE) - _rowf = _rowb; - _rowf[0] = (byte) _filterType.val; - int i, j; - switch (_filterType) { - case FILTER_NONE: - // we return the same original (be careful!) - break; - case FILTER_PAETH: - for (i = 1; i <= bytesPixel; i++) - _rowf[i] = (byte) PngHelperInternal.filterRowPaeth(_rowb[i], 0, _rowbprev[i] & 0xFF, 0); - for (j = 1, i = bytesPixel + 1; i <= bytesRow; i++, j++) - _rowf[i] = (byte) PngHelperInternal.filterRowPaeth(_rowb[i], _rowb[j] & 0xFF, _rowbprev[i] & 0xFF, _rowbprev[j] & 0xFF); - break; - case FILTER_SUB: - for (i = 1; i <= bytesPixel; i++) - _rowf[i] = _rowb[i]; - for (j = 1, i = bytesPixel + 1; i <= bytesRow; i++, j++) - _rowf[i] = (byte) (_rowb[i] - _rowb[j]); - break; - case FILTER_AVERAGE: - for (i = 1; i <= bytesPixel; i++) - _rowf[i] = (byte) (_rowb[i] - (_rowbprev[i] & 0xFF) / 2); - for (j = 1, i = bytesPixel + 1; i <= bytesRow; i++, j++) - _rowf[i] = (byte) (_rowb[i] - ((_rowbprev[i] & 0xFF) + (_rowb[j] & 0xFF)) / 2); - break; - case FILTER_UP: - for (i = 1; i <= bytesRow; i++) - _rowf[i] = (byte) (_rowb[i] - _rowbprev[i]); - break; - default: - throw new PngjOutputException("Filter type not recognized: " + _filterType); - } - return _rowf; - } - - /** - * This will be called by the PngWrite to fill the raw pixels for each row. - * This can change from call to call. - * Warning: this can be called before the object is init, implementations - * should call init() to be sure - */ - public abstract byte[] getRowb(); - - /** - * This will be called lazily just before writing row 0. Idempotent. - */ - protected final void init() { - if (!initdone) { - initParams(); - initdone = true; - } - } - - /** - * called by init(); override (calling this first) to do additional - * initialization - */ - protected void initParams() { - final IDatChunkWriter idatWriter = new IDatChunkWriter(os, idatMaxSize); - if (compressorStream == null) - compressorStream = new CompressorStreamDeflater(idatWriter, buflen, imgInfo.getTotalRawBytes(), deflaterCompLevel, deflaterStrategy); - } - - /** cleanup. This should be called explicitly. Idempotent and secure */ - public void close() { - if (compressorStream != null) - compressorStream.close(); - } - - /** - * Deflater (ZLIB) strategy. You should rarely change this from the default - * (Deflater.DEFAULT_STRATEGY) to - * Deflater.FILTERED (Deflater.HUFFMAN_ONLY is fast but compress poorly) - */ - public void setDeflaterStrategy(final Integer deflaterStrategy) { - this.deflaterStrategy = deflaterStrategy; - } - - /** - * Deflater (ZLIB) compression level, between 0 (no compression) and 9 - */ - public void setDeflaterCompLevel(final Integer deflaterCompLevel) { - this.deflaterCompLevel = deflaterCompLevel; - } - - public Integer getDeflaterCompLevel() { - return deflaterCompLevel; - } - - public final void setOs(final OutputStream datStream) { - os = datStream; - } - - public OutputStream getOs() { - return os; - } - - /** @see #filterType */ - final public FilterType getFilterType() { - return filterType; - } - - /** @see #filterType */ - final public void setFilterType(final FilterType filterType) { - this.filterType = filterType; - } - - /* out/in This should be called only after end() to get reliable results */ - public double getCompression() { - return compressorStream.isDone() ? compressorStream.getCompressionRatio() : 1.0; - } - - public void setCompressorStream(final CompressorStream compressorStream) { - this.compressorStream = compressorStream; - } - - public long getTotalBytesToWrite() { - return imgInfo.getTotalRawBytes(); - } - - public boolean isDone() { - return currentRow == imgInfo.rows - 1; - } - - /** - * computed default fixed filter type to use, if specified DEFAULT; wilde - * guess based on image properties - * - * @return One of the five concrete filter types - */ - protected FilterType getDefaultFilter() { - if (imgInfo.indexed || imgInfo.bitDepth < 8) - return FilterType.FILTER_NONE; - else if (imgInfo.getTotalPixels() < 1024) - return FilterType.FILTER_NONE; - else if (imgInfo.rows == 1) - return FilterType.FILTER_SUB; - else if (imgInfo.cols == 1) - return FilterType.FILTER_UP; - else - return FilterType.FILTER_PAETH; - } - - /** informational stats : filter used, in percentages */ - final public String getFiltersUsed() { - return String.format("%d,%d,%d,%d,%d", (int) (filtersUsed[0] * 100.0 / imgInfo.rows + 0.5), (int) (filtersUsed[1] * 100.0 / imgInfo.rows + 0.5), (int) (filtersUsed[2] * 100.0 / imgInfo.rows + 0.5), (int) (filtersUsed[3] * 100.0 / imgInfo.rows + 0.5), (int) (filtersUsed[4] * 100.0 / imgInfo.rows + 0.5)); - } - - public void setIdatMaxSize(final int idatMaxSize) { - this.idatMaxSize = idatMaxSize; - } -} diff --git a/teavm/src/main/java/ar/com/hjg/pngj/pixels/PixelsWriterDefault.java b/teavm/src/main/java/ar/com/hjg/pngj/pixels/PixelsWriterDefault.java deleted file mode 100644 index 00ffcec9..00000000 --- a/teavm/src/main/java/ar/com/hjg/pngj/pixels/PixelsWriterDefault.java +++ /dev/null @@ -1,158 +0,0 @@ -package ar.com.hjg.pngj.pixels; - -import java.util.Arrays; - -import ar.com.hjg.pngj.FilterType; -import ar.com.hjg.pngj.ImageInfo; -import ar.com.hjg.pngj.PngjOutputException; - -/** - * Default implementation of PixelsWriter, with fixed filters and also adaptive - * strategies. - */ -public class PixelsWriterDefault extends PixelsWriter { - /** current raw row */ - protected byte[] rowb; - /** previous raw row */ - protected byte[] rowbprev; - /** buffer for filtered row */ - protected byte[] rowbfilter; - - /** evaluates different filters, for adaptive strategy */ - protected FiltersPerformance filtersPerformance; - - /** currently concrete selected filter type */ - protected FilterType curfilterType; - - /** parameters for adaptive strategy */ - protected int adaptMaxSkip; // set in initParams, does not change - protected int adaptSkipIncreaseSinceRow; // set in initParams, does not change - protected double adaptSkipIncreaseFactor; // set in initParams, does not change - protected int adaptNextRow = 0; - - public PixelsWriterDefault(final ImageInfo imgInfo) { - super(imgInfo); - filtersPerformance = new FiltersPerformance(imgInfo); - } - - @Override - protected void initParams() { - super.initParams(); - - if (rowb == null || rowb.length < buflen) - rowb = new byte[buflen]; - if (rowbfilter == null || rowbfilter.length < buflen) - rowbfilter = new byte[buflen]; - if (rowbprev == null || rowbprev.length < buflen) - rowbprev = new byte[buflen]; - else - Arrays.fill(rowbprev, (byte) 0); - - // if adaptative but too few rows or columns, use default - if (imgInfo.cols < 3 && !FilterType.isValidStandard(filterType)) - filterType = FilterType.FILTER_DEFAULT; - if (imgInfo.rows < 3 && !FilterType.isValidStandard(filterType)) - filterType = FilterType.FILTER_DEFAULT; - - if (imgInfo.getTotalPixels() <= 1024 && !FilterType.isValidStandard(filterType)) - filterType = getDefaultFilter(); - - if (FilterType.isAdaptive(filterType)) { - // adaptCurSkip = 0; - adaptNextRow = 0; - if (filterType == FilterType.FILTER_ADAPTIVE_FAST) { - adaptMaxSkip = 200; - adaptSkipIncreaseSinceRow = 3; - adaptSkipIncreaseFactor = 1 / 4.0; // skip ~ row/3 - } else if (filterType == FilterType.FILTER_ADAPTIVE_MEDIUM) { - adaptMaxSkip = 8; - adaptSkipIncreaseSinceRow = 32; - adaptSkipIncreaseFactor = 1 / 80.0; - } else if (filterType == FilterType.FILTER_ADAPTIVE_FULL) { - adaptMaxSkip = 0; - adaptSkipIncreaseSinceRow = 128; - adaptSkipIncreaseFactor = 1 / 120.0; - } else - throw new PngjOutputException("bad filter " + filterType); - } - } - - @Override - protected void filterAndWrite(final byte[] rowb) { - if (rowb != this.rowb) - throw new RuntimeException("??"); // we rely on this - decideCurFilterType(); - final byte[] filtered = filterRowWithFilterType(curfilterType, rowb, rowbprev, rowbfilter); - sendToCompressedStream(filtered); - // swap rowb <-> rowbprev - final byte[] aux = this.rowb; - this.rowb = rowbprev; - rowbprev = aux; - } - - protected void decideCurFilterType() { - // decide the real filter and store in curfilterType - if (FilterType.isValidStandard(getFilterType())) - curfilterType = getFilterType(); - else if (getFilterType() == FilterType.FILTER_PRESERVE) - curfilterType = FilterType.getByVal(rowb[0]); - else if (getFilterType() == FilterType.FILTER_CYCLIC) - curfilterType = FilterType.getByVal(currentRow % 5); - else if (getFilterType() == FilterType.FILTER_DEFAULT) { - setFilterType(getDefaultFilter()); - curfilterType = getFilterType(); // this could be done once - } else if (FilterType.isAdaptive(getFilterType())) {// adaptive - if (currentRow == adaptNextRow) { - for (final FilterType ftype : FilterType.getAllStandard()) - filtersPerformance.updateFromRaw(ftype, rowb, rowbprev, currentRow); - curfilterType = filtersPerformance.getPreferred(); - int skip = currentRow >= adaptSkipIncreaseSinceRow ? (int) Math.round((currentRow - adaptSkipIncreaseSinceRow) * adaptSkipIncreaseFactor) : 0; - if (skip > adaptMaxSkip) - skip = adaptMaxSkip; - if (currentRow == 0) - skip = 0; - adaptNextRow = currentRow + 1 + skip; - } - } else - throw new PngjOutputException("not implemented filter: " + getFilterType()); - if (currentRow == 0 && curfilterType != FilterType.FILTER_NONE && curfilterType != FilterType.FILTER_SUB) - curfilterType = FilterType.FILTER_SUB; // first row should always be none or sub - } - - @Override - public byte[] getRowb() { - if (!initdone) - init(); - return rowb; - } - - @Override - public void close() { - super.close(); - } - - /** - * Only for adaptive strategies. See - * {@link FiltersPerformance#setPreferenceForNone(double)} - */ - public void setPreferenceForNone(final double preferenceForNone) { - filtersPerformance.setPreferenceForNone(preferenceForNone); - } - - /** - * Only for adaptive strategies. See - * {@link FiltersPerformance#tuneMemory(double)} - */ - public void tuneMemory(final double m) { - filtersPerformance.tuneMemory(m); - } - - /** - * Only for adaptive strategies. See - * {@link FiltersPerformance#setFilterWeights(double[])} - */ - public void setFilterWeights(final double[] weights) { - filtersPerformance.setFilterWeights(weights); - } - -} diff --git a/teavm/src/main/java/ar/com/hjg/pngj/pixels/PixelsWriterMultiple.java b/teavm/src/main/java/ar/com/hjg/pngj/pixels/PixelsWriterMultiple.java deleted file mode 100644 index bf36c12b..00000000 --- a/teavm/src/main/java/ar/com/hjg/pngj/pixels/PixelsWriterMultiple.java +++ /dev/null @@ -1,230 +0,0 @@ -package ar.com.hjg.pngj.pixels; - -import java.util.LinkedList; -import java.util.zip.Deflater; - -import ar.com.hjg.pngj.FilterType; -import ar.com.hjg.pngj.ImageInfo; - -/** Special pixels writer for experimental super adaptive strategy */ -public class PixelsWriterMultiple extends PixelsWriter { - /** - * unfiltered rowsperband elements, 0 is the current (rowb). This should - * include all rows of current band, plus one - */ - protected LinkedList rows; - - /** - * bank of compressor estimators, one for each filter and (perhaps) an - * adaptive strategy - */ - protected CompressorStream[] filterBank = new CompressorStream[6]; - /** - * stored filtered rows, one for each filter (0=none is not allocated but - * linked) - */ - protected byte[][] filteredRows = new byte[5][]; - protected byte[] filteredRowTmp; // - - protected FiltersPerformance filtersPerf; - protected int rowsPerBand = 0; // This is a 'nominal' size - protected int rowsPerBandCurrent = 0; // lastRowInThisBand-firstRowInThisBand +1 : might be - // smaller than rowsPerBand - protected int rowInBand = -1; - protected int bandNum = -1; - protected int firstRowInThisBand, lastRowInThisBand; - private boolean tryAdaptive = true; - - protected static final int HINT_MEMORY_DEFAULT_KB = 100; - // we will consume about (not more than) this memory (in buffers, not counting the compressors) - protected int hintMemoryKb = PixelsWriterMultiple.HINT_MEMORY_DEFAULT_KB; - - private int hintRowsPerBand = 1000; // default: very large number, can be changed - - private boolean useLz4 = true; - - public PixelsWriterMultiple(final ImageInfo imgInfo) { - super(imgInfo); - filtersPerf = new FiltersPerformance(imgInfo); - rows = new LinkedList<>(); - for (int i = 0; i < 2; i++) - rows.add(new byte[buflen]); // we preallocate 2 rows (rowb and rowbprev) - filteredRowTmp = new byte[buflen]; - } - - @Override - protected void filterAndWrite(final byte[] rowb) { - if (!initdone) - init(); - if (rowb != rows.get(0)) - throw new RuntimeException("?"); - setBandFromNewRown(); - final byte[] rowbprev = rows.get(1); - for (final FilterType ftype : FilterType.getAllStandardNoneLast()) { - // this has a special behaviour for NONE: filteredRows[0] is null, and the returned value is - // rowb - if (currentRow == 0 && ftype != FilterType.FILTER_NONE && ftype != FilterType.FILTER_SUB) - continue; - final byte[] filtered = filterRowWithFilterType(ftype, rowb, rowbprev, filteredRows[ftype.val]); - filterBank[ftype.val].write(filtered); - if (currentRow == 0 && ftype == FilterType.FILTER_SUB) { // litle lie, only for first row - filterBank[FilterType.FILTER_PAETH.val].write(filtered); - filterBank[FilterType.FILTER_AVERAGE.val].write(filtered); - filterBank[FilterType.FILTER_UP.val].write(filtered); - } - // adptive: report each filterted - if (tryAdaptive) - filtersPerf.updateFromFiltered(ftype, filtered, currentRow); - } - filteredRows[0] = rowb; - if (tryAdaptive) { - final FilterType preferredAdaptive = filtersPerf.getPreferred(); - filterBank[5].write(filteredRows[preferredAdaptive.val]); - } - if (currentRow == lastRowInThisBand) { - final int best = getBestCompressor(); - // PngHelperInternal.debug("won: " + best + " (rows: " + firstRowInThisBand + ":" + lastRowInThisBand + ")"); - // if(currentRow>90&¤tRow<100) - // PngHelperInternal.debug(String.format("row=%d ft=%s",currentRow,FilterType.getByVal(best))); - final byte[] filtersAdapt = filterBank[best].getFirstBytes(); - for (int r = firstRowInThisBand, i = 0, j = lastRowInThisBand - firstRowInThisBand; r <= lastRowInThisBand; r++, j--, i++) { - final int fti = filtersAdapt[i]; - byte[] filtered = null; - if (r != lastRowInThisBand) - filtered = filterRowWithFilterType(FilterType.getByVal(fti), rows.get(j), rows.get(j + 1), filteredRowTmp); - else - filtered = filteredRows[fti]; - sendToCompressedStream(filtered); - } - } - // rotate - if (rows.size() > rowsPerBandCurrent) - rows.addFirst(rows.removeLast()); - else - rows.addFirst(new byte[buflen]); - } - - @Override - public byte[] getRowb() { - return rows.get(0); - } - - private void setBandFromNewRown() { - final boolean newBand = currentRow == 0 || currentRow > lastRowInThisBand; - if (currentRow == 0) - bandNum = -1; - if (newBand) { - bandNum++; - rowInBand = 0; - } else - rowInBand++; - if (newBand) { - firstRowInThisBand = currentRow; - lastRowInThisBand = firstRowInThisBand + rowsPerBand - 1; - final int lastRowInNextBand = firstRowInThisBand + 2 * rowsPerBand - 1; - if (lastRowInNextBand >= imgInfo.rows) // hack:make this band bigger, so we don't have a small - // last band - lastRowInThisBand = imgInfo.rows - 1; - rowsPerBandCurrent = 1 + lastRowInThisBand - firstRowInThisBand; - tryAdaptive = rowsPerBandCurrent <= 3 || rowsPerBandCurrent < 10 && imgInfo.bytesPerRow < 64 ? false : true; - // rebuild bank - rebuildFiltersBank(); - } - } - - private void rebuildFiltersBank() { - final long bytesPerBandCurrent = rowsPerBandCurrent * (long) buflen; - final int DEFLATER_COMP_LEVEL = 4; - for (int i = 0; i <= 5; i++) {// one for each filter plus one adaptive - CompressorStream cp = filterBank[i]; - if (cp == null || cp.totalbytes != bytesPerBandCurrent) { - if (cp != null) - cp.close(); - if (useLz4) - cp = new CompressorStreamLz4(null, buflen, bytesPerBandCurrent); - else - cp = new CompressorStreamDeflater(null, buflen, bytesPerBandCurrent, DEFLATER_COMP_LEVEL, Deflater.DEFAULT_STRATEGY); - filterBank[i] = cp; - } else - cp.reset(); - cp.setStoreFirstByte(true, rowsPerBandCurrent); // TODO: only for adaptive? - } - } - - private int computeInitialRowsPerBand() { - // memory (only buffers) ~ (r+1+5) * bytesPerRow - int r = (int) (hintMemoryKb * 1024.0 / (imgInfo.bytesPerRow + 1) - 5); - if (r < 1) - r = 1; - if (hintRowsPerBand > 0 && r > hintRowsPerBand) - r = hintRowsPerBand; - if (r > imgInfo.rows) - r = imgInfo.rows; - if (r > 2 && r > imgInfo.rows / 8) { // redistribute more evenly - final int k = (imgInfo.rows + r - 1) / r; - r = (imgInfo.rows + k / 2) / k; - } - // PngHelperInternal.debug("rows :" + r + "/" + imgInfo.rows); - return r; - } - - private int getBestCompressor() { - double bestcr = Double.MAX_VALUE; - int bestb = -1; - for (int i = tryAdaptive ? 5 : 4; i >= 0; i--) { - final CompressorStream fb = filterBank[i]; - final double cr = fb.getCompressionRatio(); - if (cr <= bestcr) { // dirty trick, here the equality gains for row 0, so that SUB is prefered - // over PAETH, UP, AVE... - bestb = i; - bestcr = cr; - } - } - return bestb; - } - - @Override - protected void initParams() { - super.initParams(); - // if adaptative but too few rows or columns, use default - if (imgInfo.cols < 3 && !FilterType.isValidStandard(filterType)) - filterType = FilterType.FILTER_DEFAULT; - if (imgInfo.rows < 3 && !FilterType.isValidStandard(filterType)) - filterType = FilterType.FILTER_DEFAULT; - for (int i = 1; i <= 4; i++) - if (filteredRows[i] == null || filteredRows[i].length < buflen) - filteredRows[i] = new byte[buflen]; - if (rowsPerBand == 0) - rowsPerBand = computeInitialRowsPerBand(); - } - - @Override - public void close() { - super.close(); - rows.clear(); - for (final CompressorStream f : filterBank) - f.close(); - } - - public void setHintMemoryKb(final int hintMemoryKb) { - this.hintMemoryKb = hintMemoryKb <= 0 ? PixelsWriterMultiple.HINT_MEMORY_DEFAULT_KB : hintMemoryKb > 10000 ? 10000 : hintMemoryKb; - } - - public void setHintRowsPerBand(final int hintRowsPerBand) { - this.hintRowsPerBand = hintRowsPerBand; - } - - public void setUseLz4(final boolean lz4) { - useLz4 = lz4; - } - - /** for tuning memory or other parameters */ - public FiltersPerformance getFiltersPerf() { - return filtersPerf; - } - - public void setTryAdaptive(final boolean tryAdaptive) { - this.tryAdaptive = tryAdaptive; - } - -} diff --git a/teavm/src/main/java/ar/com/hjg/pngj/pixels/package.html b/teavm/src/main/java/ar/com/hjg/pngj/pixels/package.html deleted file mode 100644 index 85bd6a02..00000000 --- a/teavm/src/main/java/ar/com/hjg/pngj/pixels/package.html +++ /dev/null @@ -1,14 +0,0 @@ - - -

-Mostly related with logic specific to reading/writing pixels. -

-

-Includes ImageLine related classes, and rows filtering -

-

-Some classes like ImageLineInt should belong here, but we keep them in the main package for backward compatibility. - -

- - diff --git a/teavm/src/main/java/it/cavallium/warppi/gui/graphicengine/html/HtmlEngine.java b/teavm/src/main/java/it/cavallium/warppi/gui/graphicengine/html/HtmlEngine.java index 28e4bc6f..10b18814 100644 --- a/teavm/src/main/java/it/cavallium/warppi/gui/graphicengine/html/HtmlEngine.java +++ b/teavm/src/main/java/it/cavallium/warppi/gui/graphicengine/html/HtmlEngine.java @@ -119,7 +119,7 @@ public class HtmlEngine implements GraphicEngine { HtmlEngine.document.getElementById("container").appendChild(canvas); HtmlEngine.document.getBody().appendChild(keyInput); keyInput.setTabIndex(0); - keyInput.setValue("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); + keyInput.setValue(""); HtmlEngine.document.addEventListener("keydown", (final KeyboardEvent evt) -> { evt.preventDefault(); new Thread(() -> { diff --git a/teavm/src/main/java/it/cavallium/warppi/gui/graphicengine/html/HtmlSkin.java b/teavm/src/main/java/it/cavallium/warppi/gui/graphicengine/html/HtmlSkin.java index 19b291fe..39090002 100644 --- a/teavm/src/main/java/it/cavallium/warppi/gui/graphicengine/html/HtmlSkin.java +++ b/teavm/src/main/java/it/cavallium/warppi/gui/graphicengine/html/HtmlSkin.java @@ -2,14 +2,19 @@ package it.cavallium.warppi.gui.graphicengine.html; import java.io.IOException; import java.io.InputStream; -import java.net.URISyntaxException; +import org.apache.commons.lang3.ArrayUtils; import org.teavm.jso.browser.Window; +import org.teavm.jso.dom.events.Event; import org.teavm.jso.dom.html.HTMLDocument; import org.teavm.jso.dom.html.HTMLImageElement; -import ar.com.hjg.pngj.PngReader; import it.cavallium.warppi.Engine; +import it.cavallium.warppi.Platform.Semaphore; +import it.cavallium.warppi.flow.BehaviorSubject; +import it.cavallium.warppi.flow.SimpleSubject; +import it.cavallium.warppi.flow.Subject; +import it.cavallium.warppi.flow.ValueReference; import it.cavallium.warppi.gui.graphicengine.GraphicEngine; import it.cavallium.warppi.gui.graphicengine.Skin; @@ -38,22 +43,22 @@ public class HtmlSkin implements Skin { @Override public void load(String file) throws IOException { - if (!file.startsWith("/")) - file = "/" + file; - url = Engine.getPlatform().getStorageUtils().getBasePath() + file; - final InputStream stream = Engine.getPlatform().getStorageUtils().getResourceStream(file); - final PngReader r = new PngReader(stream); - skinSize = new int[] { r.imgInfo.cols, r.imgInfo.rows }; - r.close(); + url = Engine.getPlatform().getStorageUtils().getBasePath() + (!file.startsWith("/") ? "/" : "") + file; } @Override public void initialize(final GraphicEngine d) { final HTMLDocument doc = Window.current().getDocument(); + ValueReference done = new ValueReference(false); imgEl = doc.createElement("img").cast(); + imgEl.addEventListener("load", (Event e) -> { + done.value = true; + }); imgEl.setSrc(url); - imgEl.setClassName("hidden"); - doc.getBody().appendChild(imgEl); + while (!done.value) { + try {Thread.sleep(15);} catch (Exception e) {} + } + skinSize = new int[] { imgEl.getNaturalWidth(), imgEl.getNaturalHeight() }; initd = true; } diff --git a/teavm/src/main/java/it/cavallium/warppi/teavm/TeaVMImageReader.java b/teavm/src/main/java/it/cavallium/warppi/teavm/TeaVMImageReader.java index f0c1cff4..20c46018 100644 --- a/teavm/src/main/java/it/cavallium/warppi/teavm/TeaVMImageReader.java +++ b/teavm/src/main/java/it/cavallium/warppi/teavm/TeaVMImageReader.java @@ -2,59 +2,22 @@ package it.cavallium.warppi.teavm; import java.io.InputStream; -import ar.com.hjg.pngj.ImageLineInt; import it.cavallium.warppi.Platform.ImageUtils.ImageReader; public class TeaVMImageReader implements ImageReader { - - private final ar.com.hjg.pngj.PngReader r; - + public TeaVMImageReader(final InputStream resourceStream) { - r = new ar.com.hjg.pngj.PngReader(resourceStream); + throw new RuntimeException("Not supported by this platform"); } @Override public int[] getImageMatrix() { - final int width = r.imgInfo.cols; - final int height = r.imgInfo.rows; - final int channels = r.imgInfo.channels; - final int[] pixels = new int[width * height]; - int pi = 0; - ImageLineInt lint; - while (r.hasMoreRows()) { - lint = (ImageLineInt) r.readRow(); - final int[] scanLine = lint.getScanline(); - - for (int i = 0; i < width; i++) { - final int offset = i * channels; - - // Adjust the following code depending on your source image. - // I need the to set the alpha channel to 0xFF000000 since my destination image - // is TRANSLUCENT : BufferedImage bi = CONFIG.createCompatibleImage( width, height, Transparency.TRANSLUCENT ); - // my source was 3 channels RGB without transparency - int nextPixel; - if (channels == 4) - nextPixel = scanLine[offset] << 16 | scanLine[offset + 1] << 8 | scanLine[offset + 2] | scanLine[offset + 3] << 24; - else if (channels == 3) - nextPixel = scanLine[offset] << 16 | scanLine[offset + 1] << 8 | scanLine[offset + 2] | 0xFF << 24; - else if (channels == 2) - nextPixel = scanLine[offset] << 16 | scanLine[offset + 1] << 8 | 0xFF | 0xFF << 24; - else - nextPixel = scanLine[offset] << 16 | scanLine[offset] << 8 | scanLine[offset] | 0xFF << 24; - - // I'm placing the pixels on a memory mapped file - pixels[pi] = nextPixel; - pi++; - } - - } - - return pixels; + throw new RuntimeException("Not supported by this platform"); } @Override public int[] getSize() { - return new int[] { r.imgInfo.cols, r.imgInfo.rows }; + throw new RuntimeException("Not supported by this platform"); } } diff --git a/teavm/src/main/java/it/cavallium/warppi/teavm/TeaVMSemaphore.java b/teavm/src/main/java/it/cavallium/warppi/teavm/TeaVMSemaphore.java index ae8f3327..517dee8d 100644 --- a/teavm/src/main/java/it/cavallium/warppi/teavm/TeaVMSemaphore.java +++ b/teavm/src/main/java/it/cavallium/warppi/teavm/TeaVMSemaphore.java @@ -5,9 +5,9 @@ import java.util.Queue; public class TeaVMSemaphore implements it.cavallium.warppi.Platform.Semaphore { - private final Queue q; + private volatile Queue q; - private int freePermits = 0; + private volatile int freePermits = 0; public TeaVMSemaphore(final int i) { q = new LinkedList<>();