Compare commits

..

21 Commits

Author SHA1 Message Date
Vyacheslav Moklev
f0831a9d90 Fix for IDEA-214698 2019-05-27 15:45:37 +03:00
Alexey Ushakov
5f9b3a6dda JBR-1521 Update quality tests to Java 11
Added golden images for TextMetricsTest from Mojave
2019-05-27 13:38:48 +03:00
Alexey Ushakov
cab6dd5087 JBR-1521 Update quality tests to Java 11
Added golden images for MixedTextTest from Mojave
2019-05-27 13:21:51 +03:00
Vitaly Provodin
e4b063cb1f updated JTreg exclude list 2019-05-27 14:24:08 +07:00
Alexey Ushakov
bbd90315c9 JBR-1521 Update quality tests to Java 11
Updated golden images for DroidFontTest on Windows
2019-05-26 18:51:25 +03:00
Alexey Ushakov
c93b853a2b Updated gradle project:
- provided correct path to test jdk
 - used modern syntax instead of <<
2019-05-26 14:56:31 +03:00
Vitaly Provodin
d65279cdc1 updated JTreg exclude list 2019-05-24 10:49:09 +07:00
Alexey Ushakov
6c71f3a853 JBR-1521 Update quality tests to Java 11
Updated golden images for DroidFontTest on linux
2019-05-22 22:56:57 +03:00
Alexey Ushakov
95a5c1661b JBR-1521 Update quality tests to Java 11
Added golden images for DroidFontTest from Mojave
2019-05-22 16:59:01 +03:00
Alexey Ushakov
153020320d JBR-1415 CLion with jdk11, frame around Exit button
In addition fallback to normal painting for sub-components of non-opaque
dialogs and frames
2019-05-21 19:27:59 +03:00
Alexey Ushakov
23e2ac4c4e JBR-1528 backport rendering performance microbenchmarks from openjdk-metal branch
Backport test/jdk/jbu/quality/metal/MetalRenderTest.java
2019-05-21 12:51:57 +03:00
Denis Fokin
29ec6c2bce JBR-1311 [JBR 11] Double quote adding special character on macOS with "English, U.S. International - PC" layout 2019-05-20 19:58:03 +03:00
Alexey Ushakov
7997c7a5ee JBR-1521 Update quality tests to Java 11
Updated RenderUtil to Java 11
2019-05-17 23:17:47 +03:00
Alexey Ushakov
14659f0d30 JBR-1520 [fwp to JBR11] JRE-722 LCD text rendering performance on OSX 25X slower than grayscale
Fixed LCD text performance by using bulk rendering via vertex arrays and
reducing glTextureBarrierNV usages
Added native logging of accelerated text rendering
Replaced glClientActiveTextureARB with glClientActiveTexture
(it is now part of OpenGL 1.3 standard)
2019-05-17 23:17:46 +03:00
Alexey Ushakov
a868128d2e Updated .gitignore 2019-05-17 23:17:46 +03:00
Dmitry Batrak
1e1b932a4c JBR-1517 Update font layout speedup code to match the variant submitted to OpenJDK (JDK-8220231)
apply corresponding change from OpenJDK 13
2019-05-17 15:08:47 +03:00
Dmitry Batrak
bac12d4598 JBR-1517 Update font layout speedup code to match the variant submitted to OpenJDK (JDK-8220231)
revert original implementation of font layout speedup
2019-05-17 13:19:43 +03:00
Dmitry Batrak
2bbfe3bf2f JBR-1435 Various problems with emojis
add piece of code that was missed during migration from JBR 8
2019-05-17 11:23:03 +03:00
Vitaly Provodin
38abcae3e9 updated JTreg exclude list 2019-05-17 14:09:59 +07:00
Anton Tarasov
1746b04686 JBR-1427 pycharm jupyter preview stuck and no response when click on preview. 2019-05-16 18:28:02 +03:00
Vyacheslav Moklev
00d32e58dc JBR-1509 Client area size is wrong in Borderless mode
Fix client area size
2019-05-16 11:51:42 +03:00
65 changed files with 1312 additions and 472 deletions

2
.gitignore vendored
View File

@@ -3,3 +3,5 @@ JTreport
*.class
.idea/workspace.xml
build/
# Project exclude paths
/jb/project/java-gradle/.gradle/

View File

