aboutsummaryrefslogtreecommitdiff
path: root/src/solaris/classes/sun/font/XRGlyphCache.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/solaris/classes/sun/font/XRGlyphCache.java')
-rw-r--r--src/solaris/classes/sun/font/XRGlyphCache.java301
1 files changed, 301 insertions, 0 deletions
diff --git a/src/solaris/classes/sun/font/XRGlyphCache.java b/src/solaris/classes/sun/font/XRGlyphCache.java
new file mode 100644
index 000000000..82c7c6a87
--- /dev/null
+++ b/src/solaris/classes/sun/font/XRGlyphCache.java
@@ -0,0 +1,301 @@
+/*
+ * Copyright 2010 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.font;
+
+import java.io.*;
+import java.util.*;
+
+import sun.awt.*;
+import sun.java2d.xr.*;
+
+/**
+ * Glyph cache used by the XRender pipeline.
+ *
+ * @author Clemens Eisserer
+ */
+
+public class XRGlyphCache implements GlyphDisposedListener {
+ XRBackend con;
+ XRCompositeManager maskBuffer;
+ HashMap<MutableInteger, XRGlyphCacheEntry> cacheMap = new HashMap<MutableInteger, XRGlyphCacheEntry>(256);
+
+ int nextID = 1;
+ MutableInteger tmp = new MutableInteger(0);
+
+ int grayGlyphSet;
+ int lcdGlyphSet;
+
+ int time = 0;
+ int cachedPixels = 0;
+ static final int MAX_CACHED_PIXELS = 100000;
+
+ ArrayList<Integer> freeGlyphIDs = new ArrayList<Integer>(255);
+
+ static final boolean batchGlyphUpload = true; // Boolean.parseBoolean(System.getProperty("sun.java2d.xrender.batchGlyphUpload"));
+
+ public XRGlyphCache(XRCompositeManager maskBuf) {
+ this.con = maskBuf.getBackend();
+ this.maskBuffer = maskBuf;
+
+ grayGlyphSet = con.XRenderCreateGlyphSet(XRUtils.PictStandardA8);
+ lcdGlyphSet = con.XRenderCreateGlyphSet(XRUtils.PictStandardARGB32);
+
+ StrikeCache.addGlyphDisposedListener(this);
+ }
+
+ public void glyphDisposed(ArrayList<Long> glyphPtrList) {
+ try {
+ SunToolkit.awtLock();
+
+ ArrayList<Integer> glyphIDList = new ArrayList<Integer>(glyphPtrList.size());
+ for (long glyphPtr : glyphPtrList) {
+ glyphIDList.add(XRGlyphCacheEntry.getGlyphID(glyphPtr));
+ }
+ freeGlyphs(glyphIDList);
+ } finally {
+ SunToolkit.awtUnlock();
+ }
+ }
+
+ protected int getFreeGlyphID() {
+ if (freeGlyphIDs.size() > 0) {
+ int newID = freeGlyphIDs.remove(freeGlyphIDs.size() - 1);
+ ;
+ return newID;
+ }
+ return nextID++;
+ }
+
+ protected XRGlyphCacheEntry getEntryForPointer(long imgPtr) {
+ int id = XRGlyphCacheEntry.getGlyphID(imgPtr);
+
+ if (id == 0) {
+ return null;
+ }
+
+ tmp.setValue(id);
+ return cacheMap.get(tmp);
+ }
+
+ public XRGlyphCacheEntry[] cacheGlyphs(GlyphList glyphList) {
+ time++;
+
+ XRGlyphCacheEntry[] entries = new XRGlyphCacheEntry[glyphList.getNumGlyphs()];
+ long[] imgPtrs = glyphList.getImages();
+ ArrayList<XRGlyphCacheEntry> uncachedGlyphs = null;
+
+ for (int i = 0; i < glyphList.getNumGlyphs(); i++) {
+ XRGlyphCacheEntry glyph;
+
+ // Find uncached glyphs and queue them for upload
+ if ((glyph = getEntryForPointer(imgPtrs[i])) == null) {
+ glyph = new XRGlyphCacheEntry(imgPtrs[i], glyphList);
+ glyph.setGlyphID(getFreeGlyphID());
+ cacheMap.put(new MutableInteger(glyph.getGlyphID()), glyph);
+
+ if (uncachedGlyphs == null) {
+ uncachedGlyphs = new ArrayList<XRGlyphCacheEntry>();
+ }
+ uncachedGlyphs.add(glyph);
+ }
+ glyph.setLastUsed(time);
+ entries[i] = glyph;
+ }
+
+ // Add glyphs to cache
+ if (uncachedGlyphs != null) {
+ uploadGlyphs(entries, uncachedGlyphs, glyphList, null);
+ }
+
+ return entries;
+ }
+
+ protected void uploadGlyphs(XRGlyphCacheEntry[] glyphs, ArrayList<XRGlyphCacheEntry> uncachedGlyphs, GlyphList gl, int[] glIndices) {
+ for (XRGlyphCacheEntry glyph : uncachedGlyphs) {
+ cachedPixels += glyph.getPixelCnt();
+ }
+
+ if (cachedPixels > MAX_CACHED_PIXELS) {
+ clearCache(glyphs);
+ }
+
+ boolean containsLCDGlyphs = containsLCDGlyphs(uncachedGlyphs);
+ List<XRGlyphCacheEntry>[] seperatedGlyphList = seperateGlyphTypes(uncachedGlyphs, containsLCDGlyphs);
+ List<XRGlyphCacheEntry> grayGlyphList = seperatedGlyphList[0];
+ List<XRGlyphCacheEntry> lcdGlyphList = seperatedGlyphList[1];
+
+ /*
+ * Some XServers crash when uploading multiple glyphs at once. TODO:
+ * Implement build-switch in local case for distributors who know their
+ * XServer is fixed
+ */
+ if (batchGlyphUpload) {
+ if (grayGlyphList != null && grayGlyphList.size() > 0) {
+ con.XRenderAddGlyphs(grayGlyphSet, gl, grayGlyphList, generateGlyphImageStream(grayGlyphList));
+ }
+ if (lcdGlyphList != null && lcdGlyphList.size() > 0) {
+ con.XRenderAddGlyphs(lcdGlyphSet, gl, lcdGlyphList, generateGlyphImageStream(lcdGlyphList));
+ }
+ } else {
+ ArrayList<XRGlyphCacheEntry> tmpList = new ArrayList<XRGlyphCacheEntry>(1);
+ tmpList.add(null);
+
+ for (XRGlyphCacheEntry entry : uncachedGlyphs) {
+ tmpList.set(0, entry);
+
+ if (entry.getGlyphSet() == grayGlyphSet) {
+ con.XRenderAddGlyphs(grayGlyphSet, gl, tmpList, generateGlyphImageStream(tmpList));
+ } else {
+ con.XRenderAddGlyphs(lcdGlyphSet, gl, tmpList, generateGlyphImageStream(tmpList));
+ }
+ }
+ }
+ }
+
+ /**
+ * Seperates lcd and grayscale glyphs queued for upload, and sets the
+ * appropriate glyphset for the cache entries.
+ */
+ protected List<XRGlyphCacheEntry>[] seperateGlyphTypes(List<XRGlyphCacheEntry> glyphList, boolean containsLCDGlyphs) {
+ ArrayList<XRGlyphCacheEntry> lcdGlyphs = null;
+ ArrayList<XRGlyphCacheEntry> grayGlyphs = null;
+
+ for (XRGlyphCacheEntry cacheEntry : glyphList) {
+ if (cacheEntry.isGrayscale(containsLCDGlyphs)) {
+ if (grayGlyphs == null) {
+ grayGlyphs = new ArrayList<XRGlyphCacheEntry>(glyphList.size());
+ }
+ cacheEntry.setGlyphSet(grayGlyphSet);
+ grayGlyphs.add(cacheEntry);
+ } else {
+ if (lcdGlyphs == null) {
+ lcdGlyphs = new ArrayList<XRGlyphCacheEntry>(glyphList.size());
+ }
+ cacheEntry.setGlyphSet(lcdGlyphSet);
+ lcdGlyphs.add(cacheEntry);
+ }
+ }
+
+ return new List[] { grayGlyphs, lcdGlyphs };
+ }
+
+ /**
+ * Copies the glyph-images into a continous buffer, required for uploading.
+ */
+ protected byte[] generateGlyphImageStream(List<XRGlyphCacheEntry> glyphList) {
+ boolean isLCDGlyph = glyphList.get(0).getGlyphSet() == lcdGlyphSet;
+
+ ByteArrayOutputStream stream = new ByteArrayOutputStream((isLCDGlyph ? 4 : 1) * 48 * glyphList.size());
+ for (XRGlyphCacheEntry cacheEntry : glyphList) {
+ cacheEntry.writePixelData(stream, isLCDGlyph);
+ }
+
+ return stream.toByteArray();
+ }
+
+ protected boolean containsLCDGlyphs(List<XRGlyphCacheEntry> entries) {
+ boolean containsLCDGlyphs = false;
+
+ for (XRGlyphCacheEntry entry : entries) {
+ containsLCDGlyphs = !(entry.getSourceRowBytes() == entry.getWidth());
+
+ if (containsLCDGlyphs) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ protected void clearCache(XRGlyphCacheEntry[] glyps) {
+ /*
+ * Glyph uploading is so slow anyway, we can afford some inefficiency
+ * here, as the cache should usually be quite small. TODO: Implement
+ * something not that stupid ;)
+ */
+ ArrayList<XRGlyphCacheEntry> cacheList = new ArrayList<XRGlyphCacheEntry>(cacheMap.values());
+ Collections.sort(cacheList, new Comparator<XRGlyphCacheEntry>() {
+ public int compare(XRGlyphCacheEntry e1, XRGlyphCacheEntry e2) {
+ return e2.getLastUsed() - e1.getLastUsed();
+ }
+ });
+
+ for (XRGlyphCacheEntry glyph : glyps) {
+ glyph.setPinned();
+ }
+
+ ArrayList<Integer> deleteGlyphList = new ArrayList<Integer>();
+ int pixelsToRelease = cachedPixels - MAX_CACHED_PIXELS;
+
+ for (int i = cacheList.size() - 1; i >= 0 && pixelsToRelease > 0; i--) {
+ XRGlyphCacheEntry entry = cacheList.get(i);
+
+ if (!entry.isPinned()) {
+ pixelsToRelease -= entry.getPixelCnt();
+ deleteGlyphList.add(new Integer(entry.getGlyphID()));
+ }
+ }
+
+ for (XRGlyphCacheEntry glyph : glyps) {
+ glyph.setUnpinned();
+ }
+
+ freeGlyphs(deleteGlyphList);
+ }
+
+ private void freeGlyphs(List<Integer> glyphIdList) {
+
+ freeGlyphIDs.addAll(glyphIdList);
+
+ GrowableIntArray removedLCDGlyphs = new GrowableIntArray(1, 1);
+ GrowableIntArray removedGrayscaleGlyphs = new GrowableIntArray(1, 1);
+
+ for (Integer glyphId : glyphIdList) {
+ tmp.setValue(glyphId.intValue());
+ XRGlyphCacheEntry entry = cacheMap.get(tmp);
+ cachedPixels -= entry.getPixelCnt();
+
+ int removedGlyphID = entry.getGlyphID();
+ tmp.setValue(removedGlyphID);
+ cacheMap.remove(tmp);
+
+ if (entry.getGlyphSet() == grayGlyphSet) {
+ removedGrayscaleGlyphs.addInt(removedGlyphID);
+ } else {
+ removedLCDGlyphs.addInt(removedGlyphID);
+ }
+
+ entry.setGlyphID(0);
+ }
+
+ if (removedGrayscaleGlyphs.getSize() > 0) {
+ con.XRenderFreeGlyphs(grayGlyphSet, removedGrayscaleGlyphs.getSizedArray());
+ }
+
+ if (removedLCDGlyphs.getSize() > 0) {
+ con.XRenderFreeGlyphs(lcdGlyphSet, removedLCDGlyphs.getSizedArray());
+ }
+ }
+}