Path: blob/master/src/java.desktop/macosx/classes/apple/laf/JRSUIControl.java
41152 views
/*1* Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package apple.laf;2627import java.nio.*;28import java.util.*;2930import apple.laf.JRSUIConstants.*;3132public final class JRSUIControl {33private static native int initNativeJRSUI();3435private static native long getPtrOfBuffer(ByteBuffer byteBuffer);36private static native long getCFDictionary(boolean flipped);37private static native void disposeCFDictionary(long cfDictionaryPtr);3839private static native int syncChanges(long cfDictionaryPtr, long byteBufferPtr);4041// private static native int paint(long cfDictionaryPtr, long oldProperties, long newProperties, OSXSurfaceData osxsd, double x, double y, double w, double h);42// private static native int paintChanges(long cfDictionaryPtr, long byteBufferPtr, long oldProperties, long newProperties, OSXSurfaceData osxsd, double x, double y, double w, double h);4344private static native int paintToCGContext (long cgContext, long cfDictionaryPtr, long oldProperties, long newProperties, double x, double y, double w, double h);45private static native int paintChangesToCGContext (long cgContext, long cfDictionaryPtr, long oldProperties, long newProperties, double x, double y, double w, double h, long byteBufferPtr);4647private static native int paintImage (int[] data, int imgW, int imgH, long cfDictionaryPtr, long oldProperties, long newProperties, double x, double y, double w, double h);48private static native int paintChangesImage (int[] data, int imgW, int imgH, long cfDictionaryPtr, long oldProperties, long newProperties, double x, double y, double w, double h, long byteBufferPtr);4950private static native int getNativeHitPart( long cfDictionaryPtr, long oldProperties, long newProperties, double x, double y, double w, double h, double hitX, double hitY);51private static native void getNativePartBounds(final double[] rect, long cfDictionaryPtr, long oldProperties, long newProperties, double x, double y, double w, double h, int part);52private static native double getNativeScrollBarOffsetChange( long cfDictionaryPtr, long oldProperties, long newProperties, double x, double y, double w, double h, int offset, int visibleAmount, int extent);5354private static final int INCOHERENT = 2;55private static final int NOT_INIT = 1;56private static final int SUCCESS = 0;57private static final int NULL_PTR = -1;58private static final int NULL_CG_REF = -2;5960private static int nativeJRSInitialized = NOT_INIT;616263public static void initJRSUI() {64if (nativeJRSInitialized == SUCCESS) return;65nativeJRSInitialized = initNativeJRSUI();66if (nativeJRSInitialized != SUCCESS) throw new RuntimeException("JRSUI could not be initialized (" + nativeJRSInitialized + ").");67}6869private static final int NIO_BUFFER_SIZE = 128;70private static class ThreadLocalByteBuffer {71final ByteBuffer buffer;72final long ptr;7374public ThreadLocalByteBuffer() {75buffer = ByteBuffer.allocateDirect(NIO_BUFFER_SIZE);76buffer.order(ByteOrder.nativeOrder());77ptr = getPtrOfBuffer(buffer);78}79}8081private static final ThreadLocal<ThreadLocalByteBuffer> threadLocal = new ThreadLocal<ThreadLocalByteBuffer>();82private static ThreadLocalByteBuffer getThreadLocalBuffer() {83ThreadLocalByteBuffer byteBuffer = threadLocal.get();84if (byteBuffer != null) return byteBuffer;8586byteBuffer = new ThreadLocalByteBuffer();87threadLocal.set(byteBuffer);88return byteBuffer;89}9091private final HashMap<Key, DoubleValue> nativeMap;92private final HashMap<Key, DoubleValue> changes;93private long cfDictionaryPtr;9495private long priorEncodedProperties;96private long currentEncodedProperties;97private final boolean flipped;9899public JRSUIControl(final boolean flipped){100this.flipped = flipped;101cfDictionaryPtr = getCFDictionary(flipped);102if (cfDictionaryPtr == 0) throw new RuntimeException("Unable to create native representation");103nativeMap = new HashMap<Key, DoubleValue>();104changes = new HashMap<Key, DoubleValue>();105}106107JRSUIControl(final JRSUIControl other) {108flipped = other.flipped;109cfDictionaryPtr = getCFDictionary(flipped);110if (cfDictionaryPtr == 0) throw new RuntimeException("Unable to create native representation");111nativeMap = new HashMap<Key, DoubleValue>();112changes = new HashMap<Key, DoubleValue>(other.nativeMap);113changes.putAll(other.changes);114}115116@SuppressWarnings("deprecation")117protected synchronized void finalize() throws Throwable {118if (cfDictionaryPtr == 0) return;119disposeCFDictionary(cfDictionaryPtr);120cfDictionaryPtr = 0;121}122123124enum BufferState {125NO_CHANGE,126ALL_CHANGES_IN_BUFFER,127SOME_CHANGES_IN_BUFFER,128CHANGE_WONT_FIT_IN_BUFFER;129}130131private BufferState loadBufferWithChanges(final ThreadLocalByteBuffer localByteBuffer) {132final ByteBuffer buffer = localByteBuffer.buffer;133buffer.rewind();134135for (final JRSUIConstants.Key key : new HashSet<JRSUIConstants.Key>(changes.keySet())) {136final int changeIndex = buffer.position();137final JRSUIConstants.DoubleValue value = changes.get(key);138139try {140buffer.putLong(key.getConstantPtr());141buffer.put(value.getTypeCode());142value.putValueInBuffer(buffer);143} catch (final BufferOverflowException e) {144return handleBufferOverflow(buffer, changeIndex);145} catch (final RuntimeException e) {146System.err.println(this);147throw e;148}149150if (buffer.position() >= NIO_BUFFER_SIZE - 8) {151return handleBufferOverflow(buffer, changeIndex);152}153154changes.remove(key);155nativeMap.put(key, value);156}157158buffer.putLong(0);159return BufferState.ALL_CHANGES_IN_BUFFER;160}161162private BufferState handleBufferOverflow(final ByteBuffer buffer, final int changeIndex) {163if (changeIndex == 0) {164buffer.putLong(0, 0);165return BufferState.CHANGE_WONT_FIT_IN_BUFFER;166}167168buffer.putLong(changeIndex, 0);169return BufferState.SOME_CHANGES_IN_BUFFER;170}171172private synchronized void set(final JRSUIConstants.Key key, final JRSUIConstants.DoubleValue value) {173final JRSUIConstants.DoubleValue existingValue = nativeMap.get(key);174175if (existingValue != null && existingValue.equals(value)) {176changes.remove(key);177return;178}179180changes.put(key, value);181}182183public void set(final JRSUIState state) {184state.apply(this);185}186187void setEncodedState(final long state) {188currentEncodedProperties = state;189}190191void set(final JRSUIConstants.Key key, final double value) {192set(key, new JRSUIConstants.DoubleValue(value));193}194195// private static final Color blue = new Color(0x00, 0x00, 0xFF, 0x40);196// private static void paintDebug(Graphics2D g, double x, double y, double w, double h) {197// final Color prev = g.getColor();198// g.setColor(blue);199// g.drawRect((int)x, (int)y, (int)w, (int)h);200// g.setColor(prev);201// }202203// private static int paintsWithNoChange = 0;204// private static int paintsWithChangesThatFit = 0;205// private static int paintsWithChangesThatOverflowed = 0;206207public void paint(final int[] data, final int imgW, final int imgH, final double x, final double y, final double w, final double h) {208paintImage(data, imgW, imgH, x, y, w, h);209priorEncodedProperties = currentEncodedProperties;210}211212private synchronized int paintImage(final int[] data, final int imgW, final int imgH, final double x, final double y, final double w, final double h) {213if (changes.isEmpty()) {214// paintsWithNoChange++;215return paintImage(data, imgW, imgH, cfDictionaryPtr, priorEncodedProperties, currentEncodedProperties, x, y, w, h);216}217218final ThreadLocalByteBuffer localByteBuffer = getThreadLocalBuffer();219BufferState bufferState = loadBufferWithChanges(localByteBuffer);220221// fast tracking this, since it's the likely scenario222if (bufferState == BufferState.ALL_CHANGES_IN_BUFFER) {223// paintsWithChangesThatFit++;224return paintChangesImage(data, imgW, imgH, cfDictionaryPtr, priorEncodedProperties, currentEncodedProperties, x, y, w, h, localByteBuffer.ptr);225}226227while (bufferState == BufferState.SOME_CHANGES_IN_BUFFER) {228final int status = syncChanges(cfDictionaryPtr, localByteBuffer.ptr);229if (status != SUCCESS) throw new RuntimeException("JRSUI failed to sync changes into the native buffer: " + this);230bufferState = loadBufferWithChanges(localByteBuffer);231}232233if (bufferState == BufferState.CHANGE_WONT_FIT_IN_BUFFER) {234throw new RuntimeException("JRSUI failed to sync changes to the native buffer, because some change was too big: " + this);235}236237// implicitly ALL_CHANGES_IN_BUFFER, now that we sync'd the buffer down to native a few times238// paintsWithChangesThatOverflowed++;239return paintChangesImage(data, imgW, imgH, cfDictionaryPtr, priorEncodedProperties, currentEncodedProperties, x, y, w, h, localByteBuffer.ptr);240}241242public void paint(final long cgContext, final double x, final double y, final double w, final double h) {243paintToCGContext(cgContext, x, y, w, h);244priorEncodedProperties = currentEncodedProperties;245}246247private synchronized int paintToCGContext(final long cgContext, final double x, final double y, final double w, final double h) {248if (changes.isEmpty()) {249// paintsWithNoChange++;250return paintToCGContext(cgContext, cfDictionaryPtr, priorEncodedProperties, currentEncodedProperties, x, y, w, h);251}252253final ThreadLocalByteBuffer localByteBuffer = getThreadLocalBuffer();254BufferState bufferState = loadBufferWithChanges(localByteBuffer);255256// fast tracking this, since it's the likely scenario257if (bufferState == BufferState.ALL_CHANGES_IN_BUFFER) {258// paintsWithChangesThatFit++;259return paintChangesToCGContext(cgContext, cfDictionaryPtr, priorEncodedProperties, currentEncodedProperties, x, y, w, h, localByteBuffer.ptr);260}261262while (bufferState == BufferState.SOME_CHANGES_IN_BUFFER) {263final int status = syncChanges(cfDictionaryPtr, localByteBuffer.ptr);264if (status != SUCCESS) throw new RuntimeException("JRSUI failed to sync changes into the native buffer: " + this);265bufferState = loadBufferWithChanges(localByteBuffer);266}267268if (bufferState == BufferState.CHANGE_WONT_FIT_IN_BUFFER) {269throw new RuntimeException("JRSUI failed to sync changes to the native buffer, because some change was too big: " + this);270}271272// implicitly ALL_CHANGES_IN_BUFFER, now that we sync'd the buffer down to native a few times273// paintsWithChangesThatOverflowed++;274return paintChangesToCGContext(cgContext, cfDictionaryPtr, priorEncodedProperties, currentEncodedProperties, x, y, w, h, localByteBuffer.ptr);275}276277278Hit getHitForPoint(final int x, final int y, final int w, final int h, final int hitX, final int hitY) {279sync();280// reflect hitY about the midline of the control before sending to native281final Hit hit = JRSUIConstants.getHit(getNativeHitPart(cfDictionaryPtr, priorEncodedProperties, currentEncodedProperties, x, y, w, h, hitX, 2 * y + h - hitY));282priorEncodedProperties = currentEncodedProperties;283return hit;284}285286void getPartBounds(final double[] rect, final int x, final int y, final int w, final int h, final int part) {287if (rect == null) throw new NullPointerException("Cannot load null rect");288if (rect.length != 4) throw new IllegalArgumentException("Rect must have four elements");289290sync();291getNativePartBounds(rect, cfDictionaryPtr, priorEncodedProperties, currentEncodedProperties, x, y, w, h, part);292priorEncodedProperties = currentEncodedProperties;293}294295double getScrollBarOffsetChange(final int x, final int y, final int w, final int h, final int offset, final int visibleAmount, final int extent) {296sync();297final double offsetChange = getNativeScrollBarOffsetChange(cfDictionaryPtr, priorEncodedProperties, currentEncodedProperties, x, y, w, h, offset, visibleAmount, extent);298priorEncodedProperties = currentEncodedProperties;299return offsetChange;300}301302private void sync() {303if (changes.isEmpty()) return;304305final ThreadLocalByteBuffer localByteBuffer = getThreadLocalBuffer();306BufferState bufferState = loadBufferWithChanges(localByteBuffer);307if (bufferState == BufferState.ALL_CHANGES_IN_BUFFER) {308final int status = syncChanges(cfDictionaryPtr, localByteBuffer.ptr);309if (status != SUCCESS) throw new RuntimeException("JRSUI failed to sync changes into the native buffer: " + this);310return;311}312313while (bufferState == BufferState.SOME_CHANGES_IN_BUFFER) {314final int status = syncChanges(cfDictionaryPtr, localByteBuffer.ptr);315if (status != SUCCESS) throw new RuntimeException("JRSUI failed to sync changes into the native buffer: " + this);316bufferState = loadBufferWithChanges(localByteBuffer);317}318319if (bufferState == BufferState.CHANGE_WONT_FIT_IN_BUFFER) {320throw new RuntimeException("JRSUI failed to sync changes to the native buffer, because some change was too big: " + this);321}322}323324@Override325public int hashCode() {326int bits = (int)(currentEncodedProperties ^ (currentEncodedProperties >>> 32));327bits ^= nativeMap.hashCode();328bits ^= changes.hashCode();329return bits;330}331332@Override333public boolean equals(final Object obj) {334if (!(obj instanceof JRSUIControl)) return false;335final JRSUIControl other = (JRSUIControl)obj;336if (currentEncodedProperties != other.currentEncodedProperties) return false;337if (!nativeMap.equals(other.nativeMap)) return false;338if (!changes.equals(other.changes)) return false;339return true;340}341342@Override343public String toString() {344final StringBuilder builder = new StringBuilder("JRSUIControl[inNative:");345builder.append(Arrays.toString(nativeMap.entrySet().toArray()));346builder.append(", changes:");347builder.append(Arrays.toString(changes.entrySet().toArray()));348builder.append("]");349return builder.toString();350}351}352353354