@@ -10,11 +10,11 @@ def test_jvm = {
file(jbsdkhome + (OperatingSystem.current().isWindows()?"/bin/java.exe" : "/bin/java")).absolutePath
} else {
if (OperatingSystem.current().isMacOsX()) {
file('../../../build/macosx-x86_64-normal-server-release/images/jdk-bundle/jdk-11.jdk/Contents/Home/bin/java').absolutePath
file('../../../build/macosx-x86_64-normal-server-release/images/jdk-bundle/jdk-11.0.3.jdk/Contents/Home/bin/java').absolutePath
} else if (OperatingSystem.current().isLinux()) {
file('../../../build/linux-x86_64-normal-server-release/images/jdk/bin/java').absolutePath
} else {
file('../../../build/windows-x86_64-normal-server-release/images/j2sdk-image/bin/java.exe').absolutePath
file('../../../build/windows-x86_64-normal-server-release/images/jdk/bin/java.exe').absolutePath
}
}
}
@@ -75,15 +75,21 @@ test.dependsOn tasks.compileTestJava
test {
systemProperty "jb.java2d.metal", "true"
systemProperty "testdata", file('../../../jb/tests/testdata').absolutePath
systemProperty "testdata", file('../../../test/jdk/jbu/testdata').absolutePath
// Generate golden images for DroidFontTest and MixedTextTest
// systemProperty "gentestdata", ""
// Enable Java2D logging (https://confluence.jetbrains.com/display/JRE/Java2D+Rendering+Logging)
// systemProperty "sun.java2d.trace", "log"
// systemProperty "sun.java2d.trace", "log,pimpl"
outputs.upToDateWhen { false }
executable = test_jvm()
// Enable J2D logging (only in debug build)
// Enable async/dtrace profiler
jvmArgs "-XX:+PreserveFramePointer"
// Enable native J2D logging (only in debug build)
// Can be turned on for J2D by adding "#define DEBUG 1" into jdk/src/share/native/sun/java2d/Trace.h
// environment 'J2D_TRACE_LEVEL', '4'
@@ -96,24 +102,34 @@ if (OperatingSystem.current().isWindows()) {
def cyg_make_cmd = new File("c:/cygwin64/bin/make.exe")
if (cyg_make_cmd.exists()) make_cmd = cyg_make_cmd.absolutePath
}
task make_images << {
def pb = new ProcessBuilder().command(make_cmd.toString(), "-C", buildDir.absolutePath, "images")
def proc = pb.redirectErrorStream(true).start()
proc.inputStream.eachLine {println it}
assert proc.waitFor() == 0
def test_run = false
task make_images {
doLast {
if (!test_run) {
def pb = new ProcessBuilder().command(make_cmd.toString(), "-C", buildDir.absolutePath, "images")
def proc = pb.redirectErrorStream(true).start()
proc.inputStream.eachLine { println it }
assert proc.waitFor() == 0
}
}
}
task make_clean << {
def pb = new ProcessBuilder().command(make_cmd.toString(), "-C", buildDir.absolutePath, "clean")
def proc = pb.redirectErrorStream(true).start()
proc.inputStream.eachLine {println it}
assert proc.waitFor() == 0
task make_clean {
doLast {
def pb = new ProcessBuilder().command(make_cmd.toString(), "-C", buildDir.absolutePath, "clean")
def proc = pb.redirectErrorStream(true).start()
proc.inputStream.eachLine { println it }
assert proc.waitFor() == 0
}
}
task run_test {
doLast {
test_run = true
}
}
tasks.compileJava.enabled = false
tasks.compileTestJava.dependsOn.clear()
tasks.cleanTest.dependsOn tasks.run_test
classes.dependsOn.clear()
classes.dependsOn tasks.make_images
tasks.cleanClasses.dependsOn tasks.make_clean

View File

@@ -31,20 +31,12 @@ import java.awt.geom.AffineTransform;
import java.awt.geom.GeneralPath;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.security.AccessController;
import java.security.PrivilegedAction;
// Right now this class is final to avoid a problem with native code.
// For some reason the JNI IsInstanceOf was not working correctly
// so we are checking the class specifically. If we subclass this
// we need to modify the native code in CFontWrapper.m
public final class CFont extends PhysicalFont implements FontSubstitution {
private static final boolean useCoreTextLayout;
static {
useCoreTextLayout = AccessController.doPrivileged((PrivilegedAction<Boolean>) () ->
Boolean.getBoolean("sun.font.use.coretext.layout"));
}
/* CFontStrike doesn't call these methods so they are unimplemented.
* They are here to meet the requirements of PhysicalFont, needed
@@ -84,18 +76,11 @@ public final class CFont extends PhysicalFont implements FontSubstitution {
throw new InternalError("Not implemented");
}
@Override
protected long getLayoutTableCache() {
return getLayoutTableCacheNative(getNativeFontPtr());
}
@Override
protected byte[] getTableBytes(int tag) {
return getTableBytesNative(getNativeFontPtr(), tag);
}
private native synchronized long getLayoutTableCacheNative(long nativeFontPtr);
private native byte[] getTableBytesNative(long nativeFontPtr, int tag);
private static native long createNativeFont(final String nativeFontName,
@@ -254,13 +239,6 @@ public final class CFont extends PhysicalFont implements FontSubstitution {
return nativeFontName;
}
@Override
protected boolean isAAT() {
// CoreText layout code ignores fractional metrics font attribute
// also, using CoreText layout in Harfbuzz code leads to wrong advances for emoji glyphs
return useCoreTextLayout && !"AppleColorEmoji".equals(nativeFontName) && super.isAAT();
}
// <rdar://problem/5321707> sun.font.Font2D caches the last used strike,
// but does not check if the properties of the strike match the properties
// of the incoming java.awt.Font object (size, style, etc).

View File

@@ -758,7 +758,7 @@ public class CInputMethod extends InputMethodAdapter {
}
}
}}
}, fAwtFocussedComponent);
}, fAwtFocussedComponent, true); // [tav] avoid deadlock with javafx
} catch (InvocationTargetException ite) { ite.printStackTrace(); }
synchronized(rect) { return rect; }
@@ -779,7 +779,7 @@ public class CInputMethod extends InputMethodAdapter {
offsetInfo[0] = fIMContext.getLocationOffset(screenX, screenY);
insertPositionOffset[0] = fIMContext.getInsertPositionOffset();
}}
}, fAwtFocussedComponent);
}, fAwtFocussedComponent, true); // [tav] avoid deadlock with javafx
} catch (InvocationTargetException ite) { ite.printStackTrace(); }
// This bit of gymnastics ensures that the returned location is within the composed text.

View File

@@ -361,11 +361,7 @@ final class CPlatformResponder {
characterToSendWithTypedEvent = stringWithChar == null ? KeyEvent.CHAR_UNDEFINED : stringWithChar.charAt(0);
}
boolean nonInputMethodsModifiersAreNotPressed = (jmodifiers &
(InputEvent.META_DOWN_MASK | InputEvent.CTRL_DOWN_MASK)
) == 0;
if (nonInputMethodsModifiersAreNotPressed) {
if (!nsEvent.isHasDeadKey()) {
eventNotifier.notifyKeyEvent(KeyEvent.KEY_TYPED, when, jmodifiers,
jkeyCode, characterToSendWithTypedEvent,
KeyEvent.KEY_LOCATION_UNKNOWN);

View File

@@ -507,8 +507,7 @@ static BOOL shouldUsePressAndHold() {
JNIEnv *env = [ThreadUtilities getJNIEnv];
TISInputSourceRef sourceRef = TISCopyCurrentKeyboardLayoutInputSource();
CFDataRef keyLayoutPtr = (CFDataRef)TISGetInputSourceProperty(
sourceRef, kTISPropertyUnicodeKeyLayoutData);
CFDataRef keyLayoutPtr = (CFDataRef)TISGetInputSourceProperty(sourceRef, kTISPropertyUnicodeKeyLayoutData);
CFRelease( sourceRef);
const UCKeyboardLayout *keyboardLayout = (UCKeyboardLayout*)CFDataGetBytePtr(keyLayoutPtr);
@@ -525,7 +524,6 @@ static BOOL shouldUsePressAndHold() {
0,
LMGetKbdType(),
0,
// ignore for now
&isDeadKeyPressed,
lengthOfBuffer,
&actualLength,
@@ -547,6 +545,8 @@ static BOOL shouldUsePressAndHold() {
if (status == noErr && isDeadKeyPressed != 0) {
UInt32 isDeadKeyPressedIgnore = 0;
status = UCKeyTranslate(
keyboardLayout,
kVK_Space,
@@ -554,7 +554,7 @@ static BOOL shouldUsePressAndHold() {
0,
LMGetKbdType(),
0,
&isDeadKeyPressed,
&isDeadKeyPressedIgnore,
lengthOfBuffer,
&actualLength,
stringWithChars);

View File

@@ -26,8 +26,6 @@
#import <Cocoa/Cocoa.h>
#import <JavaRuntimeSupport/JavaRuntimeSupport.h>
#import "fontscalerdefs.h"
#define MAX_STACK_ALLOC_GLYPH_BUFFER_SIZE 256
@interface AWTFont : NSObject {
@@ -35,7 +33,6 @@
NSFont *fFont;
CGFontRef fNativeCGFont;
BOOL fIsFakeItalic;
TTLayoutTableCache* layoutTableCache;
}
+ (AWTFont *) awtFontForName:(NSString *)name

View File

@@ -43,33 +43,10 @@
if (self) {
fFont = [font retain];
fNativeCGFont = CTFontCopyGraphicsFont((CTFontRef)font, NULL);
layoutTableCache = NULL;
}
return self;
}
static TTLayoutTableCache* newCFontLayoutTableCache() {
TTLayoutTableCache* ltc = calloc(1, sizeof(TTLayoutTableCache));
if (ltc) {
int i;
for(i=0;i<LAYOUTCACHE_ENTRIES;i++) {
ltc->entries[i].len = -1;
}
}
return ltc;
}
static void freeCFontLayoutTableCache(TTLayoutTableCache* ltc) {
if (ltc) {
int i;
for(i=0;i<LAYOUTCACHE_ENTRIES;i++) {
if(ltc->entries[i].ptr) free (ltc->entries[i].ptr);
}
if (ltc->kernPairs) free(ltc->kernPairs);
free(ltc);
}
}
- (void) dealloc {
[fFont release];
fFont = nil;
@@ -77,10 +54,6 @@ static void freeCFontLayoutTableCache(TTLayoutTableCache* ltc) {
if (fNativeCGFont) {
CGFontRelease(fNativeCGFont);
fNativeCGFont = NULL;
if (layoutTableCache != NULL) {
freeCFontLayoutTableCache(layoutTableCache);
layoutTableCache = NULL;
}
}
[super dealloc];
@@ -91,10 +64,6 @@ static void freeCFontLayoutTableCache(TTLayoutTableCache* ltc) {
CGFontRelease(fNativeCGFont);
fNativeCGFont = NULL;
}
if (layoutTableCache != NULL) {
freeCFontLayoutTableCache(layoutTableCache);
layoutTableCache = NULL;
}
[super finalize];
}
@@ -476,23 +445,6 @@ Java_sun_font_CFont_getCGFontPtrNative
return (jlong)(awtFont->fNativeCGFont);
}
/*
* Class: sun_font_CFont
* Method: getLayoutTableCacheNative
* Signature: (J)J
*/
JNIEXPORT jlong JNICALL
Java_sun_font_CFont_getLayoutTableCacheNative
(JNIEnv *env, jclass clazz,
jlong awtFontPtr)
{
AWTFont *awtFont = (AWTFont *)jlong_to_ptr(awtFontPtr);
if (awtFont->layoutTableCache == NULL) {
awtFont->layoutTableCache = newCFontLayoutTableCache();
}
return (jlong)(awtFont->layoutTableCache);
}
/*
* Class: sun_font_CFont
* Method: getTableBytesNative

View File

@@ -496,18 +496,21 @@ class BufferStrategyPaintManager extends RepaintManager.PaintManager {
}
}
if (isPaint && c == rootJ && x == 0 && y == 0 &&
c.getWidth() == w && c.getHeight() == h) {
if (isPaint) {
// Fallback to normal painting in undecorated non-opaque dialogs
// and frames to resolve black background problem
if ((root instanceof Dialog && ((Dialog)root).isUndecorated() ||
root instanceof Frame && ((Frame)root).isUndecorated()) &&
bsg != null && !c.isOpaque() &&
Window window = SunToolkit.getContainingWindow(c);
if ((window instanceof Dialog && ((Dialog)window).isUndecorated() ||
window instanceof Frame && ((Frame)window).isUndecorated()) &&
bsg != null && !window.isOpaque() &&
((SunGraphics2D)bsg).getSurfaceData().getTransparency() ==
Transparency.OPAQUE) {
return false;
}
}
if (isPaint && c == rootJ && x == 0 && y == 0 &&
c.getWidth() == w && c.getHeight() == h) {
bufferInfo.setInSync(true);
}
else if (contentsLost) {

View File

@@ -25,9 +25,6 @@
package sun.font;
import sun.java2d.Disposer;
import sun.java2d.DisposerRecord;
import java.awt.Font;
import java.awt.font.FontRenderContext;
import java.awt.geom.AffineTransform;
@@ -80,8 +77,6 @@ public abstract class Font2D {
protected FontFamily family;
protected int fontRank = DEFAULT_RANK;
private HarfbuzzFaceRef harfbuzzFaceRef;
/*
* A mapper can be independent of the strike.
* Perhaps the reference to the mapper ought to be held on the
@@ -472,36 +467,12 @@ public abstract class Font2D {
return null;
}
/* implemented for fonts backed by an sfnt that has
* OpenType or AAT layout tables.
*/
protected long getLayoutTableCache() {
return 0L;
}
/* Used only on OS X.
*/
protected long getPlatformNativeFontPtr() {
return 0L;
}
protected boolean isAAT() {
return false;
}
synchronized long getHarfbuzzFacePtr() {
if (harfbuzzFaceRef == null) {
long harfbuzzFaceNativePtr = createHarfbuzzFace(isAAT(), getPlatformNativeFontPtr());
if (harfbuzzFaceNativePtr == 0) return 0;
harfbuzzFaceRef = new HarfbuzzFaceRef(harfbuzzFaceNativePtr);
Disposer.addObjectRecord(this, harfbuzzFaceRef);
}
return harfbuzzFaceRef.harfbuzzFaceNativePtr;
}
private native long createHarfbuzzFace(boolean aat, long platformNativeFontPtr);
private static native void disposeHarfbuzzFace(long harfbuzzFaceNativePtr);
/* for layout code */
protected long getUnitsPerEm() {
return 2048;
@@ -587,17 +558,4 @@ public abstract class Font2D {
}
}
private static class HarfbuzzFaceRef implements DisposerRecord {
private final long harfbuzzFaceNativePtr;
private HarfbuzzFaceRef(long harfbuzzFaceNativePtr) {
this.harfbuzzFaceNativePtr = harfbuzzFaceNativePtr;
}
@Override
public void dispose() {
disposeHarfbuzzFace(harfbuzzFaceNativePtr);
}
}
}

View File

@@ -181,25 +181,6 @@ public abstract class FontScaler implements DisposerRecord {
abstract int getMissingGlyphCode() throws FontScalerException;
abstract int getGlyphCode(char charCode) throws FontScalerException;
/* This method returns table cache used by native layout engine.
* This cache is essentially just small collection of
* pointers to various truetype tables. See definition of TTLayoutTableCache
* in the fontscalerdefs.h for more details.
*
* Note that tables themselves have same format as defined in the truetype
* specification, i.e. font scaler do not need to perform any preprocessing.
*
* Probably it is better to have API to request pointers to each table
* separately instead of requesting pointer to some native structure.
* (then there is not need to share its definition by different
* implementations of scaler).
* However, this means multiple JNI calls and potential impact on performance.
*
* Note: return value 0 is legal.
* This means tables are not available (e.g. type1 font).
*/
abstract long getLayoutTableCache() throws FontScalerException;
/* Used by the OpenType engine for mark positioning. */
abstract Point2D.Float getGlyphPoint(long pScalerContext,
int glyphCode, int ptNumber)

View File

@@ -178,10 +178,6 @@ class FreetypeFontScaler extends FontScaler {
.getNullScaler().getGlyphVectorOutline(0L, glyphs, numGlyphs, x, y);
}
synchronized long getLayoutTableCache() throws FontScalerException {
return getLayoutTableCacheNative(nativeScaler);
}
public synchronized void dispose() {
if (nativeScaler != 0L) {
disposeNativeScaler(font.get(), nativeScaler);
@@ -258,8 +254,6 @@ class FreetypeFontScaler extends FontScaler {
native Point2D.Float getGlyphPointNative(Font2D font,
long pScalerContext, long pScaler, int glyphCode, int ptNumber);
private native long getLayoutTableCacheNative(long pScaler);
private native void disposeNativeScaler(Font2D font2D, long pScaler);
private native int getGlyphCodeNative(Font2D font, long pScaler, char charCode);

View File

@@ -64,8 +64,6 @@ class NullFontScaler extends FontScaler {
return new GeneralPath();
}
long getLayoutTableCache() {return 0L;}
long createScalerContext(double[] matrix, int aa,
int fm, float boldness, float italic, boolean disableHinting) {
return getNullScalerContext();

View File

@@ -79,12 +79,6 @@ public abstract class PhysicalFont extends Font2D {
return new Point2D.Float();
}
@Override
protected boolean isAAT() {
return getTableBytes(TrueTypeFont.morxTag) != null ||
getTableBytes(TrueTypeFont.mortTag) != null;
}
/* These 3 metrics methods should be implemented to return
* values in user space.
*/

View File

@@ -31,10 +31,13 @@
package sun.font;
import sun.font.GlyphLayout.*;
import sun.java2d.Disposer;
import sun.java2d.DisposerRecord;
import java.awt.geom.Point2D;
import java.lang.ref.SoftReference;
import java.util.concurrent.ConcurrentHashMap;
import java.util.Locale;
import java.util.WeakHashMap;
/*
* different ways to do this
@@ -143,28 +146,80 @@ public final class SunLayoutEngine implements LayoutEngine, LayoutEngineFactory
}
private SoftReference<ConcurrentHashMap<LayoutEngineKey, LayoutEngine>> cacheref =
new SoftReference<>(null);
private static final WeakHashMap<Font2D, FaceRef> facePtr =
new WeakHashMap<>();
private static boolean isAAT(Font2D font) {
// CoreText layout code ignores fractional metrics font attribute
// also, using CoreText layout in Harfbuzz code leads to wrong advances for emoji glyphs
return false;
}
private SunLayoutEngine(LayoutEngineKey key) {
this.key = key;
}
private long getFacePtr(Font2D font2D) {
FaceRef ref;
synchronized (facePtr) {
ref = facePtr.computeIfAbsent(font2D, FaceRef::new);
}
return ref.getNativePtr();
}
public void layout(FontStrikeDesc desc, float[] mat, float ptSize, int gmask,
int baseIndex, TextRecord tr, int typo_flags,
Point2D.Float pt, GVData data) {
Font2D font = key.font();
FontStrike strike = font.getStrike(desc);
shape(font, strike, ptSize, mat,
font.getHarfbuzzFacePtr(), font.getPlatformNativeFontPtr(), font.isAAT(),
tr.text, data, key.script(),
tr.start, tr.limit, baseIndex, pt,
typo_flags, gmask);
long pNativeFont = font.getPlatformNativeFontPtr(); // used on OSX
long pFace = getFacePtr(font);
if (pFace != 0) {
shape(font, strike, ptSize, mat, pNativeFont,
pFace, isAAT(font),
tr.text, data, key.script(),
tr.start, tr.limit, baseIndex, pt,
typo_flags, gmask);
}
}
/* Native method to invoke harfbuzz layout engine */
private static native boolean
shape(Font2D font, FontStrike strike, float ptSize, float[] mat,
long pscaler, long pNativeFont, boolean aat,
long pNativeFont, long pFace, boolean aat,
char[] chars, GVData data,
int script, int offset, int limit,
int baseIndex, Point2D.Float pt, int typo_flags, int slot);
private static native long createFace(Font2D font,
boolean aat,
long platformNativeFontPtr);
private static native void disposeFace(long facePtr);
private static class FaceRef implements DisposerRecord {
private Font2D font;
private Long facePtr;
private FaceRef(Font2D font) {
this.font = font;
}
private synchronized long getNativePtr() {
if (facePtr == null) {
facePtr = createFace(font, isAAT(font),
font.getPlatformNativeFontPtr());
if (facePtr != 0) {
Disposer.addObjectRecord(font, this);
}
font = null;
}
return facePtr;
}
@Override
public void dispose() {
disposeFace(facePtr);
}
}
}

View File

@@ -898,15 +898,6 @@ public class TrueTypeFont extends FileFont {
}
}
@Override
protected long getLayoutTableCache() {
try {
return getScaler().getLayoutTableCache();
} catch(FontScalerException fe) {
return 0L;
}
}
@Override
protected byte[] getTableBytes(int tag) {
ByteBuffer buffer = getTableBuffer(tag);
@@ -999,12 +990,6 @@ public class TrueTypeFont extends FileFont {
return (fontWeight > 0) ? fontWeight : super.getWeight();
}
@Override
protected boolean isAAT() {
return getDirectoryEntry(TrueTypeFont.morxTag) != null ||
getDirectoryEntry(TrueTypeFont.mortTag) != null;
}
/* TrueTypeFont can use the fsSelection fields of OS/2 table
* to determine the style. In the unlikely case that doesn't exist,
* can use macStyle in the 'head' table but simpler to

View File

@@ -88,32 +88,8 @@ typedef struct GlyphInfo {
*/
#define INVISIBLE_GLYPHS 0xfffe
#define GSUB_TAG 0x47535542 /* 'GSUB' */
#define GPOS_TAG 0x47504F53 /* 'GPOS' */
#define GDEF_TAG 0x47444546 /* 'GDEF' */
#define HEAD_TAG 0x68656164 /* 'head' */
#define MORT_TAG 0x6D6F7274 /* 'mort' */
#define MORX_TAG 0x6D6F7278 /* 'morx' */
#define KERN_TAG 0x6B65726E /* 'kern' */
typedef struct TTLayoutTableCacheEntry {
const void* ptr;
int len;
int tag;
} TTLayoutTableCacheEntry;
#define LAYOUTCACHE_ENTRIES 7
typedef struct TTLayoutTableCache {
TTLayoutTableCacheEntry entries[LAYOUTCACHE_ENTRIES];
void* kernPairs;
} TTLayoutTableCache;
#include "sunfontids.h"
JNIEXPORT extern TTLayoutTableCache* newLayoutTableCache();
JNIEXPORT extern void freeLayoutTableCache(TTLayoutTableCache* ltc);
/* If font is malformed then scaler context created by particular scaler
* will be replaced by null scaler context.
* Note that this context is not compatible with structure of the context

View File

@@ -131,6 +131,7 @@ typedef void (GLAPIENTRY *glViewportType)(GLint x, GLint y, GLsizei width, GLsiz
* extensions, which is why they are called out separately here)
*/
typedef void (GLAPIENTRY *glActiveTextureARBType)(GLenum texture);
typedef void (GLAPIENTRY *glClientActiveTextureType)(GLenum texture);
typedef void (GLAPIENTRY *glMultiTexCoord2fARBType)(GLenum texture, GLfloat s, GLfloat t);
typedef void (GLAPIENTRY *glTexImage3DType)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const GLvoid *pixels);
@@ -290,6 +291,7 @@ typedef void (GLAPIENTRY *glTextureBarrierNVType) (void);
#define OGL_EXPRESS_EXT_FUNCS(action) \
OGL_##action##_EXT_FUNC(glActiveTextureARB); \
OGL_##action##_EXT_FUNC(glClientActiveTexture); \
OGL_##action##_EXT_FUNC(glMultiTexCoord2fARB); \
OGL_##action##_EXT_FUNC(glTexImage3D); \
OGL_##action##_EXT_FUNC(glBindRenderbufferEXT); \

View File

@@ -38,6 +38,7 @@
#include "OGLTextRenderer.h"
#include "OGLVertexCache.h"
#include "AccelGlyphCache.h"
#include "jni_util.h"
/**
* The following constants define the inner and outer bounds of the
@@ -712,21 +713,14 @@ OGLTR_UpdateCachedDestination(OGLSDOps *dstOps, GlyphInfo *ginfo,
}
static jboolean
OGLTR_DrawLCDGlyphViaCache(OGLContext *oglc, OGLSDOps *dstOps,
GlyphInfo *ginfo, jint x, jint y,
jint glyphIndex, jint totalGlyphs,
jboolean rgbOrder, jint contrast,
GLuint dstTextureID, jboolean * opened)
OGLTR_DrawLCDGlyphViaCache(OGLContext *oglc, OGLSDOps *dstOps, GlyphInfo *ginfo, jint x, jint y, jint glyphIndex,
jint totalGlyphs, jboolean rgbOrder, jint contrast, GLuint dstTextureID)
{
CacheCellInfo *cell;
jint dx1, dy1, dx2, dy2;
jfloat dtx1, dty1, dtx2, dty2;
if (glyphMode != MODE_USE_CACHE_LCD) {
if (*opened) {
*opened = JNI_FALSE;
j2d_glEnd();
}
OGLTR_DisableGlyphModeState();
CHECK_PREVIOUS_OP(GL_TEXTURE_2D);
j2d_glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
@@ -758,10 +752,6 @@ OGLTR_DrawLCDGlyphViaCache(OGLContext *oglc, OGLSDOps *dstOps,
}
if (ginfo->cellInfo == NULL) {
if (*opened) {
*opened = JNI_FALSE;
j2d_glEnd();
}
// rowBytes will always be a multiple of 3, so the following is safe
j2d_glPixelStorei(GL_UNPACK_ROW_LENGTH, ginfo->rowBytes / 3);
@@ -787,10 +777,6 @@ OGLTR_DrawLCDGlyphViaCache(OGLContext *oglc, OGLSDOps *dstOps,
dy2 = dy1 + ginfo->height;
if (dstTextureID == 0) {
if (*opened) {
*opened = JNI_FALSE;
j2d_glEnd();
}
// copy destination into second cached texture, if necessary
OGLTR_UpdateCachedDestination(dstOps, ginfo,
dx1, dy1, dx2, dy2,
@@ -818,23 +804,12 @@ OGLTR_DrawLCDGlyphViaCache(OGLContext *oglc, OGLSDOps *dstOps,
}
// render composed texture to the destination surface
if (!*opened) {
j2d_glBegin(GL_QUADS);
*opened = JNI_TRUE;
if (!OGLMTVertexCache_enable(oglc, dstTextureID != 0)) {
J2dTracePrimitive("OGLMTVertexCache_enable_failed");
return JNI_FALSE;
}
j2d_glMultiTexCoord2fARB(GL_TEXTURE0_ARB, cell->tx1, cell->ty1);
j2d_glMultiTexCoord2fARB(GL_TEXTURE1_ARB, dtx1, dty1);
j2d_glVertex2i(dx1, dy1);
j2d_glMultiTexCoord2fARB(GL_TEXTURE0_ARB, cell->tx2, cell->ty1);
j2d_glMultiTexCoord2fARB(GL_TEXTURE1_ARB, dtx2, dty1);
j2d_glVertex2i(dx2, dy1);
j2d_glMultiTexCoord2fARB(GL_TEXTURE0_ARB, cell->tx2, cell->ty2);
j2d_glMultiTexCoord2fARB(GL_TEXTURE1_ARB, dtx2, dty2);
j2d_glVertex2i(dx2, dy2);
j2d_glMultiTexCoord2fARB(GL_TEXTURE0_ARB, cell->tx1, cell->ty2);
j2d_glMultiTexCoord2fARB(GL_TEXTURE1_ARB, dtx1, dty2);
j2d_glVertex2i(dx1, dy2);
OGLMTVertexCache_addGlyphQuad(dx1, dy1, dx2, dy2, cell->tx1, cell->ty1,
cell->tx2, cell->ty2, dtx1, dty1, dtx2, dty2);
return JNI_TRUE;
}
@@ -1066,11 +1041,13 @@ OGLTR_DrawGlyphList(JNIEnv *env, OGLContext *oglc, OGLSDOps *dstOps,
{
int glyphCounter;
GLuint dstTextureID = 0;
jboolean hasLCDGlyphs = JNI_FALSE;
jboolean lcdOpened = JNI_FALSE;
jint ox1 = INT_MIN;
jlong time;
J2dTraceLn(J2D_TRACE_INFO, "OGLTR_DrawGlyphList");
if (graphicsPrimitive_traceflags & J2D_PTRACE_TIME) {
J2dTracePrimitive("OGLTR_DrawGlyphList");
time = J2dTraceNanoTime();
}
RETURN_IF_NULL(oglc);
RETURN_IF_NULL(dstOps);
@@ -1138,10 +1115,7 @@ OGLTR_DrawGlyphList(JNIEnv *env, OGLContext *oglc, OGLSDOps *dstOps,
}
if (ginfo->rowBytes == ginfo->width) {
if (lcdOpened) {
lcdOpened = JNI_FALSE;
j2d_glEnd();
}
OGLMTVertexCache_disable();
// grayscale or monochrome glyph data
if (ginfo->width <= OGLTR_CACHE_CELL_WIDTH &&
ginfo->height <= OGLTR_CACHE_CELL_HEIGHT)
@@ -1151,18 +1125,12 @@ OGLTR_DrawGlyphList(JNIEnv *env, OGLContext *oglc, OGLSDOps *dstOps,
ok = OGLTR_DrawGrayscaleGlyphNoCache(oglc, ginfo, x, y);
}
} else if (ginfo->rowBytes == ginfo->width * 4) {
OGLMTVertexCache_disable();
// color glyph data
ok = OGLTR_DrawColorGlyphNoCache(oglc, ginfo, x, y);
} else {
// LCD-optimized glyph data
jint rowBytesOffset = 0;
if (!hasLCDGlyphs) {
// Flush GPU buffers before processing first LCD glyph
hasLCDGlyphs = JNI_TRUE;
if (dstTextureID != 0) {
j2d_glTextureBarrierNV();
}
}
if (subPixPos) {
jint frac = (jint)((glyphx - x) * 3);
@@ -1172,29 +1140,16 @@ OGLTR_DrawGlyphList(JNIEnv *env, OGLContext *oglc, OGLSDOps *dstOps,
}
}
// Flush GPU buffers before processing overlapping LCD glyphs on OSX
if (dstTextureID != 0 && ox1 > x) {
if (lcdOpened) {
lcdOpened = JNI_FALSE;
j2d_glEnd();
}
j2d_glTextureBarrierNV();
}
if (rowBytesOffset == 0 &&
ginfo->width <= OGLTR_CACHE_CELL_WIDTH &&
ginfo->height <= OGLTR_CACHE_CELL_HEIGHT)
{
ok = OGLTR_DrawLCDGlyphViaCache(oglc, dstOps,
ginfo, x, y,
ok = OGLTR_DrawLCDGlyphViaCache(oglc, dstOps, ginfo, x, y,
glyphCounter, totalGlyphs,
rgbOrder, lcdContrast,
dstTextureID, &lcdOpened);
dstTextureID);
} else {
if (lcdOpened) {
lcdOpened = JNI_FALSE;
j2d_glEnd();
}
OGLMTVertexCache_disable();
ok = OGLTR_DrawLCDGlyphNoCache(oglc, dstOps,
ginfo, x, y,
rowBytesOffset,
@@ -1202,15 +1157,12 @@ OGLTR_DrawGlyphList(JNIEnv *env, OGLContext *oglc, OGLSDOps *dstOps,
dstTextureID);
}
}
ox1 = x + ginfo->width;
if (!ok) {
break;
}
}
if (lcdOpened) {
j2d_glEnd();
}
OGLMTVertexCache_disable();
J2dTracePrimitiveTime("OGLTR_DrawGlyphList", time);
}
JNIEXPORT void JNICALL

View File

@@ -27,9 +27,11 @@
#include <stdlib.h>
#include <string.h>
#include <float.h>
#include "sun_java2d_SunGraphics2D.h"
#include "jni_util.h"
#include "OGLPaints.h"
#include "OGLVertexCache.h"
@@ -39,11 +41,28 @@ typedef struct _J2DVertex {
jfloat dx, dy;
} J2DVertex;
// Multitexture vertex
typedef struct _J2DMTVertex {
jfloat dx, dy;
jfloat tx0, ty0;
jfloat tx1, ty1;
} J2DMTVertex;
static J2DVertex *vertexCache = NULL;
static jint vertexCacheIndex = 0;
static J2DMTVertex *mtVertexCache = NULL;
static jboolean mtVertexCacheEnabled = JNI_FALSE;
static jboolean mtUseTxtBarrier = JNI_FALSE;
static jint evenLCDGlyphInd = 0;
static jint oddLCDGlyphInd = ODD_LCD_GLYPHS_OFFSET;
static jint lcdGlyphInd = 0;
static jfloat evenOx2 = FLT_MIN;
static jfloat oddOx2 = FLT_MIN;
static GLuint maskCacheTexID = 0;
static jint maskCacheIndex = 0;
static void OGLMTVertexCache_flush(jint mask);
#define OGLVC_ADD_VERTEX(TX, TY, R, G, B, A, DX, DY) \
do { \
@@ -66,6 +85,25 @@ static jint maskCacheIndex = 0;
OGLVC_ADD_VERTEX(TX1, TY2, R, G, B, A, DX1, DY2); \
} while (0)
#define OGLMTVC_ADD_VERTEX(IND, DX, DY, TX0, TY0, TX1, TY1) \
do { \
J2DMTVertex *v = &mtVertexCache[IND++]; \
v->dx = DX; \
v->dy = DY; \
v->tx0 = TX0; \
v->ty0 = TY0; \
v->tx1 = TX1; \
v->ty1 = TY1; \
} while (0)
#define OGLMTVC_ADD_QUAD(IND, DX1, DY1, DX2, DY2, TX1, TY1, TX2, TY2, DTX1, DTY1, DTX2, DTY2) \
do { \
OGLMTVC_ADD_VERTEX((IND), DX1, DY1, TX1, TY1, DTX1, DTY1); \
OGLMTVC_ADD_VERTEX((IND), DX2, DY1, TX2, TY1, DTX2, DTY1); \
OGLMTVC_ADD_VERTEX((IND), DX2, DY2, TX2, TY2, DTX2, DTY2); \
OGLMTVC_ADD_VERTEX((IND), DX1, DY2, TX1, TY2, DTX1, DTY2); \
} while (0)
jboolean
OGLVertexCache_InitVertexCache(OGLContext *oglc)
{
@@ -287,4 +325,97 @@ OGLVertexCache_AddGlyphQuad(OGLContext *oglc,
oglc->r, oglc->g, oglc->b, oglc->a);
}
jboolean OGLMTVertexCache_enable(OGLContext *oglc, jboolean useTxtBarrier) {
mtUseTxtBarrier = useTxtBarrier;
if (mtVertexCache == NULL) {
mtVertexCache = (J2DMTVertex *)malloc(OGLMTVC_MAX_INDEX * sizeof(J2DMTVertex));
if (mtVertexCache == NULL) {
return JNI_FALSE;
}
}
if (!mtVertexCacheEnabled) {
oglc->vertexCacheEnabled = JNI_FALSE;
j2d_glVertexPointer(2, GL_FLOAT, sizeof(J2DMTVertex), &mtVertexCache[0].dx);
j2d_glEnableClientState(GL_VERTEX_ARRAY);
j2d_glClientActiveTexture(GL_TEXTURE1_ARB);
j2d_glTexCoordPointer(2, GL_FLOAT, sizeof(J2DMTVertex), &mtVertexCache[0].tx1);
j2d_glEnableClientState(GL_TEXTURE_COORD_ARRAY);
j2d_glClientActiveTexture(GL_TEXTURE0_ARB);
j2d_glTexCoordPointer(2, GL_FLOAT, sizeof(J2DMTVertex), &mtVertexCache[0].tx0);
j2d_glEnableClientState(GL_TEXTURE_COORD_ARRAY);
mtVertexCacheEnabled = JNI_TRUE;
evenLCDGlyphInd = 0;
oddLCDGlyphInd = ODD_LCD_GLYPHS_OFFSET;
lcdGlyphInd = 0;
}
return JNI_TRUE;
}
void OGLMTVertexCache_disable() {
if (mtVertexCacheEnabled) {
OGLMTVertexCache_flush(OGLMTVC_FLUSH_ALL);
mtVertexCacheEnabled = JNI_FALSE;
}
}
void OGLMTVertexCache_flush(jint mask) {
if (mtVertexCacheEnabled) {
if ((mask & OGLMTVC_FLUSH_EVEN) && evenLCDGlyphInd > 0) {
if (mtUseTxtBarrier) {
// TextureBarrierNV() will guarantee that writes have completed
// and caches have been invalidated before subsequent Draws are
// executed
j2d_glTextureBarrierNV();
evenOx2 = FLT_MIN;
}
j2d_glDrawArrays(GL_QUADS, 0, evenLCDGlyphInd);
evenLCDGlyphInd = 0;
}
if ((mask & OGLMTVC_FLUSH_ODD) && oddLCDGlyphInd > ODD_LCD_GLYPHS_OFFSET) {
if (mtUseTxtBarrier) {
// See the comment above
j2d_glTextureBarrierNV();
oddOx2 = FLT_MIN;
}
j2d_glDrawArrays(GL_QUADS, ODD_LCD_GLYPHS_OFFSET,
oddLCDGlyphInd - ODD_LCD_GLYPHS_OFFSET);
oddLCDGlyphInd = ODD_LCD_GLYPHS_OFFSET;
}
}
}
void OGLMTVertexCache_addGlyphQuad(jfloat dx1, jfloat dy1,
jfloat dx2, jfloat dy2,
jfloat tx1, jfloat ty1,
jfloat tx2, jfloat ty2,
jfloat dtx1, jfloat dty1,
jfloat dtx2, jfloat dty2)
{
jint* ind;
if (lcdGlyphInd & 0x1) {
if (oddLCDGlyphInd >= OGLMTVC_MAX_INDEX ||
(mtUseTxtBarrier && oddOx2 >= dx1))
{
OGLMTVertexCache_flush(OGLMTVC_FLUSH_ODD);
} else if (mtUseTxtBarrier) {
oddOx2 = dx2;
}
ind = &oddLCDGlyphInd;
} else {
if (evenLCDGlyphInd >= ODD_LCD_GLYPHS_OFFSET ||
(mtUseTxtBarrier && evenOx2 >= dx1))
{
OGLMTVertexCache_flush(OGLMTVC_FLUSH_EVEN);
} else if (mtUseTxtBarrier) {
evenOx2 = dx2;
}
ind = &evenLCDGlyphInd;
}
lcdGlyphInd++;
OGLMTVC_ADD_QUAD(*ind, dx1, dy1, dx2, dy2, tx1, ty1, tx2, ty2, dtx1, dty1, dtx2, dty2);
}
#endif /* !HEADLESS */

View File

@@ -30,9 +30,14 @@
#include "OGLContext.h"
/**
* Constants that control the size of the vertex cache.
* Constants that control the size of the vertex caches.
*/
#define OGLVC_MAX_INDEX 1024
#define OGLMTVC_MAX_INDEX 2048
#define ODD_LCD_GLYPHS_OFFSET (OGLMTVC_MAX_INDEX >> 1)
#define OGLMTVC_FLUSH_EVEN 1
#define OGLMTVC_FLUSH_ODD 2
#define OGLMTVC_FLUSH_ALL 3
/**
* Constants that control the size of the texture tile cache used for
@@ -83,4 +88,14 @@ void OGLVertexCache_AddGlyphQuad(OGLContext *oglc,
jfloat dx1, jfloat dy1,
jfloat dx2, jfloat dy2);
jboolean OGLMTVertexCache_enable(OGLContext *oglc, jboolean useTxtBarrier);
void OGLMTVertexCache_addGlyphQuad(jfloat dx1, jfloat dy1,
jfloat dx2, jfloat dy2,
jfloat tx1, jfloat ty1,
jfloat tx2, jfloat ty2,
jfloat dtx1, jfloat dty1,
jfloat dtx2, jfloat dty2);
void OGLMTVertexCache_disable();
#endif /* OGLVertexCache_h_Included */

View File

@@ -29,6 +29,9 @@
#include <jni.h>
#include "debug_trace.h"
#include "jni_util.h"
#ifdef __MACH__
#include <mach/mach_time.h>
#endif
#ifdef __cplusplus
extern "C" {
@@ -53,6 +56,8 @@ JNIEXPORT extern jint graphicsPrimitive_traceflags;
#define J2D_TRACE_VERBOSE2 5
#define J2D_TRACE_MAX (J2D_TRACE_VERBOSE2+1)
#define J2D_PTRACE_TIME 8
JNIEXPORT void JNICALL
J2dTraceImpl(int level, jboolean cr, const char *string, ...);
JNIEXPORT void JNICALL
@@ -182,13 +187,41 @@ J2dTraceInit();
if (graphicsPrimitive_traceflags && jvm) { \
void *env; \
jstring jstr; \
(*jvm)->AttachCurrentThreadAsDaemon(jvm, &env, NULL); \
(*jvm)->AttachCurrentThreadAsDaemon(jvm, (void**)&env, NULL); \
jstr = (*(JNIEnv*)env)->NewStringUTF(env, string); \
JNU_CallStaticMethodByName(env, NULL, "sun/java2d/loops/GraphicsPrimitive", \
"tracePrimitive", "(Ljava/lang/Object;)V", jstr); \
JNU_CallStaticMethodByName( \
env, NULL, "sun/java2d/loops/GraphicsPrimitive", \
"tracePrimitive", "(Ljava/lang/Object;)V", jstr); \
(*(JNIEnv*)env)->DeleteLocalRef(env, jstr); \
} \
}
#ifdef __MACH__
#define J2dTraceNanoTime() (mach_absolute_time())
#define J2dTracePrimitiveTime(string,t0) { \
if ((graphicsPrimitive_traceflags & J2D_PTRACE_TIME) && jvm) { \
JNIEnv *env; \
jstring jstr; \
static mach_timebase_info_data_t ti; \
jlong t1; \
t1 = mach_absolute_time(); \
if (ti.denom == 0) { \
(void) mach_timebase_info(&ti); \
} \
(*jvm)->AttachCurrentThreadAsDaemon(jvm, &env, NULL); \
jstr = (*env)->NewStringUTF(env, string); \
JNU_CallStaticMethodByName(env, NULL, "sun/java2d/loops/GraphicsPrimitive", \
"tracePrimitiveTime", "(Ljava/lang/Object;J)V", jstr,\
(((t1-t0)*ti.numer)/ti.denom)); \
(*env)->DeleteLocalRef(env, jstr); \
} \
}
#else
#define J2dTracePrimitiveTime(string,t)
#define J2dTraceNanoTime() (0)
#endif
#ifdef __cplusplus
};
#endif /* __cplusplus */

View File

@@ -238,8 +238,8 @@ JNIEXPORT jboolean JNICALL Java_sun_font_SunLayoutEngine_shape
jobject fontStrike,
jfloat ptSize,
jfloatArray matrix,
jlong pFace,
jlong pNativeFont,
jlong pFace,
jboolean aat,
jcharArray text,
jobject gvdata,

View File

@@ -111,7 +111,6 @@ typedef struct {
unsigned fontDataOffset;
unsigned fontDataLength;
unsigned fileSize;
TTLayoutTableCache* layoutTables;
} FTScalerInfo;
typedef struct FTScalerContext {
@@ -494,7 +493,6 @@ Java_sun_font_FreetypeFontScaler_initNativeScaler(
if (type == TYPE1_FROM_JAVA) { /* TYPE1 */
scalerInfo->fontData = (unsigned char*) malloc(filesize);
scalerInfo->directBuffer = NULL;
scalerInfo->layoutTables = NULL;
scalerInfo->fontDataLength = filesize;
if (scalerInfo->fontData != NULL) {
@@ -1343,32 +1341,6 @@ Java_sun_font_FreetypeFontScaler_getGlyphImageNative(
return ptr_to_jlong(glyphInfo);
}
/*
* Class: sun_font_FreetypeFontScaler
* Method: getLayoutTableCacheNative
* Signature: (J)J
*/
JNIEXPORT jlong JNICALL
Java_sun_font_FreetypeFontScaler_getLayoutTableCacheNative(
JNIEnv *env, jobject scaler, jlong pScaler) {
FTScalerInfo *scalerInfo = (FTScalerInfo*) jlong_to_ptr(pScaler);
if (scalerInfo == NULL) {
invalidateJavaScaler(env, scaler, scalerInfo);
return 0L;
}
// init layout table cache in font
// we're assuming the font is a file font and moreover it is Truetype font
// otherwise we shouldn't be able to get here...
if (scalerInfo->layoutTables == NULL) {
scalerInfo->layoutTables = newLayoutTableCache();
}
return ptr_to_jlong(scalerInfo->layoutTables);
}
/*
* Class: sun_font_FreetypeFontScaler
* Method: disposeNativeScaler

View File

@@ -24,7 +24,8 @@
*/
#include "jlong.h"
#include "sun_font_Font2D.h"
#include "sun_font_SunLayoutEngine.h"
#include "hb.h"
#include "hb-jdk.h"
#ifdef MACOSX
@@ -283,6 +284,9 @@ _hb_jdk_get_font_funcs (void)
static void _do_nothing(void) {
}
static void _free_nothing(void*) {
}
struct Font2DPtr {
JavaVM* vmPtr;
jweak font2DRef;
@@ -295,7 +299,7 @@ static void cleanupFontInfo(void* data) {
fontInfo = (Font2DPtr*) data;
fontInfo->vmPtr->GetEnv((void**)&env, JNI_VERSION_1_1);
env->DeleteWeakGlobalRef(fontInfo->font2DRef);
free((void*)data);
free(data);
}
static hb_blob_t *
@@ -305,7 +309,13 @@ reference_table(hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data) {
JNIEnv* env;
jobject font2D;
jsize length;
jbyte* buffer;
void* buffer;
// HB_TAG_NONE is 0 and is used to get the whole font file.
// It is not expected to be needed for JDK.
if (tag == 0) {
return NULL;
}
fontInfo = (Font2DPtr*)user_data;
fontInfo->vmPtr->GetEnv((void**)&env, JNI_VERSION_1_1);
@@ -314,31 +324,32 @@ reference_table(hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data) {
}
font2D = fontInfo->font2DRef;
// HB_TAG_NONE is 0 and is used to get the whole font file.
// It is not expected not be needed for JDK.
if (tag == 0) {
return NULL;
}
jbyteArray tableBytes = (jbyteArray)
env->CallObjectMethod(font2D, sunFontIDs.getTableBytesMID, tag);
if (tableBytes == NULL) {
return NULL;
}
length = env->GetArrayLength(tableBytes);
buffer = (jbyte *)calloc(length, sizeof(jbyte));
env->GetByteArrayRegion(tableBytes, 0, length, buffer);
buffer = calloc(length, sizeof(jbyte));
env->GetByteArrayRegion(tableBytes, 0, length, (jbyte*)buffer);
return hb_blob_create((const char *)buffer, length,
HB_MEMORY_MODE_WRITABLE,
buffer, free);
}
extern "C" {
/*
* Class: sun_font_Font2D
* Method: createHarfbuzzFace
* Signature: (ZJ)J
* Class: sun_font_SunLayoutEngine
* Method: createFace
* Signature: (Lsun/font/Font2D;ZJJ)J
*/
JNIEXPORT jlong JNICALL Java_sun_font_Font2D_createHarfbuzzFace(JNIEnv *env, jobject font2D, jboolean aat, jlong platformFontPtr) {
JNIEXPORT jlong JNICALL Java_sun_font_SunLayoutEngine_createFace(JNIEnv *env,
jclass cls,
jobject font2D,
jboolean aat,
jlong platformFontPtr) {
#ifdef MACOSX
if (aat && platformFontPtr) {
hb_face_t *face = hb_coretext_face_create((CGFontRef)platformFontPtr);
@@ -353,20 +364,29 @@ JNIEXPORT jlong JNICALL Java_sun_font_Font2D_createHarfbuzzFace(JNIEnv *env, job
env->GetJavaVM(&vmPtr);
fi->vmPtr = vmPtr;
fi->font2DRef = env->NewWeakGlobalRef(font2D);
hb_face_t *face = hb_face_create_for_tables(reference_table, fi, cleanupFontInfo);
if (!fi->font2DRef) {
free(fi);
return 0;
}
hb_face_t *face = hb_face_create_for_tables(reference_table, fi,
cleanupFontInfo);
return ptr_to_jlong(face);
}
/*
* Class: sun_font_Font2D
* Method: disposeHarfbuzzFace
* Class: sun_font_SunLayoutEngine
* Method: disposeFace
* Signature: (J)V
*/
JNIEXPORT void JNICALL Java_sun_font_Font2D_disposeHarfbuzzFace(JNIEnv *env, jclass cls, jlong ptr) {
JNIEXPORT void JNICALL Java_sun_font_SunLayoutEngine_disposeFace(JNIEnv *env,
jclass cls,
jlong ptr) {
hb_face_t* face = (hb_face_t*) jlong_to_ptr(ptr);
hb_face_destroy(face);
}
} // extern "C"
static hb_font_t* _hb_jdk_font_create(hb_face_t* face,
JDKFontInfo *jdkFontInfo,
hb_destroy_func_t destroy) {
@@ -384,7 +404,8 @@ static hb_font_t* _hb_jdk_font_create(hb_face_t* face,
}
#ifdef MACOSX
static hb_font_t* _hb_jdk_ct_font_create(hb_face_t* face, JDKFontInfo *jdkFontInfo) {
static hb_font_t* _hb_jdk_ct_font_create(hb_face_t* face,
JDKFontInfo *jdkFontInfo) {
hb_font_t *font = NULL;
font = hb_font_create(face);
@@ -395,19 +416,13 @@ static hb_font_t* _hb_jdk_ct_font_create(hb_face_t* face, JDKFontInfo *jdkFontIn
}
#endif
hb_font_t* hb_jdk_font_create(hb_face_t* hbface,
JDKFontInfo *jdkFontInfo,
hb_font_t* hb_jdk_font_create(hb_face_t* hbFace,
JDKFontInfo *jdkFontInfo,
hb_destroy_func_t destroy) {
hb_font_t* font = NULL;
#ifdef MACOSX
if (jdkFontInfo->aat && jdkFontInfo->nativeFont) {
font = _hb_jdk_ct_font_create(hbface, jdkFontInfo);
return _hb_jdk_ct_font_create(hbFace, jdkFontInfo);
}
#endif
if (font == NULL) {
font = _hb_jdk_font_create(hbface, jdkFontInfo, destroy);
}
return font;
return _hb_jdk_font_create(hbFace, jdkFontInfo, destroy);
}

View File

@@ -63,7 +63,7 @@ hb_face_t *
hb_jdk_face_create(JDKFontInfo* jdkFontInfo,
hb_destroy_func_t destroy);
hb_font_t *
hb_jdk_font_create(hb_face_t* hbface,
hb_jdk_font_create(hb_face_t* hbFace,
JDKFontInfo* jdkFontInfo,
hb_destroy_func_t destroy);

View File

@@ -344,32 +344,3 @@ Java_sun_font_StrikeCache_getGlyphCacheDescription
(*env)->ReleasePrimitiveArrayCritical(env, results, nresults, 0);
}
JNIEXPORT TTLayoutTableCache* newLayoutTableCache() {
TTLayoutTableCache* ltc = calloc(1, sizeof(TTLayoutTableCache));
if (ltc) {
int i;
for(i=0;i<LAYOUTCACHE_ENTRIES;i++) {
ltc->entries[i].len = -1;
}
ltc->entries[0].tag = GDEF_TAG;
ltc->entries[1].tag = GPOS_TAG;
ltc->entries[2].tag = GSUB_TAG;
ltc->entries[3].tag = HEAD_TAG;
ltc->entries[4].tag = KERN_TAG;
ltc->entries[5].tag = MORT_TAG;
ltc->entries[6].tag = MORX_TAG;
}
return ltc;
}
JNIEXPORT void freeLayoutTableCache(TTLayoutTableCache* ltc) {
if (ltc) {
int i;
for(i=0;i<LAYOUTCACHE_ENTRIES;i++) {
if(ltc->entries[i].ptr) free (ltc->entries[i].ptr);
}
if (ltc->kernPairs) free(ltc->kernPairs);
free(ltc);
}
}

View File

@@ -1794,8 +1794,10 @@ MsgRouting AwtFrame::WmNcCalcSize(BOOL wParam, LPNCCALCSIZE_PARAMS lpncsp, LRESU
if (::IsZoomed(GetHWnd())) {
rect->top += insets.bottom;
// [moklev] Workaround for RIDER-27069, IDEA-211327
rect->right += this->ScaleUpX(1);
rect->bottom -= 1;
if (!this->IsUndecorated()) {
rect->right += this->ScaleUpX(1);
rect->bottom -= 1;
}
}
else {
// this makes the native caption go uncovered

View File

@@ -0,0 +1,81 @@
/*
* Copyright 2000-2019 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* @test
* @bug 8220231
* @summary Cache HarfBuzz face object for same font's text layout calls
* @comment Test layout operations for the same font performed simultaneously
* from multiple threads
*/
import java.awt.Font;
import java.awt.font.FontRenderContext;
import java.awt.font.GlyphVector;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.atomic.AtomicReference;
public class FontLayoutStressTest {
private static final int NUMBER_OF_THREADS =
Runtime.getRuntime().availableProcessors() * 2;
private static final long TIME_TO_RUN_NS = 1_000_000_000; // 1 second
private static final Font FONT = new Font(Font.SERIF, Font.PLAIN, 12);
private static final FontRenderContext FRC = new FontRenderContext(null,
false, false);
private static final char[] TEXT = "Lorem ipsum dolor sit amet, ..."
.toCharArray();
private static double doLayout() {
GlyphVector gv = FONT.layoutGlyphVector(FRC, TEXT, 0, TEXT.length,
Font.LAYOUT_LEFT_TO_RIGHT);
return gv.getGlyphPosition(gv.getNumGlyphs()).getX();
}
public static void main(String[] args) throws Throwable {
double expectedWidth = doLayout();
AtomicReference<Throwable> throwableRef = new AtomicReference<>();
CyclicBarrier barrier = new CyclicBarrier(NUMBER_OF_THREADS);
List<Thread> threads = new ArrayList<>();
for (int i = 0; i < NUMBER_OF_THREADS; i++) {
Thread thread = new Thread(() -> {
try {
barrier.await();
long timeToStop = System.nanoTime() + TIME_TO_RUN_NS;
while (System.nanoTime() < timeToStop) {
double width = doLayout();
if (width != expectedWidth) {
throw new RuntimeException(
"Unexpected layout result");
}
}
} catch (Throwable e) {
throwableRef.set(e);
}
});
threads.add(thread);
thread.start();
}
for (Thread thread : threads) {
thread.join();
}
Throwable throwable = throwableRef.get();
if (throwable != null) {
throw throwable;
}
}
}

View File

@@ -599,7 +599,7 @@ java/awt/Window/AlwaysOnTop/AutoTestOnTop.java
java/awt/Window/AlwaysOnTop/TestAlwaysOnTopBeforeShow.java 8169530 macosx-all,windows-all
java/awt/Window/GrabSequence/GrabSequence.java 6848409 macosx-all,windows-all,linux-all
java/awt/Window/LocationAtScreenCorner/LocationAtScreenCorner.java 8203371 linux-all,solaris-all
java/awt/Window/setLocRelativeTo/SetLocationRelativeToTest.java JRE-1173 windows-all
java/awt/Window/setLocRelativeTo/SetLocationRelativeToTest.java JRE-1173 windows-all,linux-all
java/awt/datatransfer/ConstructFlavoredObjectTest/ConstructFlavoredObjectTest.java 8202860 linux-all
java/awt/datatransfer/DataFlavor/DataFlavorRemoteTest.java JRE-898 macosx-all
java/awt/datatransfer/DragImage/MultiResolutionDragImageTest.java 8080982 generic-all
@@ -984,7 +984,7 @@ javax/swing/JPopupMenu/6515446/bug6515446.java
javax/swing/JPopupMenu/6580930/bug6580930.java 8196096 windows-all,linux-all,macosx-all
javax/swing/JPopupMenu/6583251/bug6583251.java 8213564 linux-all
javax/swing/JPopupMenu/6675802/bug6675802.java 8196097 windows-all
javax/swing/JPopupMenu/6800513/bug6800513.java 7184956,8080868 macosx-all,windows-all
javax/swing/JPopupMenu/6800513/bug6800513.java 7184956,8080868 macosx-all,windows-all,linux-all
javax/swing/JPopupMenu/6987844/bug6987844.java 8169956 macosx-all,windows-all
javax/swing/JPopupMenu/7156657/bug7156657.java 8171381 macosx-all,windows-all
javax/swing/JPopupMenu/8075063/ContextMenuScrollTest.java 8202880 linux-all,windows-all,macosx-all

View File

@@ -3,6 +3,7 @@ java/awt/Choice/DragMouseOutAndRelease/DragMouseOutAndRelease.java
java/awt/Choice/GetSizeTest/GetSizeTest.java nobug macosx-all,windows-all
java/awt/Choice/ResizeAutoClosesChoice/ResizeAutoClosesChoice.java nobug windows-all,linux-all
java/awt/Component/F10TopToplevel/F10TopToplevel.html nobug linux-all
java/awt/Component/PaintAll/PaintAll.java nobug linux-all
java/awt/Container/isRemoveNotifyNeeded/JInternalFrameTest.java nobug generic-all
java/awt/Dialog/MakeWindowAlwaysOnTop/MakeWindowAlwaysOnTop.java nobug macosx-all,windows-all
java/awt/Dialog/NestedDialogs/Modal/NestedModalDialogTest.java nobug macosx-all,windows-all
@@ -14,6 +15,8 @@ java/awt/EventDispatchThread/PreserveDispathThread/PreserveDispatchThread.java
java/awt/FileDialog/FileDialogMemoryLeak/FileDialogLeakTest.java nobug windows-all
java/awt/FileDialog/FilenameFilterTest/FilenameFilterTest.html nobug macosx-all
java/awt/FileDialog/MoveToTrashTest.java nobug windows-all
java/awt/FileDialog/ModalFocus/FileDialogModalFocusTest.java nobug linux-all,windows-all
java/awt/Focus/6981400/Test2.java nobug linux-all
java/awt/Focus/AppletInitialFocusTest/AppletInitialFocusTest1.html nobug linux-all,windows-all
java/awt/FileDialog/ModalFocus/FileDialogModalFocusTest.java nobug linux-all,windows-all
java/awt/Focus/ConsumeNextKeyTypedOnModalShowTest/ConsumeNextKeyTypedOnModalShowTest.java nobug macosx-all,linux-all,windows-all
@@ -28,7 +31,9 @@ java/awt/Focus/RemoveAfterRequest/RemoveAfterRequest.java
java/awt/Focus/RequestFocusAndHideTest/RequestFocusAndHideTest.java nobug macosx-all
java/awt/Focus/RequestFocusByCause/RequestFocusByCauseTest.java nobug linux-all,windows-all reproduced with Adopt, OpenJDK
java/awt/Focus/WindowIsFocusableAccessByThreadsTest/WindowIsFocusableAccessByThreadsTest.java nobug macosx-all,windows-all
java/awt/Frame/8158918/SetExtendedState.java nobug linux-all
java/awt/Frame/FrameLocation/FrameLocation.java nobug linux-all
java/awt/Frame/FrameSize/TestFrameSize.java nobug linux-all,mac-osx
java/awt/Frame/MaximizedToIconified/MaximizedToIconified.java nobug linux-all,windows-all
java/awt/Frame/MiscUndecorated/ActiveAWTWindowTest.java nobug macosx-all,windows-all
java/awt/Frame/MiscUndecorated/ActiveSwingWindowTest.java nobug macosx-all,windows-all
@@ -71,6 +76,7 @@ java/awt/Mouse/MouseWheelAbsXY/MouseWheelAbsXY.java
java/awt/MouseAdapter/MouseAdapterUnitTest/MouseAdapterUnitTest.java nobug macosx-all,windows-all
java/awt/MouseInfo/ComponentMousePositionTest.java nobug macosx-all,windows-all
java/awt/MouseInfo/JContainerMousePositionTest.java nobug macosx-all,windows-all
java/awt/Paint/ComponentIsNotDrawnAfterRemoveAddTest/ComponentIsNotDrawnAfterRemoveAddTest.java nobug linux-all,macosx-all,windows-all
java/awt/Paint/ExposeOnEDT.java nobug windows-all
java/awt/Paint/ListRepaint.java nobug linux-all (java.lang.NullPointerException reproduced with Adopt)
java/awt/PopupMenu/PopupMenuLocation.java nobug macosx-all,linux-all,windows-all

View File

@@ -0,0 +1,826 @@
/*
* Copyright 2019 JetBrains s.r.o.
* 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.
*
* 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package performance.rendering;
import org.junit.Test;
import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.awt.geom.QuadCurve2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
public class RenderPerfTest {
private final static int N = 1000;
private final static float WIDTH = 800;
private final static float HEIGHT = 800;
private final static float R = 25;
private final static int BW = 50;
private final static int BH = 50;
private final static int COUNT = 300;
private final static int DELAY = 10;
private final static int RESOLUTION = 5;
private final static int COLOR_TOLERANCE = 10;
interface Renderable {
void render(Graphics2D g2d);
void update();
}
static class Particles {
private float[] bx;
private float[] by;
private float[] vx;
private float[] vy;
private float r;
private int n;
private float x0;
private float y0;
private float width;
private float height;
Particles(int n, float r, float x0, float y0, float width, float height) {
bx = new float[n];
by = new float[n];
vx = new float[n];
vy = new float[n];
this.n = n;
this.r = r;
this.x0 = x0;
this.y0 = y0;
this.width = width;
this.height = height;
for (int i = 0; i < n; i++) {
bx[i] = (float) (x0 + r + 0.1 + Math.random() * (width - 2 * r - 0.2 - x0));
by[i] = (float) (y0 + r + 0.1 + Math.random() * (height - 2 * r - 0.2 - y0));
vx[i] = 0.1f * (float) (Math.random() * 2 * r - r);
vy[i] = 0.1f * (float) (Math.random() * 2 * r - r);
}
}
void render(Graphics2D g2d, ParticleRenderer renderer) {
for (int i = 0; i < n; i++) {
renderer.render(g2d, i, bx, by, vx, vy);
}
}
void update() {
for (int i = 0; i < n; i++) {
bx[i] += vx[i];
if (bx[i] + r > width || bx[i] - r < x0) vx[i] = -vx[i];
by[i] += vy[i];
if (by[i] + r > height || by[i] - r < y0) vy[i] = -vy[i];
}
}
}
interface ParticleRenderer {
void render(Graphics2D g2d, int id, float[] x, float[] y, float[] vx, float[] vy);
}
private static void report(String name, double value) {
System.err.println("##teamcity[buildStatisticValue key='" + name + "' value='" + value +"']");
}
static class FlatParticleRenderer implements ParticleRenderer {
Color[] colors;
float r;
FlatParticleRenderer(int n, float r) {
colors = new Color[n];
this.r = r;
for (int i = 0; i < n; i++) {
colors[i] = new Color((float) Math.random(),
(float) Math.random(), (float) Math.random());
}
}
@Override
public void render(Graphics2D g2d, int id, float[] x, float[] y, float[] vx, float[] vy) {
g2d.setColor(colors[id % colors.length]);
g2d.fillOval((int)(x[id] - r), (int)(y[id] - r), (int)(2*r), (int)(2*r));
}
}
static class WhiteTextParticleRenderer implements ParticleRenderer {
float r;
Object hint;
WhiteTextParticleRenderer(float r, Object hint) {
this.r = r;
this.hint = hint;
}
void setPaint(Graphics2D g2d, int id) {
g2d.setColor(Color.WHITE);
}
@Override
public void render(Graphics2D g2d, int id, float[] x, float[] y, float[] vx, float[] vy) {
g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, hint);
setPaint(g2d, id);
g2d.drawString("The quick brown fox jumps over the lazy dog",
(int)(x[id] - r), (int)(y[id] - r));
g2d.drawString("The quick brown fox jumps over the lazy dog",
(int)(x[id] - r), (int)y[id]);
g2d.drawString("The quick brown fox jumps over the lazy dog",
(int)(x[id] - r), (int)(y[id] + r));
}
}
static class TextParticleRenderer extends WhiteTextParticleRenderer {
Color[] colors;
float r;
TextParticleRenderer(int n, float r, Object hint) {
super(r, hint);
colors = new Color[n];
this.r = r;
for (int i = 0; i < n; i++) {
colors[i] = new Color((float) Math.random(),
(float) Math.random(), (float) Math.random());
}
}
void setPaint(Graphics2D g2d, int id) {
g2d.setColor(colors[id % colors.length]);
}
}
static class FlatOvalRotParticleRenderer extends FlatParticleRenderer {
FlatOvalRotParticleRenderer(int n, float r) {
super(n, r);
}
void setPaint(Graphics2D g2d, int id) {
g2d.setColor(colors[id % colors.length]);
}
@Override
public void render(Graphics2D g2d, int id, float[] x, float[] y, float[] vx, float[] vy) {
setPaint(g2d, id);
if (Math.abs(vx[id] + vy[id]) > 0.001) {
AffineTransform t = (AffineTransform) g2d.getTransform().clone();
double l = vx[id] / Math.sqrt(vx[id] * vx[id] + vy[id] * vy[id]);
if (vy[id] < 0) {
l = -l;
}
g2d.translate(x[id], y[id]);
g2d.rotate(Math.acos(l));
g2d.fillOval(-(int)r, (int)(-0.5*r), (int) (2 * r), (int)r);
g2d.setTransform(t);
} else {
g2d.fillOval((int)(x[id] - r), (int)(y[id] - 0.5*r),
(int) (2 * r), (int) r);
}
}
}
static class LinGradOvalRotParticleRenderer extends FlatOvalRotParticleRenderer {
LinGradOvalRotParticleRenderer(int n, float r) {
super(n, r);
}
@Override
void setPaint(Graphics2D g2d, int id) {
Point2D start = new Point2D.Double(- r, - 0.5*r);
Point2D end = new Point2D.Double( 2 * r, r);
float[] dist = {0.0f, 1.0f};
Color[] cls = {colors[id %colors.length], colors[(colors.length - id) %colors.length]};
LinearGradientPaint p =
new LinearGradientPaint(start, end, dist, cls);
g2d.setPaint(p);
}
}
static class FlatBoxParticleRenderer extends FlatParticleRenderer {
FlatBoxParticleRenderer(int n, float r) {
super(n, r);
}
@Override
public void render(Graphics2D g2d, int id, float[] x, float[] y, float[] vx, float[] vy) {
g2d.setColor(colors[id % colors.length]);
g2d.fillRect((int)(x[id] - r), (int)(y[id] - r), (int)(2*r), (int)(2*r));
}
}
static class ImgParticleRenderer extends FlatParticleRenderer {
BufferedImage dukeImg;
ImgParticleRenderer(int n, float r) {
super(n, r);
String testDataStr = System.getProperty("testdata");
assertNotNull("testdata property is not set", testDataStr);
File testData = new File(testDataStr, "performance" + File.separator + "rendering");
assertTrue("Test data dir does not exist", testData.exists());
File dukeFile = new File(testData, "duke.png");
if (!dukeFile.exists()) throw new RuntimeException(dukeFile.toString() + " not found");
try {
dukeImg = ImageIO.read(dukeFile);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public void render(Graphics2D g2d, int id, float[] x, float[] y, float[] vx, float[] vy) {
g2d.setColor(colors[id % colors.length]);
g2d.drawImage(dukeImg, (int)(x[id] - r), (int)(y[id] - r), (int)(2*r), (int)(2*r), null);
}
}
static class FlatBoxRotParticleRenderer extends FlatParticleRenderer {
FlatBoxRotParticleRenderer(int n, float r) {
super(n, r);
}
@Override
public void render(Graphics2D g2d, int id, float[] x, float[] y, float[] vx, float[] vy) {
g2d.setColor(colors[id % colors.length]);
if (Math.abs(vx[id] + vy[id]) > 0.001) {
AffineTransform t = (AffineTransform) g2d.getTransform().clone();
double l = vx[id] / Math.sqrt(vx[id] * vx[id] + vy[id] * vy[id]);
if (vy[id] < 0) {
l = -l;
}
g2d.translate(x[id], y[id]);
g2d.rotate(Math.acos(l));
g2d.fillRect(-(int)r, -(int)r, (int) (2 * r), (int) (2 * r));
g2d.setTransform(t);
} else {
g2d.fillRect((int)(x[id] - r), (int)(y[id] - r),
(int) (2 * r), (int) (2 * r));
}
}
}
static class WiredParticleRenderer extends FlatParticleRenderer {
WiredParticleRenderer(int n, float r) {
super(n, r);
}
@Override
public void render(Graphics2D g2d, int id, float[] x, float[] y, float[] vx, float[] vy) {
g2d.setColor(colors[id % colors.length]);
g2d.drawOval((int)(x[id] - r), (int)(y[id] - r), (int)(2*r), (int)(2*r));
}
}
static class WiredBoxParticleRenderer extends FlatParticleRenderer {
WiredBoxParticleRenderer(int n, float r) {
super(n, r);
}
@Override
public void render(Graphics2D g2d, int id, float[] x, float[] y, float[] vx, float[] vy) {
g2d.setColor(colors[id % colors.length]);
g2d.drawRect((int)(x[id] - r), (int)(y[id] - r), (int)(2*r), (int)(2*r));
}
}
static class SegParticleRenderer extends FlatParticleRenderer {
SegParticleRenderer(int n, float r) {
super(n, r);
}
@Override
public void render(Graphics2D g2d, int id, float[] x, float[] y, float[] vx, float[] vy) {
double v = Math.sqrt(vx[id]*vx[id]+vy[id]*vy[id]);
float nvx = (float) (vx[id]/v);
float nvy = (float) (vy[id]/v);
g2d.setColor(colors[id % colors.length]);
g2d.drawLine((int)(x[id] - r*nvx), (int)(y[id] - r*nvy),
(int)(x[id] + 2*r*nvx), (int)(y[id] + 2*r*nvy));
}
}
static class WiredQuadParticleRenderer extends FlatParticleRenderer {
WiredQuadParticleRenderer(int n, float r) {
super(n, r);
}
@Override
public void render(Graphics2D g2d, int id, float[] x, float[] y, float[] vx, float[] vy) {
if (id > 2 && (id % 3) == 0) {
g2d.setColor(colors[id % colors.length]);
g2d.draw(new QuadCurve2D.Float(x[id-3], y[id-3], x[id-2], y[id-2], x[id-1], y[id-1]));
}
}
}
static class FlatQuadParticleRenderer extends FlatParticleRenderer {
FlatQuadParticleRenderer(int n, float r) {
super(n, r);
}
@Override
public void render(Graphics2D g2d, int id, float[] x, float[] y, float[] vx, float[] vy) {
if (id > 2 && (id % 3) == 0) {
g2d.setColor(colors[id % colors.length]);
g2d.fill(new QuadCurve2D.Float(x[id-3], y[id-3], x[id-2], y[id-2], x[id-1], y[id-1]));
}
}
}
class PerfMeter {
private int frame = 0;
private JPanel panel;
private long time;
private double execTime = 0;
private Color expColor = Color.RED;
AtomicBoolean waiting = new AtomicBoolean(false);
double exec(final Renderable renderable) throws Exception {
final CountDownLatch latch = new CountDownLatch(COUNT);
final CountDownLatch latchFrame = new CountDownLatch(1);
final JFrame f = new JFrame();
f.addWindowListener(new WindowAdapter() {
@Override
public void windowClosed(WindowEvent e) {
latchFrame.countDown();
}
});
SwingUtilities.invokeAndWait(new Runnable() {
@Override
public void run() {
panel = new JPanel()
{
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
time = System.nanoTime();
Graphics2D g2d = (Graphics2D) g;
renderable.render(g2d);
g2d.setColor(expColor);
g.fillRect(0, 0, BW, BH);
}
};
panel.setPreferredSize(new Dimension((int)(WIDTH + BW), (int)(HEIGHT + BH)));
panel.setBackground(Color.BLACK);
f.add(panel);
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
f.pack();
f.setVisible(true);
}
});
Robot robot = new Robot();
Timer timer = new Timer(DELAY, e -> {
if (waiting.compareAndSet(false, true)) {
Color c = robot.getPixelColor(panel.getTopLevelAncestor().getX() + 25,
panel.getTopLevelAncestor().getY() + 25);
if (isAlmostEqual(c, Color.BLUE)) {
expColor = Color.RED;
} else {
expColor = Color.BLUE;
}
renderable.update();
panel.getParent().repaint();
} else {
while (!isAlmostEqual(
robot.getPixelColor(
panel.getTopLevelAncestor().getX() + BW / 2,
panel.getTopLevelAncestor().getY() + BH / 2),
expColor))
{
try {
Thread.sleep(RESOLUTION);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
time = System.nanoTime() - time;
execTime += time;
frame++;
waiting.set(false);
}
latch.countDown();
});
timer.start();
latch.await();
SwingUtilities.invokeAndWait(() -> {
timer.stop();
f.setVisible(false);
f.dispose();
});
latchFrame.await();
return 1e9/(execTime / frame);
}
private boolean isAlmostEqual(Color c1, Color c2) {
return Math.abs(c1.getRed() - c2.getRed()) < COLOR_TOLERANCE ||
Math.abs(c1.getGreen() - c2.getGreen()) < COLOR_TOLERANCE ||
Math.abs(c1.getBlue() - c2.getBlue()) < COLOR_TOLERANCE;
}
}
private static final Particles balls = new Particles(N, R, BW, BH, WIDTH, HEIGHT);
private static final ParticleRenderer flatRenderer = new FlatParticleRenderer(N, R);
private static final ParticleRenderer flatOvalRotRenderer = new FlatOvalRotParticleRenderer(N, R);
private static final ParticleRenderer flatBoxRenderer = new FlatBoxParticleRenderer(N, R);
private static final ParticleRenderer flatBoxRotRenderer = new FlatBoxRotParticleRenderer(N, R);
private static final ParticleRenderer linGradOvalRotRenderer = new LinGradOvalRotParticleRenderer(N, R);
private static final ParticleRenderer wiredRenderer = new WiredParticleRenderer(N, R);
private static final ParticleRenderer wiredBoxRenderer = new WiredBoxParticleRenderer(N, R);
private static final ParticleRenderer segRenderer = new SegParticleRenderer(N, R);
private static final ParticleRenderer flatQuadRenderer = new FlatQuadParticleRenderer(N, R);
private static final ParticleRenderer wiredQuadRenderer = new WiredQuadParticleRenderer(N, R);
private static final ParticleRenderer imgRenderer = new ImgParticleRenderer(N, R);
private static final ParticleRenderer textRendererNoAA =
new TextParticleRenderer(N, R, RenderingHints.VALUE_TEXT_ANTIALIAS_OFF);
private static final ParticleRenderer textRendererLCD =
new TextParticleRenderer(N, R, RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB);
private static final ParticleRenderer textRendererGray =
new TextParticleRenderer(N, R, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
private static final ParticleRenderer whiteTextRendererNoAA =
new WhiteTextParticleRenderer(R, RenderingHints.VALUE_TEXT_ANTIALIAS_OFF);
private static final ParticleRenderer whiteTextRendererLCD =
new WhiteTextParticleRenderer(R, RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB);
private static final ParticleRenderer whiteTextRendererGray =
new WhiteTextParticleRenderer(R, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
@Test
public void testFlatBubbles() throws Exception {
double fps = (new PerfMeter()).exec(new Renderable() {
@Override
public void render(Graphics2D g2d) {
balls.render(g2d, flatRenderer);
}
@Override
public void update() {
balls.update();
}
});
report("FlatOval", fps);
}
@Test
public void testFlatBoxBubbles() throws Exception {
double fps = (new PerfMeter()).exec(new Renderable() {
@Override
public void render(Graphics2D g2d) {
balls.render(g2d, flatBoxRenderer);
}
@Override
public void update() {
balls.update();
}
});
report("FlatBox", fps);
}
@Test
public void testImgBubbles() throws Exception {
double fps = (new PerfMeter()).exec(new Renderable() {
@Override
public void render(Graphics2D g2d) {
balls.render(g2d, imgRenderer);
}
@Override
public void update() {
balls.update();
}
});
report("Image", fps);
}
@Test
public void testFlatBoxRotBubbles() throws Exception {
double fps = (new PerfMeter()).exec(new Renderable() {
@Override
public void render(Graphics2D g2d) {
balls.render(g2d, flatBoxRotRenderer);
}
@Override
public void update() {
balls.update();
}
});
report("RotatedBox", fps);
}
@Test
public void testFlatOvalRotBubbles() throws Exception {
double fps = (new PerfMeter()).exec(new Renderable() {
@Override
public void render(Graphics2D g2d) {
balls.render(g2d, flatOvalRotRenderer);
}
@Override
public void update() {
balls.update();
}
});
report("RotatedOval", fps);
}
@Test
public void testLinGradOvalRotBubbles() throws Exception {
double fps = (new PerfMeter()).exec(new Renderable() {
@Override
public void render(Graphics2D g2d) {
balls.render(g2d, linGradOvalRotRenderer);
}
@Override
public void update() {
balls.update();
}
});
report("LinGradRotatedOval", fps);
}
@Test
public void testWiredBubbles() throws Exception {
double fps = (new PerfMeter()).exec(new Renderable() {
@Override
public void render(Graphics2D g2d) {
balls.render(g2d, wiredRenderer);
}
@Override
public void update() {
balls.update();
}
});
report("WiredOval", fps);
}
@Test
public void testWiredBoxBubbles() throws Exception {
double fps = (new PerfMeter()).exec(new Renderable() {
@Override
public void render(Graphics2D g2d) {
balls.render(g2d, wiredBoxRenderer);
}
@Override
public void update() {
balls.update();
}
});
report("WiredBox", fps);
}
@Test
public void testLines() throws Exception {
double fps = (new PerfMeter()).exec(new Renderable() {
@Override
public void render(Graphics2D g2d) {
balls.render(g2d, segRenderer);
}
@Override
public void update() {
balls.update();
}
});
report("Lines", fps);
}
@Test
public void testFlatQuad() throws Exception {
double fps = (new PerfMeter()).exec(new Renderable() {
@Override
public void render(Graphics2D g2d) {
balls.render(g2d, flatQuadRenderer);
}
@Override
public void update() {
balls.update();
}
});
report("FlatQuad", fps);
}
@Test
public void testWiredQuad() throws Exception {
double fps = (new PerfMeter()).exec(new Renderable() {
@Override
public void render(Graphics2D g2d) {
balls.render(g2d, wiredQuadRenderer);
}
@Override
public void update() {
balls.update();
}
});
report("WiredQuad", fps);
}
@Test
public void testTextBubblesNoAA() throws Exception {
double fps = (new PerfMeter()).exec(new Renderable() {
@Override
public void render(Graphics2D g2d) {
balls.render(g2d, textRendererNoAA);
}
@Override
public void update() {
balls.update();
}
});
report("TextNoAA", fps);
}
@Test
public void testTextBubblesLCD() throws Exception {
double fps = (new PerfMeter()).exec(new Renderable() {
@Override
public void render(Graphics2D g2d) {
balls.render(g2d, textRendererLCD);
}
@Override
public void update() {
balls.update();
}
});
report("TextLCD", fps);
}
@Test
public void testTextBubblesGray() throws Exception {
double fps = (new PerfMeter()).exec(new Renderable() {
@Override
public void render(Graphics2D g2d) {
balls.render(g2d, textRendererGray);
}
@Override
public void update() {
balls.update();
}
});
report("TextGray", fps);
}
@Test
public void testWhiteTextBubblesNoAA() throws Exception {
double fps = (new PerfMeter()).exec(new Renderable() {
@Override
public void render(Graphics2D g2d) {
balls.render(g2d, whiteTextRendererNoAA);
}
@Override
public void update() {
balls.update();
}
});
report("WhiteTextNoAA", fps);
}
@Test
public void testWhiteTextBubblesLCD() throws Exception {
double fps = (new PerfMeter()).exec(new Renderable() {
@Override
public void render(Graphics2D g2d) {
balls.render(g2d, whiteTextRendererLCD);
}
@Override
public void update() {
balls.update();
}
});
report("WhiteTextLCD", fps);
}
@Test
public void testWhiteTextBubblesGray() throws Exception {
double fps = (new PerfMeter()).exec(new Renderable() {
@Override
public void render(Graphics2D g2d) {
balls.render(g2d, whiteTextRendererGray);
}
@Override
public void update() {
balls.update();
}
});
report("WhiteTextGray", fps);
}
}

View File

@@ -1,14 +1,12 @@
package quality.text;
import org.junit.Test;
import quality.util.RenderUtil;
import java.awt.*;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.awt.image.Raster;
import java.io.File;
@@ -40,7 +38,7 @@ public class DroidFontTest {
String[] testDataVariant = {
"osx_hardware_rendering", "osx_software_rendering", "osx_sierra_rendering",
"linux_rendering"};
"osx_mojave_rendering", "linux_rendering"};
String testDataStr = System.getProperty("testdata");
assertNotNull("testdata property is not set", testDataStr);
@@ -70,7 +68,7 @@ public class DroidFontTest {
(int) bnd.getWidth(), (int) bnd.getHeight());
String gfName = name.toLowerCase().replace(" ", "") +
Integer.toString(style) + "_" + Integer.toString(size) + ".png";
style + "_" + size + ".png";
if (System.getProperty("gentestdata") == null) {
boolean failed = true;
@@ -98,7 +96,7 @@ public class DroidFontTest {
for (int j = 0; j < gRaster.getHeight(); j++) {
gRaster.getPixel(i, j, gArr);
rRaster.getPixel(i, j, rArr);
assertTrue(gArr.length == rArr.length);
assertEquals(gArr.length, rArr.length);
for (int k = 0; k < gArr.length; k++) {
if (gArr[k] != rArr[k]) {
failureReason.append(variant).append(" : Different pixels found ").append("at (").append(i).append(",").append(j).append(")");

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2017 JetBrains s.r.o.
* Copyright 2019 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,81 +16,25 @@
package quality.util;
import com.sun.jna.Pointer;
import org.apache.commons.lang3.SystemUtils;
import quality.util.osx.Foundation;
import quality.util.osx.FoundationLibrary;
import quality.util.osx.ID;
import quality.util.osx.MacUtil;
import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.color.ColorSpace;
import java.awt.image.BufferedImage;
import java.awt.image.ColorConvertOp;
import java.awt.image.Raster;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.nio.ByteBuffer;
import java.util.function.Consumer;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.*;
public class RenderUtil {
private static BufferedImage captureScreen(Window belowWindow, Rectangle rect) {
ID pool = Foundation.invoke("NSAutoreleasePool", "new");
try {
ID windowId = belowWindow != null ? MacUtil.findWindowFromJavaWindow(belowWindow) : null;
Foundation.NSRect nsRect = new Foundation.NSRect(rect.x, rect.y, rect.width, rect.height);
ID cgWindowId = windowId != null ? Foundation.invoke(windowId, "windowNumber") : ID.NIL;
int windowListOptions = cgWindowId != null
? FoundationLibrary.kCGWindowListOptionIncludingWindow
: FoundationLibrary.kCGWindowListOptionAll;
int windowImageOptions = FoundationLibrary.kCGWindowImageBestResolution |
FoundationLibrary.kCGWindowImageShouldBeOpaque;
ID cgImageRef = Foundation.cgWindowListCreateImage(
nsRect, windowListOptions, cgWindowId, windowImageOptions);
ID bitmapRep = Foundation.invoke(
Foundation.invoke("NSBitmapImageRep", "alloc"),
"initWithCGImage:", cgImageRef);
ID nsImage = Foundation.invoke(
Foundation.invoke("NSImage", "alloc"),
"init");
Foundation.invoke(nsImage, "addRepresentation:", bitmapRep);
ID data = Foundation.invoke(nsImage, "TIFFRepresentation");
ID bytes = Foundation.invoke(data, "bytes");
ID length = Foundation.invoke(data, "length");
Pointer ptr = new Pointer(bytes.longValue());
ByteBuffer byteBuffer = ptr.getByteBuffer(0, length.longValue());
Foundation.invoke(nsImage, "release");
byte[] b = new byte[byteBuffer.remaining()];
byteBuffer.get(b);
BufferedImage result = ImageIO.read(new ByteArrayInputStream(b));
if (result != null) {
ColorSpace ics = ColorSpace.getInstance(ColorSpace.CS_sRGB);
ColorConvertOp cco = new ColorConvertOp(ics, null);
return cco.filter(result, null);
}
return null;
}
catch (Throwable t) {
return null;
}
finally {
Foundation.invoke(pool, "release");
}
}
private final static int TOLERANCE = 1;
public static BufferedImage capture(int width, int height, Consumer<Graphics2D> painter)
throws Exception
{
JFrame[] f = new JFrame[1];
Point[] p = new Point[1];
double[] scale = new double[2];
SwingUtilities.invokeAndWait(() -> {
f[0] = new JFrame();
@@ -99,22 +43,26 @@ public class RenderUtil {
f[0].add(c);
c.setSize(width + 10, height + 10);
f[0].setSize(width + 100, height + 100); // giving some space
// for frame border effects,
// e.g. rounded frame
// for frame border effects,
// e.g. rounded frame
c.setLocation(50, 50);
f[0].setVisible(true);
p[0]= c.getLocationOnScreen();
scale[0] = f[0].getGraphicsConfiguration().getDefaultTransform().getScaleX();
scale[1] = f[0].getGraphicsConfiguration().getDefaultTransform().getScaleY();
});
Rectangle screenRect;
Robot r = new Robot();
while (!Color.black.equals(r.getPixelColor(p[0].x, p[0].y))) {
while (!Color.black.equals(r.getPixelColor(p[0].x+1, p[0].y))) {
Thread.sleep(100);
}
screenRect = new Rectangle(p[0].x + 5, p[0].y + 5, width, height);
screenRect = new Rectangle(
p[0].x + 5,
p[0].y + 5,
(int)((width - 20) * scale[0]), (int)((height - 30) * scale[1]));
BufferedImage result = SystemUtils.IS_OS_MAC ?
captureScreen(f[0], screenRect) : r.createScreenCapture(screenRect);
BufferedImage result = r.createScreenCapture(screenRect);
SwingUtilities.invokeAndWait(f[0]::dispose);
return result;
}
@@ -129,14 +77,16 @@ public class RenderUtil {
@Override
protected void paintComponent(Graphics g) {
g.translate(5, 5);
Shape savedClip = g.getClip();
g.clipRect(0, 0, getWidth() - 20, getHeight() - 20);
g.translate(5, 5);
painter.accept((Graphics2D)g);
g.translate(-5, -5);
g.setClip(savedClip);
g.setColor(Color.black);
((Graphics2D) g).setStroke(new BasicStroke(10));
g.drawRect(-5, -5, getWidth() - 5, getHeight() - 5);
g.fillRect(0, 0, getWidth() + 10, 5);
g.fillRect(0, getHeight()-5, getWidth() + 10, 5);
g.fillRect(getWidth() - 10, -10, getWidth() + 5, getHeight() + 5);
g.fillRect(-5, -10, 10, getHeight() + 5);
}
}
@@ -145,7 +95,7 @@ public class RenderUtil {
String[] testDataVariant = {
"osx_hardware_rendering", "osx_software_rendering",
"osx_sierra_rendering", "osx_lowres_rendering",
"osx_sierra_rendering", "osx_mojave_rendering", "osx_lowres_rendering",
"linux_rendering", "windows_rendering"};
String testDataStr = System.getProperty("testdata");
@@ -181,11 +131,12 @@ public class RenderUtil {
for (int j = 0; j < gRaster.getHeight(); j++) {
gRaster.getPixel(i, j, gArr);
rRaster.getPixel(i, j, rArr);
assertTrue(gArr.length == rArr.length);
for (int k = 0; k < gArr.length; k++) {
if (gArr[k] != rArr[k]) {
failureReason.append(variant).append(" : Different pixels found ").
append("at (").append(i).append(",").append(j).append(")");
int diff = Math.abs(gArr[k] - rArr[k]);
if (diff > TOLERANCE) {
failureReason.append(variant).append(" : Different pixels found (").
append("c[").append(k).append("]=").append(diff).
append(") at (").append(i).append(",").append(j).append(")");
failed = true;
break scan;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB