added Affine.premultiply()
[org.ibex.core.git] / src / org / ibex / graphics / Surface.java
index 7e23169..31ac36e 100644 (file)
@@ -1,8 +1,11 @@
 // Copyright 2004 Adam Megacz, see the COPYING file for licensing [GPL]
-package org.ibex;
+package org.ibex.graphics;
 
 import org.ibex.js.*;
 import org.ibex.util.*;
+import org.ibex.plat.*;
+
+import org.ibex.core.*;  // FIXME
 
 /** 
  *  A Surface, as described in the Ibex Reference.
@@ -10,7 +13,7 @@ import org.ibex.util.*;
  *  Platform subclasses should include an inner class subclass of
  *  Surface to return from the Platform._createSurface() method
  */
-public abstract class Surface extends PixelBuffer implements Scheduler.Task {
+public abstract class Surface extends PixelBuffer implements Task {
 
     // Static Data ////////////////////////////////////////////////////////////////////////////////
 
@@ -21,12 +24,12 @@ public abstract class Surface extends PixelBuffer implements Scheduler.Task {
     public static Vec allSurfaces = new Vec();
     
     /** When set to true, render() should abort as soon as possible and restart the rendering process */
-    volatile boolean abort = false;
+    public volatile boolean abort = false;
 
     // these three variables are used to ensure that user resizes trump programmatic resizes
-    volatile boolean syncRootBoxToSurface = false;
-    volatile int pendingWidth = 0;
-    volatile int pendingHeight = 0;
+    public volatile boolean syncRootBoxToSurface = false;
+    public volatile int pendingWidth = 0;
+    public volatile int pendingHeight = 0;
 
     public static boolean alt = false;          ///< true iff the alt button is pressed down
     public static boolean control = false;      ///< true iff the control button is pressed down
@@ -48,6 +51,7 @@ public abstract class Surface extends PixelBuffer implements Scheduler.Task {
     public int newmousey = -1;                         ///< y position of the mouse, in real time; this lets us collapse Move's
     public boolean minimized = false;                  ///< True iff this surface is minimized, in real time
     public boolean maximized = false;                  ///< True iff this surface is maximized, in real time
+    public boolean unrendered = true;                  ///< True iff this surface has not yet been rendered
     DirtyList dirtyRegions = new DirtyList();          ///< Dirty regions on the surface
 
     // Used For Simulating Clicks and DoubleClicks /////////////////////////////////////////////////
@@ -74,6 +78,7 @@ public abstract class Surface extends PixelBuffer implements Scheduler.Task {
     public void setMinimumSize(int minx, int miny, boolean resizable) { }
     protected void setSize(int w, int h) { _setSize(w, h); }
 
+    public static Picture scarImage = null;
 
     // Helper methods for subclasses ////////////////////////////////////////////////////////////
 
@@ -88,7 +93,7 @@ public abstract class Surface extends PixelBuffer implements Scheduler.Task {
         if (button == 1) new Message("_Press1", T, root);
         else if (button == 2) new Message("_Press2", T, root);
         else if (button == 3) {
-            Scheduler.add(new Scheduler.Task() { public void perform() throws JSExn {
+            Scheduler.add(new Task() { public void perform() throws JSExn {
                 Platform.clipboardReadEnabled = true;
                 try {
                     root.putAndTriggerTraps("_Press3", T);
@@ -126,7 +131,7 @@ public abstract class Surface extends PixelBuffer implements Scheduler.Task {
     }
 
     /** we enqueue ourselves in the Scheduler when we have a Move message to deal with */
-    private Scheduler.Task mover = new Scheduler.Task() {
+    private Task mover = new Task() {
             public void perform() {
                 if (mousex == newmousex && mousey == newmousey) return;
                 int oldmousex = mousex;     mousex = newmousex;
@@ -157,7 +162,7 @@ public abstract class Surface extends PixelBuffer implements Scheduler.Task {
 
     /** subclasses should invoke this method when the user resizes the window */
     protected final void SizeChange(final int width, final int height) {
-        if (pendingWidth == width && pendingHeight == height) return;
+        if (unrendered || (pendingWidth == width && pendingHeight == height)) return;
         pendingWidth = width;
         pendingHeight = height;
         syncRootBoxToSurface = true;
@@ -167,7 +172,7 @@ public abstract class Surface extends PixelBuffer implements Scheduler.Task {
 
     // FEATURE: can we avoid creating objects here?
     protected final void PosChange(final int x, final int y) {
-        Scheduler.add(new Scheduler.Task() { public void perform() throws JSExn {
+        Scheduler.add(new Task() { public void perform() throws JSExn {
             root.x = x;
             root.y = y;
             root.putAndTriggerTrapsAndCatchExceptions("PosChange", T);
@@ -220,15 +225,26 @@ public abstract class Surface extends PixelBuffer implements Scheduler.Task {
 
     public Surface(Box root) {
         this.root = root;
-        root.setMaxWidth(JS.N(Math.min(Platform.getScreenWidth(), root.maxwidth)));
-        root.setMaxHeight(JS.N(Math.min(Platform.getScreenHeight(), root.maxheight)));
+        // FIXME: document this in the reference
+        if (!root.test(root.HSHRINK) && root.maxwidth == Integer.MAX_VALUE)
+            root.maxwidth = Platform.getScreenWidth() / 2;
+        if (!root.test(root.VSHRINK) && root.maxheight == Integer.MAX_VALUE)
+            root.maxheight = Platform.getScreenHeight() / 2;
+        root.setWidth(root.minwidth,
+                      root.test(root.HSHRINK)
+                      ? Math.max(root.minwidth, root.contentwidth)
+                      : Math.min(Platform.getScreenWidth(), root.maxwidth));
+        root.setHeight(root.minheight,
+                      root.test(root.VSHRINK)
+                      ? Math.max(root.minheight, root.contentheight)
+                      : Math.min(Platform.getScreenHeight(), root.maxheight));
         Surface old = fromBox(root);
         if (old != null) old.dispose(false);
         else root.removeSelf();
         Refresh();
     }
 
-    private static VectorGraphics.Affine identity = VectorGraphics.Affine.identity();
+    private static Affine identity = Affine.identity();
 
     /** runs the prerender() and render() pipelines in the root Box to regenerate the backbuffer, then blits it to the screen */
     public synchronized void render() {
@@ -238,17 +254,19 @@ public abstract class Surface extends PixelBuffer implements Scheduler.Task {
             abort = false;
             root.pack();
             if (syncRootBoxToSurface) {
-                root.setMaxWidth(JS.N(pendingWidth));
-                root.setMaxHeight(JS.N(pendingHeight));
+                root.setWidth(root.minwidth, pendingWidth);
+                root.setHeight(root.minheight, pendingHeight);
                 syncRootBoxToSurface = false;
             }
-            if (root.maxwidth != root.width || root.maxheight != root.height) {
+            int rootwidth = root.test(root.HSHRINK) ? root.contentwidth : root.maxwidth;
+            int rootheight = root.test(root.VSHRINK) ? root.contentheight : root.maxheight;
+            if (rootwidth != root.width || rootheight != root.height) {
                 // dirty the place where the scar used to be and where it is now
-                dirty(0, root.height - Main.scarImage.height, Main.scarImage.width, Main.scarImage.height);
-                dirty(0, root.maxheight - Main.scarImage.height, Main.scarImage.width, Main.scarImage.height);
+                dirty(0, root.height - scarImage.height, scarImage.width, scarImage.height);
+                dirty(0, rootheight - scarImage.height, scarImage.width, scarImage.height);
             }
             root.reflow();
-            setSize(root.width, root.height);
+            setSize(rootwidth, rootheight);
             /*String oldcursor = cursor;
             cursor = "default";
             root.putAndTriggerTrapsAndCatchExceptions("_Move", JS.T);
@@ -266,7 +284,7 @@ public abstract class Surface extends PixelBuffer implements Scheduler.Task {
             if (w <= 0 || h <= 0) continue;
 
             root.render(0, 0, x, y, x + w, y + h, this, identity);
-            drawPicture(Main.scarImage, 0, root.height - Main.scarImage.height, x, y, x+w, y+h);
+            drawPicture(scarImage, 0, root.height - scarImage.height, x, y, x+w, y+h);
             
             if (abort) {
                 // x,y,w,h is only partially reconstructed, so we must be careful not to re-blit it
@@ -278,10 +296,12 @@ public abstract class Surface extends PixelBuffer implements Scheduler.Task {
                 return;
             }
         }
+
+        unrendered = false;
     }
 
     // FEATURE: reinstate recycler
-    public class Message implements Scheduler.Task {
+    public class Message implements Task {
         
         private Box boxContainingMouse;
         private Object value;