2003/11/03 07:36:40
authormegacz <megacz@xwt.org>
Fri, 30 Jan 2004 07:40:57 +0000 (07:40 +0000)
committermegacz <megacz@xwt.org>
Fri, 30 Jan 2004 07:40:57 +0000 (07:40 +0000)
darcs-hash:20040130074057-2ba56-1e5cb4e2a75d08acda2fda2188280515422417a9.gz

src/org/xwt/Font.java
src/org/xwt/Res.java
src/org/xwt/XWT.java
src/org/xwt/builtin/splash.xwt
src/org/xwt/translators/Freetype.java
src/org/xwt/util/CachedInputStream.java

index b1d9230..75bd9c8 100644 (file)
@@ -4,6 +4,7 @@ import org.xwt.translators.*;
 import org.xwt.util.*;
 import org.xwt.js.*;
 import java.util.*;
+import java.io.*;
 
 public class Font {
 
@@ -46,7 +47,6 @@ public class Font {
             if (g == null) glyphsToBeRendered.prepend(g = new Glyph(c));
             if (g.p == null) {
                 glyphsToBeRendered.prepend(g);
-                Scheduler.add(glyphRenderingTask);
                 encounteredUnrenderedGlyph = true;
             } else if (!encounteredUnrenderedGlyph) {
                 if (pb != null && g.p != null)
@@ -67,6 +67,7 @@ public class Font {
         }});
     
         if (!used) for(int i=32; i<128; i++) glyphsToBeRendered.append(glyphs[i] = new Glyph((char)i));
+        if (!used || encounteredUnrenderedGlyph) { System.out.println("foo!"); Scheduler.add(glyphRenderingTask); }
         used = true;
         return ((long)width << 16) | (long)height;
     }
@@ -85,9 +86,14 @@ public class Font {
     static final Queue glyphsToBeRendered = new Queue(255);
     static final Scheduler.Task glyphRenderingTask = new Scheduler.Task() { public void perform() {
         Glyph g = (Glyph)glyphsToBeRendered.remove(false);
-        if (g == null || g.p != null) return;
+        if (g == null) return;
+        if (g.p != null) { perform(); return; }
         Log.log(Glyph.class, "rendering glyph " + g.c);
-        freetype.renderGlyph(g);
+        try {
+            freetype.renderGlyph(g);
+        } catch (IOException e) {
+            Log.log(Freetype.class, e);
+        }
         Scheduler.add(this);
     } };
 }
index 954f290..3bf88ee 100644 (file)
@@ -11,7 +11,7 @@ import org.bouncycastle.util.encoders.Base64;
 /** base class for XWT resources */
 public abstract class Res extends JS {
 
-    public String getDescriptiveName() { return ""; }
+    public abstract String getDescriptiveName();
     public String typeName() { return "resource"; }
 
     /** cache of subresources so that the equality operator works on them */
@@ -87,13 +87,36 @@ public abstract class Res extends JS {
     }
 
     /** subclass from this if you want a CachedInputStream for each path */
-    public static abstract class CachedRes extends Res {
+    public static class CachedRes extends Res {
+        private Res parent;
+        private boolean disk = false;
+
+        // FIXME: security concern here
+        private String subdir = null;
+
+        public String getDescriptiveName() { return parent.getDescriptiveName(); }
         private Hash cachedInputStreams = new Hash();
-        abstract InputStream _getInputStream(String path) throws IOException;
-        public final InputStream getInputStream(String path) throws IOException {
+        public CachedRes(Res parent, String subdir, boolean disk) {
+            this.parent = parent; this.disk = disk; this.subdir = subdir;
+        }
+        public InputStream getInputStream(String path) throws IOException {
             CachedInputStream cis = (CachedInputStream)cachedInputStreams.get(path);
             if (cis == null) {
-                cis = new CachedInputStream(_getInputStream(path));
+                java.io.File f = null;
+                if (disk) {
+                    // FIXME ugly
+                    // FIXME need separate hash for disk/nondisk
+                    f = new java.io.File(System.getProperty("user.home") +
+                                         java.io.File.separatorChar + ".xwt" +
+                                         java.io.File.separatorChar + "caches" +
+                                         java.io.File.separatorChar + subdir +
+                                         java.io.File.separatorChar +
+                                         new String(Base64.encode(parent.getDescriptiveName().getBytes())));
+                    Log.log(this, "caching resource in " + f);
+                    new java.io.File(f.getParent()).mkdirs();
+                    if (f.exists()) return new FileInputStream(f);
+                }
+                cis = new CachedInputStream(parent.getInputStream(path), f);
                 cachedInputStreams.put(path, cis);
             }
             return cis.getInputStream();
@@ -101,11 +124,11 @@ public abstract class Res extends JS {
     }
 
     /** HTTP or HTTPS resource */
-    public static class HTTP extends CachedRes {
+    public static class HTTP extends Res {
         private String url;
         HTTP(String url) { this.url = url; }
         public String getDescriptiveName() { return url; }
-        public InputStream _getInputStream(String path) throws IOException {
+        public InputStream getInputStream(String path) throws IOException {
             return new org.xwt.HTTP(url + path).GET(); }
     }
 
index 0c01e67..53514ed 100644 (file)
@@ -88,6 +88,10 @@ public final class XWT extends JS.Obj {
             if (checkOnly) return Boolean.TRUE;
             return new Res.Cab((Res)args.elementAt(0));
 
+        } else if (method.equals("cache")) {
+            if (checkOnly) return Boolean.TRUE;
+            return new Res.CachedRes((Res)args.elementAt(0), "resources", true);
+
         } else if (method.equals("watchProgress")) {
             if (checkOnly) return Boolean.TRUE;
             return new Res.ProgressWatcher((Res)args.elementAt(0), (Function)args.elementAt(1));
@@ -195,15 +199,15 @@ public final class XWT extends JS.Obj {
         }
     }
 
+    // FIXME
     public static Object sleep(final int i) {
         final JS.Context jsthread = JS.Context.current();
         final long currentTime = System.currentTimeMillis();
-        final Scheduler.Task task = new Scheduler.Task() { public void perform() {
-            // FIXME: don't busy-wait
-            if (System.currentTimeMillis() - currentTime < i) Scheduler.add(this);
-            else jsthread.resume();
-        } };
-        Scheduler.add(task);
+        new Thread() { public void run() {
+            try { Thread.sleep(i); } catch (InterruptedException e) { }
+            Scheduler.add(new Scheduler.Task() { public void perform() {
+                jsthread.resume();
+            } }); } }.start();
         return JS.Context.pause;
     }
     
index 6294636..a99bb00 100644 (file)
@@ -11,7 +11,7 @@
 
         xwt.thread = function() {
 //            $text.font = xwt.fonts.vera["Vera.ttf"];
-            $text.font = xwt.uncab(xwt.load("http://master.dist.xwt.org/msfonts/arial32.exe"))["Arial.TTF"];
+            $text.font = xwt.cache(xwt.uncab(xwt.load("http://master.dist.xwt.org/msfonts/arial32.exe"))["Arial.TTF"]);
             $text.fontsize = 18;
             $text.text = "downloading...";
             fill = xwt.org.xwt.builtin["splash.png"];
@@ -28,6 +28,7 @@
             var new_xwt = xwt.clone(new_rr);
 
             xwt.thread = function() {
+xwt.sleep(1000); // let the fonts get pulled in
 for(var i=0; 100>i; i++) {
 progress(i, 100);
 xwt.yield();
index de5e0c0..18934c9 100644 (file)
@@ -4,6 +4,7 @@ import org.xwt.util.*;
 import java.io.*;
 import java.util.zip.*;
 import java.util.*;
+import org.bouncycastle.util.encoders.Base64;
 
 // FEATURE: use streams, not memoryfont's
 // FEATURE: kerning pairs
@@ -37,8 +38,33 @@ public class Freetype {
         }
     }
 
-    public synchronized void renderGlyph(Font.Glyph glyph) {
-        try {
+    public synchronized void renderGlyph(Font.Glyph glyph) throws IOException {
+        int width = 0;
+        int height = 0;
+        byte[] data = null;
+        String key = glyph.font.res.getDescriptiveName() + ":" + glyph.c;
+        key = new String(Base64.encode(key.getBytes()));
+        File cacheFile = new java.io.File(System.getProperty("user.home") +
+                                          java.io.File.separatorChar + ".xwt" +
+                                          java.io.File.separatorChar + "caches" +
+                                          java.io.File.separatorChar + "glyphs" +
+                                          java.io.File.separatorChar +
+                                          key);
+        new java.io.File(cacheFile.getParent()).mkdirs();
+
+        if (cacheFile.exists()) {
+            DataInputStream dis = new DataInputStream(new FileInputStream(cacheFile));
+            width = dis.readInt();
+            height = dis.readInt();
+            glyph.font.max_ascent = dis.readInt();
+            glyph.font.max_descent = dis.readInt();
+            glyph.baseline = dis.readInt();
+            glyph.advance = dis.readInt();
+            data = new byte[width * height];
+            if (width != 0 && height != 0) dis.readFully(data);
+            
+        } else try {
+            System.out.println("cache miss!");
             if (loadedStream != glyph.font.res) loadFontByteStream(glyph.font.res);
             vm.setUserInfo(2, (int)glyph.c);
             vm.setUserInfo(3, (int)glyph.c);
@@ -50,21 +76,29 @@ public class Freetype {
             glyph.baseline = vm.getUserInfo(10);
             glyph.advance = vm.getUserInfo(11);
             
-            int width = vm.getUserInfo(6);
-            int height = vm.getUserInfo(7);
-            if (width == 0 || height == 0) {
-                Log.log(Freetype.class, "warning glyph has zero width/height");
-                glyph.p = Platform.createAlphaOnlyPicture(new byte[] { }, 0, 0);
-                
-            } else {
-                byte[] data = new byte[width * height];
-                int addr = vm.getUserInfo(5);
-                vm.copyin(addr,data,width*height);
-                glyph.p = Platform.createAlphaOnlyPicture(data, width, height);
-            }
+            width = vm.getUserInfo(6);
+            height = vm.getUserInfo(7);
+
+            data = new byte[width * height];
+            int addr = vm.getUserInfo(5);
+            vm.copyin(addr,data,width*height);
+            File tmp = new File(cacheFile.getCanonicalPath() + ".tmp");
+            DataOutputStream dis = new DataOutputStream(new FileOutputStream(tmp));
+            dis.writeInt(width);
+            dis.writeInt(height);
+            dis.writeInt(glyph.font.max_ascent);
+            dis.writeInt(glyph.font.max_descent);
+            dis.writeInt(glyph.baseline);
+            dis.writeInt(glyph.advance);
+            if (width != 0 && height != 0) dis.write(data, 0, data.length);
+            dis.close();
+            tmp.renameTo(cacheFile);
 
         } catch (Exception e) {
             Log.log(this, e);
         }
+
+        if (width == 0 || height == 0) Log.log(Freetype.class, "warning glyph has zero width/height");
+        glyph.p = Platform.createAlphaOnlyPicture(data, width, height);
     }
 }
index d46569e..a599459 100644 (file)
@@ -1,6 +1,7 @@
 package org.xwt.util;
 import java.io.*;
 
+// FEATURE: don't use a byte[] if we have a diskCache file
 /**
  *  Wraps around an InputStream, caching the stream in a byte[] as it
  *  is read and permitting multiple simultaneous readers
@@ -12,9 +13,19 @@ public class CachedInputStream {
     byte[] cache = new byte[1024 * 128];
     int size = 0;
     final InputStream is;
+    File diskCache;
 
-    public CachedInputStream(InputStream is) { this.is = is; }
-    public InputStream getInputStream() { return new SubStream(); }
+    public CachedInputStream(InputStream is) { this(is, null); }
+    public CachedInputStream(InputStream is, File diskCache) {
+        this.is = is;
+        this.diskCache = diskCache;
+    }
+    public InputStream getInputStream() throws IOException {
+        System.out.println("diskCache == " + diskCache);
+        System.out.println("diskCache.exists() == " + diskCache.exists());
+        if (diskCache != null && diskCache.exists()) return new FileInputStream(diskCache);
+        return new SubStream();
+    }
 
     public void grow(int newLength) {
         if (newLength < cache.length) return;
@@ -28,7 +39,21 @@ public class CachedInputStream {
         filling = true;
         grow(size + howMuch);
         int ret = is.read(cache, size, howMuch);
-        if (ret == -1) eof = true;
+        if (ret == -1) {
+            eof = true;
+            // FIXME: probably a race here
+            if (diskCache != null && !diskCache.exists())
+                try {
+                    File cacheFile = new File(diskCache + ".tmp");
+                    FileOutputStream cacheFileStream = new FileOutputStream(cacheFile);
+                    cacheFileStream.write(cache, 0, size);
+                    cacheFileStream.close();
+                    cacheFile.renameTo(diskCache);
+                } catch (IOException e) {
+                    Log.log(this, "exception thrown while writing disk cache");
+                    Log.log(this, e);
+                }
+        }
         else size += ret;
         filling = false;
         notifyAll();