sortedValues = entries.values().toArray();
75 | sortedValues.sort((value1, value2) -> value1.filePath.compareTo(value2.filePath));
76 |
77 | for (Entry entry : sortedValues) {
78 |
79 | writer.write(entry.sha1.toString());
80 | writer.write(" ");
81 | writer.write(entry.filePath);
82 | writer.write("\n");
83 |
84 | }
85 |
86 | writer.flush();
87 | writer.close();
88 | }
89 |
90 | /**
91 | * Checks the SHA1 hash of a file against the SHA1 stored in the table.
92 | */
93 | public CheckFileResult checkFile(File file) {
94 |
95 | if (!isKnownFile(file)) {
96 | return CheckFileResult.NoSHA1SumFound;
97 | }
98 |
99 | try {
100 |
101 | Entry entry = entries.get(file.getPath());
102 |
103 | if (entry.checkResult != CheckFileResult.Unchecked) {
104 | // no need to hash more than once
105 | return entry.checkResult;
106 | }
107 |
108 | SHA1 sha1 = FileStreamReader.hashStream(new FileInputStream(file));
109 |
110 | if (sha1.equals(entry.sha1)) {
111 | entry.checkResult = CheckFileResult.Unmodified;
112 | } else {
113 | entry.checkResult = CheckFileResult.Modified;
114 | }
115 |
116 | return entry.checkResult;
117 |
118 | } catch (IOException e) {
119 | return CheckFileResult.FileNotFound;
120 | }
121 |
122 | }
123 |
124 | /**
125 | * Adds a file and its hash to the SHA1 table.
126 | */
127 | public void registerFile(File file) throws IOException {
128 |
129 | SHA1 sha1 = FileStreamReader.hashStream(new FileInputStream(file));
130 |
131 | if (isKnownFile(file)) {
132 | Entry entry = entries.get(file.getPath());
133 | entry.sha1 = sha1;
134 | } else {
135 | Entry entry = new Entry(file.getPath(), sha1);
136 | entries.put(file.getPath(), entry);
137 | }
138 | }
139 |
140 | /**
141 | * Removes a table entry.
142 | */
143 | @Deprecated
144 | public void unregisterFile(File file) {
145 | if (isKnownFile(file)) {
146 | entries.remove(file.getPath());
147 | }
148 | }
149 |
150 | public boolean hasUncheckedFiles() {
151 | for (Entry entry : entries.values()) {
152 | if (entry.checkResult == CheckFileResult.Unchecked) {
153 | return true;
154 | }
155 | }
156 | return false;
157 | }
158 |
159 | private boolean isKnownFile(File file) {
160 | return entries.containsKey(file.getPath());
161 | }
162 |
163 | }
164 |
--------------------------------------------------------------------------------
/src/main/java/com/badlogic/gdx/concurrent/AsyncTask.java:
--------------------------------------------------------------------------------
1 | package com.badlogic.gdx.concurrent;
2 |
3 | import com.badlogic.gdx.function.Consumer;
4 | import com.badlogic.gdx.function.Predicate;
5 |
6 | import java.util.concurrent.*;
7 | import java.util.concurrent.atomic.AtomicReference;
8 |
9 | /**
10 | * A reentrant wrapper to {@link FutureTask}, with some additional properties to synchronize its result with the
11 | * calling thread.
12 | *
13 | * The task is (re-)scheduled for asynchronous execution with {@link AsyncTask#execute(ExecutorService)}.
14 | *
15 | * Upon completion, a {@link CyclicBarrier} is entered, waiting for the scheduling thread to call
16 | * {@link AsyncTask#await(Consumer)}. After both threads have entered the barrier, {@link AsyncTaskJob#completed()}
17 | * is called.
18 | */
19 | public class AsyncTask> {
20 |
21 | private enum State {
22 | READY,
23 | PENDING,
24 | COMPLETED
25 | }
26 |
27 | protected final V job;
28 |
29 | private final CyclicBarrier completionBarrier;
30 |
31 | private final AtomicReference state = new AtomicReference<>(State.READY);
32 |
33 | private Task task;
34 |
35 | public AsyncTask(V job) {
36 | this.job = job;
37 | completionBarrier = new CyclicBarrier(2, this::completed);
38 | }
39 |
40 | public boolean consumeJobPredicate(Predicate consumer) {
41 |
42 | if (state.get() != State.READY) {
43 | //throw new IllegalStateException("Invalid task state!");
44 | return false;
45 | }
46 |
47 | return consumer.test(job);
48 | }
49 |
50 | public void consumeJob(Consumer consumer) {
51 |
52 | if (state.get() != State.READY) {
53 | //throw new IllegalStateException("Invalid task state!");
54 | return;
55 | }
56 |
57 | consumer.accept(job);
58 | }
59 |
60 | public boolean isReady() {
61 | return state.get() == State.READY;
62 | }
63 |
64 | public boolean isPending() {
65 | return state.get() == State.PENDING;
66 | }
67 |
68 | public boolean isCompleted() {
69 | return state.get() == State.COMPLETED;
70 | }
71 |
72 | /**
73 | * Enters the task's completion barrier, waiting for {@link AsyncTaskJob#completed()} to be called.
74 | *
75 | * This function blocks execution if the task is still pending. Use {@link AsyncTask#isCompleted()}
76 | * for a non-blocking check.
77 | *
78 | * Returns the arrival index of the current thread, see {@link CyclicBarrier#await()}.
79 | */
80 | public int await(Consumer consumeAfterCompletion) throws InterruptedException {
81 |
82 | if (state.get() == State.READY) {
83 | throw new IllegalStateException("Invalid task state!");
84 | }
85 |
86 | try {
87 |
88 | int arrivalIndex = completionBarrier.await();
89 |
90 | task.get(); // this causes an ExecutionException if there has been some error
91 |
92 | if (consumeAfterCompletion != null) {
93 | consumeAfterCompletion.accept(job);
94 | }
95 |
96 | if (!state.compareAndSet(State.COMPLETED, State.READY)) {
97 | throw new IllegalStateException("Invalid task state!");
98 | }
99 |
100 | return arrivalIndex;
101 |
102 | } catch (BrokenBarrierException e) {
103 | throw new InterruptedException(e.getMessage());
104 | } catch (ExecutionException e) {
105 | throw new RuntimeException("Exception thrown during execution of asynchronous task!", e);
106 | }
107 | }
108 |
109 | /**
110 | * Queue the task for execution by the given {@link ExecutorService}.
111 | *
112 | * @throws IllegalStateException if the task is not ready yet, after it has been scheduled previously.
113 | */
114 | void execute(ExecutorService service) {
115 |
116 | // reset state
117 | if (!state.compareAndSet(State.READY, State.PENDING)) {
118 | throw new IllegalStateException("Invalid task state!");
119 | }
120 |
121 | completionBarrier.reset();
122 |
123 | // pass to executor service
124 | service.execute(task = new Task());
125 | }
126 |
127 | /**
128 | * Called from {@link CyclicBarrier} when the async task is completed.
129 | */
130 | private void completed() {
131 | job.completed();
132 | }
133 |
134 | private class Task extends FutureTask {
135 |
136 | Task() {
137 | super(job);
138 | }
139 |
140 | @Override
141 | protected void done() {
142 |
143 | try {
144 |
145 | if (!state.compareAndSet(State.PENDING, State.COMPLETED)) {
146 | throw new IllegalStateException("Invalid completion state!");
147 | }
148 |
149 | completionBarrier.await();
150 |
151 | } catch (InterruptedException | BrokenBarrierException e) {
152 | throw new IllegalStateException(e);
153 | }
154 |
155 | }
156 | }
157 | }
158 |
--------------------------------------------------------------------------------
/src/main/java/com/badlogic/gdx/concurrent/AsyncTaskExecutor.java:
--------------------------------------------------------------------------------
1 | package com.badlogic.gdx.concurrent;
2 |
3 | import com.badlogic.gdx.utils.Disposable;
4 | import com.badlogic.gdx.utils.GdxSnippets;
5 |
6 | import java.util.concurrent.*;
7 | import java.util.concurrent.atomic.AtomicInteger;
8 |
9 | /**
10 | * A wrapper to {@link ExecutorService} which executes {@link AsyncTask} tasks.
11 | */
12 | public class AsyncTaskExecutor implements Disposable {
13 |
14 | private final ExecutorService service;
15 |
16 | public AsyncTaskExecutor(int threadCount, String threadNamePrefix) {
17 |
18 | threadCount = Math.max(threadCount, 1);
19 | GdxSnippets.log.info("Starting {} with {} threads.", threadNamePrefix, threadCount);
20 |
21 | service = new FixedThreadPoolExecutor(threadCount, new Factory(threadNamePrefix));
22 | }
23 |
24 | public >
25 | void execute(AsyncTask task) {
26 | task.execute(service);
27 | }
28 |
29 | /**
30 | * fire & forget - no synchronization with main thread is done
31 | */
32 | public void executeJob(Runnable job) {
33 | service.execute(job);
34 | }
35 |
36 | @Override
37 | public void dispose() {
38 |
39 | service.shutdown();
40 | GdxSnippets.log.info("Shutting down AsyncTaskExecutor");
41 |
42 | try {
43 | service.awaitTermination(2500, TimeUnit.MILLISECONDS);
44 | } catch (InterruptedException e) {
45 | e.printStackTrace();
46 | }
47 | }
48 |
49 | private static class Factory implements ThreadFactory {
50 |
51 | private final ThreadGroup group;
52 | private final AtomicInteger threadNumber = new AtomicInteger(1);
53 | private final String namePrefix;
54 |
55 | Factory(String prefix) {
56 | group = Thread.currentThread().getThreadGroup();
57 | namePrefix = prefix;
58 | }
59 |
60 | @Override
61 | public Thread newThread(Runnable runnable) {
62 | Thread thread = new Thread(group, runnable, namePrefix + "-" + threadNumber.getAndIncrement());
63 | thread.setDaemon(true);
64 | return thread;
65 | }
66 | }
67 |
68 | private static class FixedThreadPoolExecutor extends ThreadPoolExecutor {
69 |
70 | FixedThreadPoolExecutor(int nThreads, ThreadFactory threadFactory) {
71 | super(nThreads, nThreads,
72 | 0L, TimeUnit.MILLISECONDS,
73 | new LinkedBlockingQueue<>(),
74 | threadFactory);
75 | }
76 |
77 | @Override
78 | protected void afterExecute(Runnable r, Throwable t) {
79 | super.afterExecute(r, t);
80 | if (t == null && r instanceof Future>) {
81 | try {
82 | Object result = ((Future>) r).get();
83 | } catch (CancellationException ce) {
84 | t = ce;
85 | } catch (ExecutionException ee) {
86 | t = ee.getCause();
87 | } catch (InterruptedException ie) {
88 | Thread.currentThread().interrupt(); // ignore/reset
89 | }
90 | }
91 | if (t != null) {
92 | GdxSnippets.log.error("thread pool execution error", t);
93 | }
94 | }
95 | }
96 |
97 | }
98 |
--------------------------------------------------------------------------------
/src/main/java/com/badlogic/gdx/concurrent/AsyncTaskJob.java:
--------------------------------------------------------------------------------
1 | package com.badlogic.gdx.concurrent;
2 |
3 | import java.util.concurrent.Callable;
4 |
5 | public interface AsyncTaskJob extends Callable {
6 |
7 | void completed();
8 |
9 | }
10 |
--------------------------------------------------------------------------------
/src/main/java/com/badlogic/gdx/concurrent/ReentrantLock.java:
--------------------------------------------------------------------------------
1 | package com.badlogic.gdx.concurrent;
2 |
3 | import com.badlogic.gdx.function.Consumer;
4 | import com.badlogic.gdx.function.Supplier;
5 |
6 | import java.util.concurrent.TimeUnit;
7 | import java.util.concurrent.locks.Lock;
8 |
9 | /**
10 | * A functional style wrapper to {@link java.util.concurrent.locks.ReentrantLock}.
11 | */
12 | public class ReentrantLock {
13 |
14 | private final Lock lock = new java.util.concurrent.locks.ReentrantLock();
15 |
16 | public void lock(Runnable runnable) {
17 | try {
18 | lock.lock();
19 | runnable.run();
20 | } finally {
21 | lock.unlock();
22 | }
23 | }
24 |
25 | public void lock(C context, Consumer consumer) {
26 | try {
27 | lock.lock();
28 | consumer.accept(context);
29 | } finally {
30 | lock.unlock();
31 | }
32 | }
33 |
34 | public T lock(Supplier supplier) {
35 | try {
36 | lock.lock();
37 | return supplier.get();
38 | } finally {
39 | lock.unlock();
40 | }
41 | }
42 |
43 | public T tryLock(Supplier supplier, T defaultValue) {
44 | if (!lock.tryLock()) {
45 | return defaultValue;
46 | }
47 | try {
48 | return supplier.get();
49 | } finally {
50 | lock.unlock();
51 | }
52 | }
53 |
54 | public T tryLock(Supplier supplier, T defaultValue, long time, TimeUnit unit) throws InterruptedException {
55 | if (!lock.tryLock(time, unit)) {
56 | return defaultValue;
57 | }
58 | try {
59 | return supplier.get();
60 | } finally {
61 | lock.unlock();
62 | }
63 | }
64 |
65 | }
66 |
--------------------------------------------------------------------------------
/src/main/java/com/badlogic/gdx/concurrent/ReentrantReadWriteLock.java:
--------------------------------------------------------------------------------
1 | package com.badlogic.gdx.concurrent;
2 |
3 | import com.badlogic.gdx.function.Consumer;
4 | import com.badlogic.gdx.function.Supplier;
5 |
6 | import java.util.concurrent.TimeUnit;
7 | import java.util.concurrent.locks.ReadWriteLock;
8 |
9 | /**
10 | * A functional style wrapper to {@link java.util.concurrent.locks.ReentrantReadWriteLock}.
11 | */
12 | public class ReentrantReadWriteLock {
13 |
14 | private final ReadWriteLock lock = new java.util.concurrent.locks.ReentrantReadWriteLock();
15 |
16 | public void read(Runnable runnable) {
17 | try {
18 | lock.readLock().lock();
19 | runnable.run();
20 | } finally {
21 | lock.readLock().unlock();
22 | }
23 | }
24 |
25 | public void read(C context, Consumer consumer) {
26 | try {
27 | lock.readLock().lock();
28 | consumer.accept(context);
29 | } finally {
30 | lock.readLock().unlock();
31 | }
32 | }
33 |
34 | public T read(Supplier supplier) {
35 | try {
36 | lock.readLock().lock();
37 | return supplier.get();
38 | } finally {
39 | lock.readLock().unlock();
40 | }
41 | }
42 |
43 | public T tryRead(Supplier supplier, T defaultValue) {
44 | if (!lock.readLock().tryLock()) {
45 | return defaultValue;
46 | }
47 | try {
48 | return supplier.get();
49 | } finally {
50 | lock.readLock().unlock();
51 | }
52 | }
53 |
54 | public T tryRead(Supplier supplier, T defaultValue, long time, TimeUnit unit) throws InterruptedException {
55 | if (!lock.readLock().tryLock(time, unit)) {
56 | return defaultValue;
57 | }
58 | try {
59 | return supplier.get();
60 | } finally {
61 | lock.readLock().unlock();
62 | }
63 | }
64 |
65 | public void write(Runnable runnable) {
66 | try {
67 | lock.writeLock().lock();
68 | runnable.run();
69 | } finally {
70 | lock.writeLock().unlock();
71 | }
72 | }
73 |
74 | public T write(Supplier supplier) {
75 | try {
76 | lock.writeLock().lock();
77 | return supplier.get();
78 | } finally {
79 | lock.writeLock().unlock();
80 | }
81 | }
82 |
83 | public T tryWrite(Supplier supplier, T defaultValue) {
84 | if (!lock.writeLock().tryLock()) {
85 | return defaultValue;
86 | }
87 | try {
88 | return supplier.get();
89 | } finally {
90 | lock.writeLock().unlock();
91 | }
92 | }
93 |
94 | public T tryWrite(Supplier supplier, T defaultValue, long time, TimeUnit unit) throws InterruptedException {
95 | if (!lock.writeLock().tryLock(time, unit)) {
96 | return defaultValue;
97 | }
98 | try {
99 | return supplier.get();
100 | } finally {
101 | lock.writeLock().unlock();
102 | }
103 | }
104 |
105 | }
106 |
--------------------------------------------------------------------------------
/src/main/java/com/badlogic/gdx/concurrent/ThreadLocalArray.java:
--------------------------------------------------------------------------------
1 | package com.badlogic.gdx.concurrent;
2 |
3 | import com.badlogic.gdx.function.Supplier;
4 | import com.badlogic.gdx.function.ThrowableSupplier;
5 | import com.badlogic.gdx.utils.GdxRuntimeException;
6 | import com.badlogic.gdx.utils.reflect.*;
7 |
8 | /**
9 | * A convenience wrapper to {@link ThreadLocal} storing an array of objects.
10 | *
11 | *
12 | * {@code
13 | * ThreadLocalArray tls = new ThreadLocalArray<>(64, UserObject.class, () -> new UserObject(params...));
14 | * UserObject[] array = tls.get();
15 | * }
16 | *
17 | */
18 | public class ThreadLocalArray implements Supplier {
19 |
20 | private final ThreadLocal tls = new ThreadLocal<>();
21 |
22 | public ThreadLocalArray(int capacity, Class extends T> clazz) {
23 | this(capacity, clazz, () -> ClassReflection.newInstance(clazz));
24 | }
25 |
26 | @SuppressWarnings("unchecked")
27 | public ThreadLocalArray(int capacity, Class extends T> clazz,
28 | ThrowableSupplier initialValueSupplier) {
29 |
30 | try {
31 |
32 | T[] values = (T[]) ArrayReflection.newInstance(clazz, capacity);
33 |
34 | for (int i = 0; i < capacity; i++) {
35 | values[i] = initialValueSupplier.get();
36 | }
37 |
38 | tls.set(values);
39 |
40 | } catch (ReflectionException e) {
41 | throw new GdxRuntimeException(e);
42 | }
43 | }
44 |
45 | @Override
46 | public T[] get() {
47 | return tls.get();
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/main/java/com/badlogic/gdx/concurrent/ThreadLocalInstance.java:
--------------------------------------------------------------------------------
1 | package com.badlogic.gdx.concurrent;
2 |
3 | import com.badlogic.gdx.function.Supplier;
4 |
5 | /**
6 | * Substitute for {@link ThreadLocal#withInitial(Supplier)}
7 | */
8 | public class ThreadLocalInstance extends ThreadLocal {
9 |
10 | private final Supplier extends T> supplier;
11 |
12 | public ThreadLocalInstance(Supplier extends T> supplier) {
13 | this.supplier = supplier;
14 | }
15 |
16 | @Override
17 | protected T initialValue() {
18 | return supplier.get();
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/main/java/com/badlogic/gdx/files/FileStreamConsumer.java:
--------------------------------------------------------------------------------
1 | package com.badlogic.gdx.files;
2 |
3 | import java.io.IOException;
4 |
5 | @FunctionalInterface
6 | public interface FileStreamConsumer {
7 |
8 | void read(byte[] bytes, int length) throws IOException;
9 |
10 | }
11 |
--------------------------------------------------------------------------------
/src/main/java/com/badlogic/gdx/files/FileStreamReader.java:
--------------------------------------------------------------------------------
1 | package com.badlogic.gdx.files;
2 |
3 | import com.badlogic.gdx.checksum.SHA1;
4 |
5 | import java.io.*;
6 |
7 | public final class FileStreamReader {
8 |
9 | /**
10 | * Uses a {@link BufferedInputStream} to consume an {@link InputStream}.
11 | */
12 | public static void readStream(InputStream stream,
13 | int cacheSize,
14 | FileStreamConsumer consumer) throws IOException {
15 |
16 | try (BufferedInputStream bis = new BufferedInputStream(stream)) {
17 |
18 | int n = 0;
19 | byte[] buffer = new byte[cacheSize];
20 |
21 | while (n != -1) {
22 |
23 | n = bis.read(buffer);
24 |
25 | if (n > 0) {
26 | consumer.read(buffer, n);
27 | }
28 | }
29 | }
30 | }
31 |
32 | /**
33 | * Calculates the SHA-1 hash on a {@link InputStream}.
34 | */
35 | public static SHA1 hashStream(InputStream stream) throws IOException {
36 |
37 | SHA1 sha1 = SHA1.create();
38 |
39 | readStream(stream, 1024,
40 | (bytes, n) -> SHA1.update(sha1, bytes, 0, n));
41 |
42 | SHA1.submit(sha1);
43 |
44 | return sha1;
45 | }
46 |
47 | }
48 |
--------------------------------------------------------------------------------
/src/main/java/com/badlogic/gdx/files/FileUtils.java:
--------------------------------------------------------------------------------
1 | package com.badlogic.gdx.files;
2 |
3 | import com.badlogic.gdx.Files.FileType;
4 | import com.badlogic.gdx.utils.Host;
5 |
6 | import java.io.File;
7 | import java.io.IOException;
8 | import java.nio.file.Path;
9 | import java.nio.file.Paths;
10 | import java.util.Arrays;
11 | import java.util.Comparator;
12 | import java.util.regex.Matcher;
13 | import java.util.regex.Pattern;
14 |
15 | import static com.badlogic.gdx.utils.Host.OS.Linux;
16 |
17 | public final class FileUtils {
18 |
19 | /**
20 | * Default comparator to sort {@link FileHandle}s:
21 | * - directories first
22 | * - then by name, case insensitive
23 | */
24 | public static final Comparator DEFAULT_COMPARATOR = (file1, file2) -> {
25 |
26 | boolean d1 = file1.isDirectory();
27 | boolean d2 = file2.isDirectory();
28 |
29 | if (d1 != d2) {
30 | return d1 ? -1 : 1;
31 | }
32 |
33 | return file1.name().compareToIgnoreCase(file2.name());
34 | };
35 |
36 | /**
37 | * Wrapper to {@link FileHandle#list()} which also sorts the result.
38 | */
39 | public static FileHandle[] list(FileHandle folder) {
40 |
41 | FileHandle[] files = folder.list();
42 | Arrays.sort(files, DEFAULT_COMPARATOR);
43 |
44 | return files;
45 | }
46 |
47 | /**
48 | * Wrapper to {@link FileHandle#list(String)} which also sorts the result.
49 | */
50 | public static FileHandle[] list(FileHandle folder, String suffix) {
51 |
52 | FileHandle[] files = folder.list(suffix);
53 | Arrays.sort(files, DEFAULT_COMPARATOR);
54 |
55 | return files;
56 | }
57 |
58 | /**
59 | * Queries (and creates, if necessary) a path to store user files.
60 | *
61 | * Linux:
62 | * - $XDG_DATA_HOME
63 | * - user.home/.local/share
64 | * - $HOME/.local/share
65 | * MacOS:
66 | * - $XDG_DATA_HOME
67 | * - user.home/Library/Application Support
68 | * - $HOME/Library/Application Support
69 | * Windows:
70 | * - %LOCALAPPDATA%
71 | * - %APPDATA%
72 | */
73 | public static FileHandle getUserFolder(String companyIdentifier,
74 | String... productFolders) throws IOException {
75 |
76 | Path path = null;
77 |
78 | switch (Host.os) {
79 |
80 | case Linux: {
81 |
82 | // $XDG_DATA_HOME
83 |
84 | String dataDir = System.getenv("XDG_DATA_HOME");
85 |
86 | if (dataDir != null && !dataDir.isEmpty()) {
87 |
88 | path = Paths.get(dataDir);
89 |
90 | if (path.toFile().isDirectory()) {
91 | break;
92 | }
93 |
94 | path = null;
95 | }
96 |
97 | // [user.home | $HOME]/.local/share
98 |
99 | String homeDir = System.getProperty("user.home");
100 |
101 | if (homeDir == null || homeDir.isEmpty()) {
102 | homeDir = System.getenv("HOME");
103 | }
104 |
105 | if (homeDir != null && !homeDir.isEmpty()) {
106 |
107 | path = Paths.get(homeDir, ".local", "share");
108 |
109 | if (path.toFile().isDirectory()) {
110 | break;
111 | }
112 |
113 | path = null;
114 | }
115 |
116 | break;
117 | }
118 |
119 | case MacOS: {
120 |
121 | // $XDG_DATA_HOME
122 |
123 | String dataDir = System.getenv("XDG_DATA_HOME");
124 |
125 | if (dataDir != null && !dataDir.isEmpty()) {
126 |
127 | path = Paths.get(dataDir);
128 |
129 | if (path.toFile().isDirectory()) {
130 | break;
131 | }
132 |
133 | path = null;
134 | }
135 |
136 | // [user.home | $HOME]/Library/"Application Support"
137 |
138 | String homeDir = System.getProperty("user.home");
139 |
140 | if (homeDir == null || homeDir.isEmpty()) {
141 | homeDir = System.getenv("HOME");
142 | }
143 |
144 | if (homeDir != null && !homeDir.isEmpty()) {
145 |
146 | path = Paths.get(homeDir, "Library", "Application Support");
147 |
148 | if (path.toFile().isDirectory()) {
149 | break;
150 | }
151 |
152 | path = null;
153 | }
154 |
155 | break;
156 | }
157 |
158 | case Windows:
159 |
160 | // [%LOCALAPPDATA% | %APPDATA%]
161 |
162 | String appData = System.getenv("LOCALAPPDATA");
163 |
164 | if (appData == null || appData.isEmpty()) {
165 | appData = System.getenv("APPDATA");
166 | }
167 |
168 | if (appData != null && !appData.isEmpty()) {
169 |
170 | path = Paths.get(appData);
171 |
172 | if (path.toFile().isDirectory()) {
173 | break;
174 | }
175 |
176 | path = null;
177 | }
178 |
179 | break;
180 | }
181 |
182 | if (path == null) {
183 | throw new IOException("no valid user data folder found");
184 | }
185 |
186 | // company sub-folder
187 |
188 | path = path.resolve(Host.os == Linux ? companyIdentifier.toLowerCase() : companyIdentifier);
189 |
190 | // append remaining user-defined folders
191 |
192 | for (String folder : productFolders) {
193 | path = path.resolve(folder);
194 | }
195 |
196 | // create if necessary
197 |
198 | FileHandle fileHandle = newFileHandle(path.toFile(), FileType.Absolute);
199 |
200 | if (!fileHandle.exists()) {
201 | fileHandle.mkdirs();
202 | }
203 |
204 | return fileHandle;
205 | }
206 |
207 | public static FileHandle newFileHandle(File file, FileType type) {
208 | return new FileHandleHelper(file, type);
209 | }
210 |
211 | /**
212 | * Normalizes the path of a file handle.
213 | *
214 | * This does some hoops to work around some restrictions of the {@link FileHandle} class.
215 | */
216 | public static FileHandle normalize(FileHandle file) {
217 | return new FileHandleHelper(file).normalize();
218 | }
219 |
220 | private static class FileHandleHelper extends FileHandle {
221 |
222 | private static final Pattern relative = Pattern.compile("(/[a-zA-Z0-9\\-_]+/\\.\\.)");
223 |
224 | FileHandleHelper(FileHandle file) {
225 | super(file.file(), file.type());
226 | }
227 |
228 | FileHandleHelper(File file, FileType type) {
229 | super(file, type);
230 | }
231 |
232 | FileHandleHelper normalize() {
233 | String path = file.getPath();
234 | if (path.startsWith("..")) {
235 | throw new IllegalStateException();
236 | }
237 | // normalize path separators first
238 | path = path.replaceAll("\\\\", "/");
239 | // extract any "//.."
240 | for (;;) {
241 | Matcher matcher = relative.matcher(path);
242 | if (!matcher.find()) {
243 | break;
244 | }
245 | String match = matcher.group(0);
246 | path = path.replace(match, "");
247 | }
248 | file = new File(path);
249 | return this;
250 | }
251 | }
252 |
253 | }
254 |
--------------------------------------------------------------------------------
/src/main/java/com/badlogic/gdx/files/TextFileUtils.java:
--------------------------------------------------------------------------------
1 | package com.badlogic.gdx.files;
2 |
3 | import com.badlogic.gdx.function.ThrowableConsumer;
4 | import com.badlogic.gdx.utils.*;
5 | import com.badlogic.gdx.utils.StringBuilder;
6 |
7 | import java.io.*;
8 | import java.util.regex.Pattern;
9 |
10 | public class TextFileUtils {
11 |
12 | /**
13 | * Reads the file as text, passing its content to the consumer function, line by line.
14 | */
15 | public static void readLines(FileHandle file,
16 | ThrowableConsumer consumer) throws IOException {
17 |
18 | readLines(file, null, consumer);
19 | }
20 |
21 | /**
22 | * Reads the file as text, filtering each line by a user-defined set of RegEx patterns.
23 | * The line is passed to the consumer function only if (at least) one of the patterns matches.
24 | */
25 | public static void readLines(FileHandle file, String[] patterns,
26 | ThrowableConsumer consumer) throws IOException {
27 |
28 | Pattern[] p = null;
29 | int lineNo = 0;
30 |
31 | if (!ArrayUtils.isNullOrEmpty(patterns)) {
32 | p = new Pattern[patterns.length];
33 | for (int i = 0; i < patterns.length; i++) {
34 | p[i] = Pattern.compile(patterns[i]);
35 | }
36 | }
37 |
38 | try (BufferedReader reader = new BufferedReader(file.reader())) {
39 |
40 | String line;
41 | while ((line = reader.readLine()) != null) {
42 |
43 | lineNo++;
44 |
45 | if (p == null) {
46 | consumer.accept(line);
47 | } else {
48 | for (int i = 0; i < patterns.length; i++) {
49 | if (p[i].matcher(line).matches()) {
50 | consumer.accept(line);
51 | break;
52 | }
53 | }
54 | }
55 | }
56 |
57 | } catch (IOException e) {
58 | throw new IOException("Error reading " + file.path() + " at line #" + lineNo, e);
59 | }
60 | }
61 |
62 | public static String readString(FileHandle file) throws IOException {
63 |
64 | StringBuilder builder = new StringBuilder((int) file.length());
65 |
66 | readLines(file, line -> {
67 |
68 | if (builder.length() > 0) {
69 | builder.append('\n');
70 | }
71 |
72 | builder.append(line);
73 | });
74 |
75 | return builder.toString();
76 | }
77 |
78 | public static void writeString(FileHandle file, String text) throws IOException {
79 |
80 | StringBuilder builder = new StringBuilder(text.length());
81 | String newLine = Host.os != Host.OS.Windows ? "\n" : "\r\n";
82 |
83 | try (BufferedReader reader = new BufferedReader(new StringReader(text))) {
84 |
85 | String line;
86 | while ((line = reader.readLine()) != null) {
87 |
88 | if (builder.length() > 0) {
89 | builder.append(newLine);
90 | }
91 |
92 | builder.append(line);
93 | }
94 | }
95 |
96 | file.writeString(builder.toString(), false);
97 | }
98 |
99 | }
100 |
--------------------------------------------------------------------------------
/src/main/java/com/badlogic/gdx/function/BiConsumer.java:
--------------------------------------------------------------------------------
1 | package com.badlogic.gdx.function;
2 |
3 | @FunctionalInterface
4 | public interface BiConsumer {
5 | void accept(T t, U u);
6 | }
7 |
--------------------------------------------------------------------------------
/src/main/java/com/badlogic/gdx/function/BiFunction.java:
--------------------------------------------------------------------------------
1 | package com.badlogic.gdx.function;
2 |
3 | @FunctionalInterface
4 | public interface BiFunction {
5 | R apply(T t, U u);
6 | }
7 |
--------------------------------------------------------------------------------
/src/main/java/com/badlogic/gdx/function/BiPredicate.java:
--------------------------------------------------------------------------------
1 | package com.badlogic.gdx.function;
2 |
3 | @FunctionalInterface
4 | public interface BiPredicate {
5 | boolean test(T t, U u);
6 | }
7 |
--------------------------------------------------------------------------------
/src/main/java/com/badlogic/gdx/function/BooleanSupplier.java:
--------------------------------------------------------------------------------
1 | package com.badlogic.gdx.function;
2 |
3 | @FunctionalInterface
4 | public interface BooleanSupplier {
5 | boolean getAsBoolean();
6 | }
7 |
--------------------------------------------------------------------------------
/src/main/java/com/badlogic/gdx/function/Consumer.java:
--------------------------------------------------------------------------------
1 | package com.badlogic.gdx.function;
2 |
3 | @FunctionalInterface
4 | public interface Consumer {
5 | void accept(T t);
6 | }
7 |
--------------------------------------------------------------------------------
/src/main/java/com/badlogic/gdx/function/Function.java:
--------------------------------------------------------------------------------
1 | package com.badlogic.gdx.function;
2 |
3 | @FunctionalInterface
4 | public interface Function {
5 | R apply(T t);
6 | }
7 |
--------------------------------------------------------------------------------
/src/main/java/com/badlogic/gdx/function/Functions.java:
--------------------------------------------------------------------------------
1 | package com.badlogic.gdx.function;
2 |
3 | import java.util.Comparator;
4 |
5 | public final class Functions {
6 |
7 | public static final Runnable NOOP_RUNNABLE = () -> {
8 | };
9 |
10 | @SuppressWarnings("ComparatorCombinators")
11 | public static > Comparator comparing(
12 | Function super T, ? extends U> fn) {
13 | return (o1, o2) -> fn.apply(o1).compareTo(fn.apply(o2));
14 | }
15 |
16 | @SuppressWarnings("ComparatorCombinators")
17 | public static Comparator comparingInt(ToIntFunction fn) {
18 | return (o1, o2) -> Integer.compare(fn.applyAsInt(o1), fn.applyAsInt(o2));
19 | }
20 |
21 | public static Comparator comparingIntDescending(ToIntFunction fn) {
22 | return (o1, o2) -> Integer.compare(fn.applyAsInt(o2), fn.applyAsInt(o1));
23 | }
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/src/main/java/com/badlogic/gdx/function/IntConsumer.java:
--------------------------------------------------------------------------------
1 | package com.badlogic.gdx.function;
2 |
3 | @FunctionalInterface
4 | public interface IntConsumer {
5 | void accept(int value);
6 | }
7 |
--------------------------------------------------------------------------------
/src/main/java/com/badlogic/gdx/function/IntFunction.java:
--------------------------------------------------------------------------------
1 | package com.badlogic.gdx.function;
2 |
3 | @FunctionalInterface
4 | public interface IntFunction {
5 | R apply(int value);
6 | }
7 |
--------------------------------------------------------------------------------
/src/main/java/com/badlogic/gdx/function/IntObjConsumer.java:
--------------------------------------------------------------------------------
1 | package com.badlogic.gdx.function;
2 |
3 | /**
4 | * Similar to {@link java.util.function.ObjIntConsumer}.
5 | */
6 | @FunctionalInterface
7 | public interface IntObjConsumer {
8 |
9 | void accept(int value, T t);
10 | }
11 |
--------------------------------------------------------------------------------
/src/main/java/com/badlogic/gdx/function/IntPredicate.java:
--------------------------------------------------------------------------------
1 | package com.badlogic.gdx.function;
2 |
3 | @FunctionalInterface
4 | public interface IntPredicate {
5 | boolean test(int value);
6 | }
7 |
--------------------------------------------------------------------------------
/src/main/java/com/badlogic/gdx/function/Iterables.java:
--------------------------------------------------------------------------------
1 | package com.badlogic.gdx.function;
2 |
3 | public final class Iterables {
4 |
5 | /**
6 | * Substitute for {@link Iterable#forEach(java.util.function.Consumer)}.
7 | */
8 | public static void forEach(Iterable iterable, Consumer super T> action) {
9 | for (T t : iterable) {
10 | action.accept(t);
11 | }
12 | }
13 |
14 | }
15 |
--------------------------------------------------------------------------------
/src/main/java/com/badlogic/gdx/function/Predicate.java:
--------------------------------------------------------------------------------
1 | package com.badlogic.gdx.function;
2 |
3 | @FunctionalInterface
4 | public interface Predicate {
5 | boolean test(T t);
6 | }
7 |
--------------------------------------------------------------------------------
/src/main/java/com/badlogic/gdx/function/Supplier.java:
--------------------------------------------------------------------------------
1 | package com.badlogic.gdx.function;
2 |
3 | @FunctionalInterface
4 | public interface Supplier {
5 | T get();
6 | }
7 |
--------------------------------------------------------------------------------
/src/main/java/com/badlogic/gdx/function/ThrowableBiFunction.java:
--------------------------------------------------------------------------------
1 | package com.badlogic.gdx.function;
2 |
3 | @FunctionalInterface
4 | public interface ThrowableBiFunction {
5 |
6 | R apply(T t, U u) throws E;
7 | }
8 |
--------------------------------------------------------------------------------
/src/main/java/com/badlogic/gdx/function/ThrowableConsumer.java:
--------------------------------------------------------------------------------
1 | package com.badlogic.gdx.function;
2 |
3 | /**
4 | * Version of {@link Consumer} which can throw an exception.
5 | *
6 | * @param the type of parameter passed to this consumer
7 | * @param the type of exception to be handled
8 | */
9 | @FunctionalInterface
10 | public interface ThrowableConsumer {
11 | void accept(T t) throws E;
12 | }
13 |
--------------------------------------------------------------------------------
/src/main/java/com/badlogic/gdx/function/ThrowableFunction.java:
--------------------------------------------------------------------------------
1 | package com.badlogic.gdx.function;
2 |
3 | /**
4 | * Version of {@link Function} which can throw an exception.
5 | *
6 | * @param the type of parameter passed to this consumer
7 | * @param the type of the function result
8 | * @param the type of exception to be handled
9 | */
10 | @FunctionalInterface
11 | public interface ThrowableFunction {
12 |
13 | R apply(T t) throws E;
14 | }
15 |
--------------------------------------------------------------------------------
/src/main/java/com/badlogic/gdx/function/ThrowableSupplier.java:
--------------------------------------------------------------------------------
1 | package com.badlogic.gdx.function;
2 |
3 | /**
4 | * A variant of {@link Supplier} which can throw an exception.
5 | *
6 | * @param the type of result supplied by this supplier
7 | * @param the type of exception to be handled
8 | */
9 | @FunctionalInterface
10 | public interface ThrowableSupplier {
11 | T get() throws E;
12 | }
13 |
--------------------------------------------------------------------------------
/src/main/java/com/badlogic/gdx/function/ToBooleanFunction.java:
--------------------------------------------------------------------------------
1 | package com.badlogic.gdx.function;
2 |
3 | /**
4 | * Represents a function that produces an boolean-valued result. This is a
5 | * {@code boolean}-producing primitive specialization for {@link Function}.
6 | *
7 | * @param the type of the input to the function
8 | * @see Function
9 | */
10 | @FunctionalInterface
11 | public interface ToBooleanFunction {
12 |
13 | /**
14 | * Applies this function to the given argument.
15 | *
16 | * @param value the function argument
17 | * @return the function result
18 | */
19 | boolean applyAsBoolean(T value);
20 | }
21 |
--------------------------------------------------------------------------------
/src/main/java/com/badlogic/gdx/function/ToByteBiFunction.java:
--------------------------------------------------------------------------------
1 | package com.badlogic.gdx.function;
2 |
3 | @FunctionalInterface
4 | public interface ToByteBiFunction {
5 |
6 | /**
7 | * Applies this function to the given arguments.
8 | */
9 | byte applyAsByte(T t, U u);
10 | }
11 |
--------------------------------------------------------------------------------
/src/main/java/com/badlogic/gdx/function/ToIntFunction.java:
--------------------------------------------------------------------------------
1 | package com.badlogic.gdx.function;
2 |
3 | @FunctionalInterface
4 | public interface ToIntFunction {
5 | int applyAsInt(T t);
6 | }
7 |
--------------------------------------------------------------------------------
/src/main/java/com/badlogic/gdx/graphics/GL33Ext.java:
--------------------------------------------------------------------------------
1 | package com.badlogic.gdx.graphics;
2 |
3 | import com.badlogic.gdx.Gdx;
4 | import com.badlogic.gdx.utils.GdxSnippets;
5 |
6 | import java.nio.LongBuffer;
7 |
8 | /**
9 | * Extension to Gdx.gl30 which adds functions and constants not available to OpenGL ES, and
10 | * therefore not exposed through the libGDX interface.
11 | */
12 | public final class GL33Ext {
13 |
14 | public static final int GL_TEXTURE_BORDER_COLOR = 0x1004;
15 |
16 | public static final int GL_POINT = 0x1B00;
17 | public static final int GL_LINE = 0x1B01;
18 | public static final int GL_FILL = 0x1B02;
19 |
20 | public static final int GL_CLAMP_TO_BORDER = 0x812D;
21 |
22 | public static final int GL_CLIP_DISTANCE0 = 0x3000;
23 | public static final int GL_CLIP_DISTANCE1 = 0x3001;
24 | public static final int GL_CLIP_DISTANCE2 = 0x3002;
25 | public static final int GL_CLIP_DISTANCE3 = 0x3003;
26 | public static final int GL_CLIP_DISTANCE4 = 0x3004;
27 | public static final int GL_CLIP_DISTANCE5 = 0x3005;
28 | public static final int GL_CLIP_DISTANCE6 = 0x3006;
29 | public static final int GL_CLIP_DISTANCE7 = 0x3007;
30 |
31 | public static final int GL_INTERNALFORMAT_SUPPORTED = 0x826F;
32 | public static final int GL_INTERNALFORMAT_PREFERRED = 0x8270;
33 |
34 | public static void glBlendEquationi(int buffer, int mode) {
35 |
36 | if (!Gdx.graphics.supportsExtension("GL_ARB_draw_buffers_blend")) {
37 | GdxSnippets.log.warn("Extension ARB_draw_buffers_blend not supported!");
38 | }
39 |
40 | nglBlendEquationi(buffer, mode);
41 | }
42 |
43 | public static void glGetInternalFormativ(int target, int internalformat, int pname, LongBuffer params) {
44 |
45 | if (!Gdx.graphics.supportsExtension("GL_ARB_internalformat_query2")) {
46 | GdxSnippets.log.warn("Extension ARB_internalformat_query2 not supported!");
47 | }
48 |
49 | nglGetInternalFormati64v(target, internalformat, pname, params.capacity(), params);
50 | }
51 |
52 | // @off
53 |
54 | /*JNI
55 | #include "flextGL.h"
56 | */
57 |
58 | public static native void setupGL(boolean setupGLBindings); /*
59 | if (setupGLBindings) {
60 | flextInit();
61 | }
62 | */
63 |
64 | public static native void glBindFragDataLocation(int program, int colorNumber, String name); /*
65 | glBindFragDataLocation(program, colorNumber, name);
66 | */
67 |
68 | private static native void nglBlendEquationi(int buffer, int mode); /*
69 | if (FLEXT_ARB_draw_buffers_blend) {
70 | glpfBlendEquationiARB(buffer, mode);
71 | }
72 | */
73 |
74 | public static native void glColorMaski(int buffer, boolean r, boolean g, boolean b, boolean a); /*
75 | glColorMaski(buffer, r, g, b, a);
76 | */
77 |
78 | public static native void glDrawElementsBaseVertex(int mode, int count, int type, int indices, int baseVertex); /*
79 | glDrawElementsBaseVertex(mode, count, type, (const void*) ((size_t) indices), baseVertex);
80 | */
81 |
82 | private static native void nglGetInternalFormati64v(int target, int internalformat,
83 | int pname, int bufSize, LongBuffer params); /*
84 | if (FLEXT_ARB_internalformat_query2) {
85 | glGetInternalformati64v(target, internalformat, pname, bufSize, (GLint64*) params);
86 | }
87 | */
88 |
89 | public static native void glPolygonMode(int face, int mode); /*
90 | glPolygonMode(face, mode);
91 | */
92 |
93 | }
94 |
--------------------------------------------------------------------------------
/src/main/java/com/badlogic/gdx/graphics/TextureUtils.java:
--------------------------------------------------------------------------------
1 | package com.badlogic.gdx.graphics;
2 |
3 | import com.badlogic.gdx.Gdx;
4 | import com.badlogic.gdx.function.Consumer;
5 | import com.badlogic.gdx.utils.Array;
6 |
7 | public class TextureUtils {
8 |
9 | /**
10 | * Calls consumer on each texture currently registered as managed texture.
11 | */
12 | public static void forEachManagedTexture(Consumer consumer) {
13 | Array textures = Texture.managedTextures.get(Gdx.app);
14 | for (int i = textures.size - 1; i >= 0; i--) {
15 | consumer.accept(textures.get(i));
16 | }
17 | }
18 |
19 | /**
20 | * Disposes all textures still registered as managed textures.
21 | */
22 | public static void disposeAllManagedTextures() {
23 | // works because forEachManagedTexture() iterates in reverse order
24 | forEachManagedTexture(Texture::dispose);
25 | }
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/src/main/java/com/badlogic/gdx/graphics/g2d/PixmapAtlas.java:
--------------------------------------------------------------------------------
1 | package com.badlogic.gdx.graphics.g2d;
2 |
3 | import com.badlogic.gdx.files.FileHandle;
4 | import com.badlogic.gdx.function.Iterables;
5 | import com.badlogic.gdx.graphics.Pixmap;
6 | import com.badlogic.gdx.utils.*;
7 |
8 | /**
9 | * Loads images from texture atlas, just like {@link TextureAtlas}, but stores them in pixmaps instead of textures
10 | * to avoid creation of texture resources.
11 | *
12 | * Internally uses {@link com.badlogic.gdx.graphics.g2d.TextureAtlas.TextureAtlasData} to avoid code duplication.
13 | */
14 | public class PixmapAtlas implements Disposable {
15 |
16 | private final ObjectSet pixmaps = new ObjectSet<>(4);
17 | private final Array pages = new Array<>();
18 | private final Array regions = new Array<>();
19 |
20 | public PixmapAtlas(FileHandle packFile) {
21 | this(packFile, packFile.parent(), false);
22 | }
23 |
24 | public PixmapAtlas(FileHandle packFile, PixmapReader reader) {
25 | load(new TextureAtlas.TextureAtlasData(packFile, packFile.parent(), false), reader);
26 | }
27 |
28 | public PixmapAtlas(FileHandle packFile, FileHandle imagesDir, boolean flip) {
29 | load(new TextureAtlas.TextureAtlasData(packFile, imagesDir, flip), DEFAULT_READER);
30 | }
31 |
32 | public PixmapAtlas(TextureAtlas.TextureAtlasData data) {
33 | load(data, DEFAULT_READER);
34 | }
35 |
36 | private PixmapAtlas() {
37 |
38 | }
39 |
40 | private void load(TextureAtlas.TextureAtlasData data, PixmapReader reader) {
41 |
42 | ObjectMap pageToPixmap = new ObjectMap<>();
43 |
44 | for (int pageIndex = 0; pageIndex < data.pages.size; pageIndex++) {
45 |
46 | TextureAtlas.TextureAtlasData.Page page = data.pages.get(pageIndex);
47 |
48 | Pixmap pixmap = reader.read(page.textureFile, (int) page.width, (int) page.height);
49 | pixmaps.add(pixmap);
50 |
51 | AtlasPage atlasPage = new AtlasPage(pageIndex, page.textureFile, pixmap);
52 | pages.add(atlasPage);
53 |
54 | pageToPixmap.put(page, atlasPage);
55 | }
56 |
57 | for (TextureAtlas.TextureAtlasData.Region region : data.regions) {
58 |
59 | int width = region.width;
60 | int height = region.height;
61 |
62 | AtlasPage atlasPage = pageToPixmap.get(region.page);
63 |
64 | AtlasRegion atlasRegion = new AtlasRegion(
65 | atlasPage.pixmap,
66 | region.left,
67 | region.top,
68 | region.rotate ? height : width,
69 | region.rotate ? width : height);
70 |
71 | atlasRegion.page = atlasPage;
72 | atlasRegion.index = region.index;
73 | atlasRegion.name = region.name;
74 | atlasRegion.rotate = region.rotate;
75 |
76 | regions.add(atlasRegion);
77 | }
78 | }
79 |
80 | @Override
81 | public void dispose() {
82 | Iterables.forEach(pixmaps, Pixmap::dispose);
83 | pixmaps.clear();
84 | pages.clear();
85 | regions.clear();
86 | }
87 |
88 | public ObjectSet getPixmaps() {
89 | return pixmaps;
90 | }
91 |
92 | public Iterable getPages() {
93 | return pages;
94 | }
95 |
96 | public AtlasRegion findRegion(String name) {
97 | for (AtlasRegion region : regions) {
98 | if (region.name.equals(name)) {
99 | return region;
100 | }
101 | }
102 | return null;
103 | }
104 |
105 | public FileHandle getFolder() {
106 | return pages.get(0).fileHandle.parent();
107 | }
108 |
109 | public static PixmapAtlas createFromPixmap(FileHandle pixmapFile, String regionName) {
110 |
111 | Pixmap pixmap = new Pixmap(pixmapFile);
112 |
113 | PixmapAtlas atlas = new PixmapAtlas();
114 |
115 | // add single pixmap
116 | atlas.pixmaps.add(pixmap);
117 |
118 | // add one page
119 | AtlasPage atlasPage = new AtlasPage(0, pixmapFile, pixmap);
120 | atlas.pages.add(atlasPage);
121 |
122 | // add one region
123 | int width = pixmap.getWidth();
124 | int height = pixmap.getHeight();
125 |
126 | AtlasRegion atlasRegion = new AtlasRegion(pixmap, 0, 0, width, height);
127 |
128 | atlasRegion.page = atlasPage;
129 | atlasRegion.index = 0;
130 | atlasRegion.name = regionName;
131 | atlasRegion.rotate = false;
132 |
133 | atlas.regions.add(atlasRegion);
134 |
135 | return atlas;
136 | }
137 |
138 | public static class AtlasRegion extends PixmapRegion {
139 |
140 | public AtlasPage page;
141 |
142 | // todo: "copy" required fields from TextureAtlas$AtlasRegion
143 | int index;
144 | String name;
145 | boolean rotate;
146 |
147 | AtlasRegion(Pixmap pixmap, int x, int y, int width, int height) {
148 | super(pixmap, x, y, width, height);
149 | }
150 | }
151 |
152 | public static class AtlasPage {
153 |
154 | public final int pageIndex;
155 | public final FileHandle fileHandle;
156 | public final Pixmap pixmap;
157 |
158 | AtlasPage(int pageIndex, FileHandle fileHandle, Pixmap pixmap) {
159 | this.pageIndex = pageIndex;
160 | this.fileHandle = fileHandle;
161 | this.pixmap = pixmap;
162 | }
163 | }
164 |
165 | @FunctionalInterface
166 | public interface PixmapReader {
167 |
168 | Pixmap read(FileHandle file, int width, int height);
169 | }
170 |
171 | private static PixmapReader DEFAULT_READER = (file, width, height) -> new Pixmap(file);
172 |
173 | }
174 |
--------------------------------------------------------------------------------
/src/main/java/com/badlogic/gdx/graphics/g2d/PixmapRegion.java:
--------------------------------------------------------------------------------
1 | package com.badlogic.gdx.graphics.g2d;
2 |
3 | import com.badlogic.gdx.graphics.Pixmap;
4 | import com.badlogic.gdx.utils.GdxRuntimeException;
5 |
6 | import java.nio.ByteBuffer;
7 |
8 | /**
9 | * Defines a rectangular area of a {@link Pixmap}.
10 | */
11 | public class PixmapRegion {
12 |
13 | private Pixmap pixmap;
14 | private ByteBuffer pixels;
15 | private int x, y;
16 | private int width, height;
17 | private int pixelStride, lineStride;
18 |
19 | public PixmapRegion() {
20 |
21 | }
22 |
23 | public PixmapRegion(Pixmap pixmap) {
24 | this(pixmap, 0, 0, pixmap.getWidth(), pixmap.getHeight());
25 | }
26 |
27 | public PixmapRegion(PixmapRegion region) {
28 | setRegion(region);
29 | }
30 |
31 | public PixmapRegion(PixmapRegion region, int x, int y, int width, int height) {
32 | this(region.pixmap, region.x + x, region.y + y, width, height);
33 | }
34 |
35 | public PixmapRegion(Pixmap pixmap, int x, int y, int width, int height) {
36 | setRegion(pixmap, x, y, width, height);
37 | }
38 |
39 | public PixmapRegion(Pixmap pixmap, PixmapRegion region) {
40 | setRegion(pixmap, region.x, region.y, region.width, region.height);
41 | }
42 |
43 | private void changePixmap(Pixmap pixmap) {
44 | this.pixmap = pixmap;
45 | pixels = pixmap.getPixels();
46 | pixelStride = PixmapUtils.getPixelStride(pixmap.getFormat());
47 | lineStride = pixmap.getWidth() * pixelStride;
48 | }
49 |
50 | public Pixmap getPixmap() {
51 | return pixmap;
52 | }
53 |
54 | public int getPixel(int x, int y) {
55 | if (pixelStride != 4) {
56 | throw new GdxRuntimeException("Unsupported format");
57 | }
58 | if (x < 0 || x >= width || y < 0 || y >= height) {
59 | throw new GdxRuntimeException("Out of bounds");
60 | }
61 | int offset = (this.y + y) * lineStride + (this.x + x) * pixelStride;
62 | return pixels.getInt(offset);
63 | }
64 |
65 | public int getPixel(int x, int y, boolean flipX, boolean flipY) {
66 | return getPixel(
67 | flipX ? width - 1 - x : x,
68 | flipY ? height - 1 - y : y);
69 | }
70 |
71 | public int getPixelSlow(int x, int y) {
72 | if (pixelStride < 3) {
73 | throw new GdxRuntimeException("Unsupported format");
74 | }
75 |
76 | int offset = (this.y + y) * lineStride + (this.x + x) * pixelStride;
77 | int r = Byte.toUnsignedInt(pixels.get(offset));
78 | int g = Byte.toUnsignedInt(pixels.get(offset + 1));
79 | int b = Byte.toUnsignedInt(pixels.get(offset + 2));
80 |
81 | int a;
82 | if (pixelStride < 4) {
83 | a = (r + g + b) >= 4 ? 0xff : 0;
84 | } else {
85 | a = pixels.get(offset + 3);
86 | }
87 |
88 | return (r << 24) | (g << 16) | (b << 8) | a;
89 | }
90 |
91 | public void drawPixel(int x, int y, int color) {
92 | if (pixelStride != 4) {
93 | throw new GdxRuntimeException("Unsupported format");
94 | }
95 | int offset = (this.y + y) * lineStride + (this.x + x) * pixelStride;
96 | pixels.putInt(offset, color);
97 | }
98 |
99 | public void setRegion(PixmapRegion region) {
100 | changePixmap(region.pixmap);
101 | this.x = region.x;
102 | this.y = region.y;
103 | this.width = region.width;
104 | this.height = region.height;
105 | }
106 |
107 | public void setRegion(Pixmap pixmap, int x, int y, int width, int height) {
108 | changePixmap(pixmap);
109 | this.x = x;
110 | this.y = y;
111 | this.width = width;
112 | this.height = height;
113 | }
114 |
115 | public int getRegionX() {
116 | return x;
117 | }
118 |
119 | public int getRegionY() {
120 | return y;
121 | }
122 |
123 | public int getRegionWidth() {
124 | return width;
125 | }
126 |
127 | public int getRegionHeight() {
128 | return height;
129 | }
130 |
131 | /**
132 | * Copies region UV data into {@link TextureRegion} instance.
133 | *
134 | * Does not modify {@link TextureRegion#texture}.
135 | */
136 | public void getRegion(TextureRegion region) {
137 |
138 | float invTexWidth = 1.0f / pixmap.getWidth();
139 | float invTexHeight = 1.0f / pixmap.getHeight();
140 |
141 | region.u = x * invTexWidth;
142 | region.u2 = (x + width) * invTexWidth;
143 |
144 | region.v = y * invTexHeight;
145 | region.v2 = (y + height) * invTexHeight;
146 |
147 | region.regionWidth = width;
148 | region.regionHeight = height;
149 | }
150 |
151 | public Pixmap.Format getFormat() {
152 | return pixmap.getFormat();
153 | }
154 |
155 | @Override
156 | public boolean equals(Object other) {
157 |
158 | if (other instanceof PixmapRegion) {
159 |
160 | PixmapRegion region = (PixmapRegion) other;
161 |
162 | return (this.pixmap == region.pixmap)
163 | && (this.x == region.x) && (this.y == region.y)
164 | && (this.width == region.width) && (this.height == region.height);
165 | }
166 |
167 | return false;
168 | }
169 |
170 | @Override
171 | public int hashCode() {
172 | assert false : "not implemented";
173 | return -1;
174 | }
175 |
176 | }
177 |
--------------------------------------------------------------------------------
/src/main/java/com/badlogic/gdx/graphics/g2d/PixmapTransform.java:
--------------------------------------------------------------------------------
1 | package com.badlogic.gdx.graphics.g2d;
2 |
3 | import com.badlogic.gdx.graphics.Pixmap;
4 |
5 | public class PixmapTransform {
6 |
7 | public enum Mirror {
8 | None,
9 | X,
10 | Y,
11 | XY
12 | }
13 |
14 | public enum Rotate {
15 | None,
16 | _90,
17 | _180,
18 | _270
19 | }
20 |
21 | /**
22 | * Read pixel from {@link Pixmap}, using back-transformed flipped and rotated pixel coordinates.
23 | */
24 | public static int getPixel(Pixmap pixmap, int x, int y, Mirror mirror, Rotate rotate) {
25 |
26 | int widthMinusOne = pixmap.getWidth() - 1;
27 | int heightMinusOne = pixmap.getHeight() - 1;
28 |
29 | int px = x, py = y;
30 | int outX, outY;
31 |
32 | // mirror
33 |
34 | if (mirror == Mirror.X || mirror == Mirror.XY) {
35 | px = widthMinusOne - x;
36 | }
37 |
38 | if (mirror == Mirror.Y || mirror == Mirror.XY) {
39 | py = heightMinusOne - y;
40 | }
41 |
42 | // rotate
43 |
44 | switch (rotate) {
45 | case _90:
46 | outX = py;
47 | outY = heightMinusOne - px;
48 | break;
49 | case _180:
50 | outX = widthMinusOne - px;
51 | outY = heightMinusOne - py;
52 | break;
53 | case _270:
54 | outX = widthMinusOne - py;
55 | outY = px;
56 | break;
57 | default:
58 | case None:
59 | outX = px;
60 | outY = py;
61 | break;
62 | }
63 |
64 | return pixmap.getPixel(outX, outY);
65 | }
66 |
67 | public static int getPixelRotated(Pixmap pixmap, int x, int y, Rotate rotate) {
68 | int widthMinusOne = pixmap.getWidth() - 1;
69 | int heightMinusOne = pixmap.getHeight() - 1;
70 |
71 | int px = x, py = y;
72 | int outX, outY;
73 |
74 |
75 |
76 | // rotate
77 |
78 | switch (rotate) {
79 | case _90:
80 | outX = py;
81 | outY = heightMinusOne - px;
82 | break;
83 | case _180:
84 | outX = widthMinusOne - px;
85 | outY = heightMinusOne - py;
86 | break;
87 | case _270:
88 | outX = widthMinusOne - py;
89 | outY = px;
90 | break;
91 | default:
92 | case None:
93 | outX = px;
94 | outY = py;
95 | break;
96 | }
97 |
98 | return pixmap.getPixel(outX, outY);
99 | }
100 |
101 | public static int getPixelMirrored(Pixmap pixmap, int x, int y, Mirror mirror) {
102 | int widthMinusOne = pixmap.getWidth() - 1;
103 | int heightMinusOne = pixmap.getHeight() - 1;
104 |
105 | int px = x, py = y;
106 |
107 | if (mirror == Mirror.X || mirror == Mirror.XY) {
108 | px = widthMinusOne - x;
109 | }
110 |
111 | if (mirror == Mirror.Y || mirror == Mirror.XY) {
112 | py = heightMinusOne - y;
113 | }
114 |
115 | return pixmap.getPixel(px, py);
116 | }
117 |
118 | public static Pixmap getRotatedMirroredCopy(Pixmap pixmap, Mirror mirror, Rotate rotate){
119 |
120 | Pixmap mirrored = new Pixmap(pixmap.getWidth(), pixmap.getHeight(), pixmap.getFormat());
121 | copyToMirrored(pixmap, mirrored, mirror);
122 |
123 | int newWidth = PixmapTransform.getWidth(pixmap, rotate);
124 | int newHeight = PixmapTransform.getHeight(pixmap, rotate);
125 |
126 | Pixmap copy = new Pixmap(newWidth, newHeight, pixmap.getFormat());
127 | copyToRotated(mirrored, copy, rotate);
128 |
129 | /** Only temp for copying around */
130 | mirrored.dispose();
131 |
132 | return copy;
133 | }
134 |
135 | public static void copyToRotated(Pixmap source, Pixmap target, Rotate rotate){
136 | for(int y=0; y> 16) | (masks[i] & 0xff00) | ((masks[i] & 0xff) << 16);
46 | }
47 |
48 | int a = MathUtils.floor(alpha * 255.0f) & 0xff;
49 |
50 | while (pixels.remaining() > 0) {
51 |
52 | int rgba = pixels.getInt();
53 | int rgb = rgba >>> 8;
54 |
55 | for (int mask : masks) {
56 | if (rgb == mask) {
57 | pixels.position(pixels.position() - 4);
58 | pixels.putInt((rgba & 0xffffff00) | a);
59 | break;
60 | }
61 | }
62 | }
63 |
64 | pixels.flip();
65 | }
66 |
67 | /**
68 | * Sets alpha for all pixels passing the RGB predicate function.
69 | */
70 | public static void mask(Pixmap pixmap, float alpha, IntPredicate rgb) {
71 |
72 | ByteBuffer pixels = pixmap.getPixels();
73 |
74 | int a = MathUtils.floor(alpha * 255.0f) & 0xff;
75 |
76 | while (pixels.remaining() > 0) {
77 |
78 | int rgba = pixels.getInt();
79 |
80 | if (rgb.test(rgba >>> 8)) {
81 | pixels.position(pixels.position() - 4);
82 | pixels.putInt((rgba & 0xffffff00) | a);
83 | }
84 | }
85 |
86 | pixels.flip();
87 | }
88 |
89 | public interface CropResult {
90 |
91 | void accept(int left, int bottom, int width, int height);
92 | }
93 |
94 | /**
95 | * Calculates crop regions of the pixmap in up to four directions. Pixel rows/columns are subject
96 | * to removal if all their pixels have an alpha channel value of exact the same value as given in the parameter.
97 | */
98 | public static void crop(Pixmap pixmap,
99 | boolean left,
100 | boolean bottom,
101 | boolean right,
102 | boolean top,
103 | float alpha,
104 | CropResult consumer) {
105 |
106 | int width = pixmap.getWidth();
107 | int height = pixmap.getHeight();
108 |
109 | int a = MathUtils.floor(alpha * 255.0f) & 0xff;
110 |
111 | int minX = left ? width - 1 : 0;
112 | int maxX = right ? 0 : width - 1;
113 |
114 | int minY = bottom ? height - 1 : 0;
115 | int maxY = top ? 0 : height - 1;
116 |
117 | ByteBuffer pixels = pixmap.getPixels();
118 |
119 | for (int y = 0; y < height; y++) {
120 | for (int x = 0; x < width; x++) {
121 |
122 | int rgba = pixels.getInt();
123 |
124 | if ((rgba & 0xff) != a) {
125 |
126 | minX = Math.min(x, minX);
127 | maxX = Math.max(x, maxX);
128 |
129 | minY = Math.min(y, minY);
130 | maxY = Math.max(y, maxY);
131 | }
132 | }
133 | }
134 |
135 | pixels.flip();
136 |
137 | consumer.accept(minX, minY, maxX, maxY);
138 | }
139 |
140 | /**
141 | * Returns a copy of the given pixmap, cropped about the given dimensions.
142 | */
143 | public static Pixmap crop(Pixmap pixmap, int left, int bottom, int width, int height) {
144 |
145 | Pixmap result = new Pixmap(width, height, pixmap.getFormat());
146 | result.drawPixmap(pixmap, 0, 0, left, bottom, width, height);
147 |
148 | return result;
149 | }
150 |
151 | /**
152 | * Horizontally mirrors the {@link Pixmap} content, in place, line by line.
153 | */
154 | public static void flipX(Pixmap pixmap) {
155 |
156 | int width = pixmap.getWidth();
157 | int height = pixmap.getHeight();
158 |
159 | int bytesPerPixel = getPixelStride(pixmap.getFormat());
160 | int pitch = width * bytesPerPixel;
161 |
162 | ByteBuffer pixels = pixmap.getPixels();
163 |
164 | byte[] buffer = new byte[pitch];
165 |
166 | for (int y = 0; y < height; y++) {
167 |
168 | pixels.position(y * pitch);
169 | pixels.get(buffer, 0, pitch);
170 |
171 | pixels.position(y * pitch);
172 |
173 | for (int x = 0, offs = pitch - bytesPerPixel; x < width; x++, offs -= bytesPerPixel) {
174 | pixels.put(buffer, offs, bytesPerPixel);
175 | }
176 | }
177 |
178 | pixels.position(0);
179 | }
180 |
181 | /**
182 | * Vertically mirrors the {@link Pixmap} content, in place, line by line.
183 | */
184 | public static void flipY(Pixmap pixmap) {
185 |
186 | int width = pixmap.getWidth();
187 | int height = pixmap.getHeight();
188 |
189 | int pitch = width * getPixelStride(pixmap.getFormat());
190 |
191 | ByteBuffer pixels = pixmap.getPixels();
192 |
193 | byte[][] buffer = new byte[2][pitch];
194 |
195 | for (int y = 0; y < height / 2; y++) {
196 |
197 | pixels.position(y * pitch);
198 | pixels.get(buffer[0], 0, pitch);
199 |
200 | pixels.position((height - y - 1) * pitch);
201 | pixels.get(buffer[1], 0, pitch);
202 |
203 | pixels.position(y * pitch);
204 | pixels.put(buffer[1], 0, pitch);
205 |
206 | pixels.position((height - y - 1) * pitch);
207 | pixels.put(buffer[0], 0, pitch);
208 | }
209 |
210 | pixels.position(0);
211 | }
212 |
213 | public static int getPixelStride(Format format) {
214 | switch (format) {
215 | case Alpha:
216 | case Intensity:
217 | return 1;
218 | case LuminanceAlpha:
219 | case RGB565:
220 | case RGBA4444:
221 | return 2;
222 | case RGB888:
223 | return 3;
224 | case RGBA8888:
225 | return 4;
226 | }
227 | throw new IllegalArgumentException("Not implemented");
228 | }
229 |
230 | }
231 |
--------------------------------------------------------------------------------
/src/main/java/com/badlogic/gdx/graphics/glutils/GLBufferObject.java:
--------------------------------------------------------------------------------
1 | package com.badlogic.gdx.graphics.glutils;
2 |
3 | import com.badlogic.gdx.utils.BufferUtils;
4 | import com.badlogic.gdx.utils.Disposable;
5 |
6 | import java.nio.Buffer;
7 | import java.nio.ByteBuffer;
8 |
9 | import static com.badlogic.gdx.Gdx.gl20;
10 | import static com.badlogic.gdx.graphics.GL20.GL_DYNAMIC_DRAW;
11 | import static com.badlogic.gdx.graphics.GL20.GL_STATIC_DRAW;
12 |
13 | /**
14 | * Abstract base class to handle OpenGL buffer objects.
15 | */
16 | abstract class GLBufferObject implements Disposable {
17 |
18 | private final int handle;
19 | private final int target;
20 | private final int usage;
21 | protected final int elementSize;
22 | private final ByteBuffer byteBuffer;
23 |
24 | protected T buffer;
25 | protected int wordSize;
26 |
27 | GLBufferObject(int target, boolean isStatic, int numElements, int elementSize) {
28 |
29 | handle = gl20.glGenBuffer();
30 |
31 | this.target = target;
32 | usage = isStatic ? GL_STATIC_DRAW : GL_DYNAMIC_DRAW;
33 |
34 | this.elementSize = elementSize;
35 | byteBuffer = BufferUtils.newUnsafeByteBuffer(numElements * elementSize);
36 |
37 | createElementBuffer(byteBuffer);
38 |
39 | byteBuffer.flip();
40 | }
41 |
42 | @Override
43 | public void dispose() {
44 | gl20.glDeleteBuffer(handle);
45 | BufferUtils.disposeUnsafeByteBuffer(byteBuffer);
46 | }
47 |
48 | public void bind() {
49 | gl20.glBindBuffer(target, handle);
50 | }
51 |
52 | public void unbind() {
53 | gl20.glBindBuffer(target, 0);
54 | }
55 |
56 | public void uploadData() {
57 | buffer.flip();
58 | byteBuffer.limit(buffer.limit() * wordSize);
59 | gl20.glBufferData(target, byteBuffer.limit(), byteBuffer, usage);
60 | }
61 |
62 | /* TODO: proper API to specify buffer slice
63 | public void uploadSubData(int firstElement, int numElements) {
64 | gl20.glBufferSubData(target, firstElement * elementSize, numElements * elementSize, byteBuffer);
65 | }*/
66 |
67 | public int getNumElements() {
68 | return buffer.limit() * wordSize / elementSize;
69 | }
70 |
71 | public int getMaxElements() {
72 | return byteBuffer.capacity() / elementSize;
73 | }
74 |
75 | public T getBuffer() {
76 | return buffer;
77 | }
78 |
79 | protected abstract void createElementBuffer(ByteBuffer byteBuffer);
80 |
81 | }
82 |
--------------------------------------------------------------------------------
/src/main/java/com/badlogic/gdx/graphics/glutils/GLTextureUtils.java:
--------------------------------------------------------------------------------
1 | package com.badlogic.gdx.graphics.glutils;
2 |
3 | import com.badlogic.gdx.graphics.GL30;
4 | import com.badlogic.gdx.utils.BufferUtils;
5 |
6 | import java.nio.LongBuffer;
7 |
8 | import static com.badlogic.gdx.graphics.GL33Ext.*;
9 |
10 | public class GLTextureUtils {
11 |
12 | private static LongBuffer tmp = BufferUtils.newLongBuffer(1);
13 |
14 | public static boolean isInternalFormatSupported(int target, int format) {
15 | tmp.clear();
16 | glGetInternalFormativ(target, format, GL_INTERNALFORMAT_SUPPORTED, tmp);
17 | return tmp.get(0) == GL30.GL_TRUE;
18 | }
19 |
20 | public static long getInternalFormatPreferred(int target, int format) {
21 | tmp.clear();
22 | glGetInternalFormativ(target, format, GL_INTERNALFORMAT_PREFERRED, tmp);
23 | return tmp.get(0);
24 | }
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/src/main/java/com/badlogic/gdx/graphics/glutils/IndexBufferObjectExt.java:
--------------------------------------------------------------------------------
1 | package com.badlogic.gdx.graphics.glutils;
2 |
3 | import com.badlogic.gdx.graphics.GL33Ext;
4 | import com.badlogic.gdx.utils.BufferUtils;
5 |
6 | import java.nio.ByteBuffer;
7 | import java.nio.ShortBuffer;
8 |
9 | import static com.badlogic.gdx.Gdx.gl30;
10 | import static com.badlogic.gdx.graphics.GL30.*;
11 |
12 | /**
13 | * A lean replacement for {@link IndexBufferObject}.
14 | * Designed to work with {@link VertexArrayObject}.
15 | */
16 | public class IndexBufferObjectExt extends GLBufferObject {
17 |
18 | public IndexBufferObjectExt(boolean isStatic, int numIndices) {
19 | super(GL_ELEMENT_ARRAY_BUFFER, isStatic, numIndices, 2);
20 | }
21 |
22 | public void setIndices(short[] indices, int offset, int count) {
23 | buffer.position(0);
24 | BufferUtils.copy(indices, offset, buffer, count);
25 | buffer.position(buffer.limit());
26 | buffer.limit(buffer.capacity());
27 | }
28 |
29 | public void addIndices(int count, short... indices) {
30 | buffer.put(indices, 0, count);
31 | }
32 |
33 | public void drawElements(int count, int offset) {
34 | gl30.glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_SHORT, offset * wordSize);
35 | }
36 |
37 | public void drawElementsBaseVertex(int count, int offset, int baseVertex) {
38 | GL33Ext.glDrawElementsBaseVertex(GL_TRIANGLES, count, GL_UNSIGNED_SHORT, offset * wordSize, baseVertex);
39 | }
40 |
41 | @Override
42 | protected void createElementBuffer(ByteBuffer byteBuffer) {
43 | buffer = byteBuffer.asShortBuffer();
44 | wordSize = 2;
45 | }
46 |
47 | public static boolean fitsElements(IndexBufferObjectExt bufferObject, int numElements) {
48 | return (bufferObject != null) && (numElements <= bufferObject.getMaxElements());
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/main/java/com/badlogic/gdx/graphics/glutils/MultiTargetFrameBuffer.java:
--------------------------------------------------------------------------------
1 | package com.badlogic.gdx.graphics.glutils;
2 |
3 | import com.badlogic.gdx.graphics.*;
4 | import com.badlogic.gdx.graphics.glutils.GLFrameBuffer.FrameBufferBuilder;
5 | import com.badlogic.gdx.utils.*;
6 |
7 | import java.nio.*;
8 |
9 | import static com.badlogic.gdx.Gdx.gl30;
10 | import static com.badlogic.gdx.graphics.GL30.*;
11 | import static com.badlogic.gdx.graphics.GL33Ext.GL_CLAMP_TO_BORDER;
12 | import static com.badlogic.gdx.graphics.GL33Ext.GL_TEXTURE_BORDER_COLOR;
13 |
14 | /**
15 | * An extension to {@link FrameBuffer} with multiple color attachments. Can be used as
16 | * multi-render-target in deferred rendering (G-buffer).
17 | *
18 | * Uses alternate depth/stencil buffer formats to allow for GL_DEPTH24_STENCIL8.
19 | */
20 | public class MultiTargetFrameBuffer implements Disposable {
21 |
22 | public enum Format {
23 |
24 | R32F(GL_R32F, GL_RED, GL_FLOAT),
25 | RG32F(GL_RG32F, GL_RG, GL_FLOAT),
26 | RGB32F(GL_RGB32F, GL_RGB, GL_FLOAT),
27 | RGBA32F(GL_RGBA32F, GL_RGBA, GL_FLOAT),
28 |
29 | RG16F(GL_RG16F, GL_RG, GL_HALF_FLOAT),
30 |
31 | R8(GL_R8, GL_RED, GL_UNSIGNED_BYTE),
32 | RG8(GL_RG8, GL_RG, GL_UNSIGNED_BYTE),
33 |
34 | R16I(GL_R16I, GL_RED_INTEGER, GL_SHORT),
35 | R32I(GL_R32I, GL_RED_INTEGER, GL_INT),
36 |
37 | PixmapFormat(GL_NONE, GL_NONE, GL_NONE);
38 |
39 | private final int internal, format, type;
40 |
41 | Format(int internal, int format, int type) {
42 | this.internal = internal;
43 | this.format = format;
44 | this.type = type;
45 | }
46 | }
47 |
48 | public static class ColorAttachmentFormat {
49 |
50 | Format format = Format.PixmapFormat;
51 | Pixmap.Format pixmapFormat = Pixmap.Format.RGB888;
52 | boolean generateMipmaps = false;
53 | Texture.TextureFilter minFilter = Texture.TextureFilter.Nearest;
54 | Texture.TextureFilter magFilter = Texture.TextureFilter.Nearest;
55 | Texture.TextureWrap wrap = Texture.TextureWrap.ClampToEdge;
56 |
57 | public ColorAttachmentFormat(Format format,
58 | Pixmap.Format pixmapFormat) {
59 | this.format = format;
60 | this.pixmapFormat = pixmapFormat;
61 | }
62 |
63 | public ColorAttachmentFormat(Format format,
64 | Pixmap.Format pixmapFormat,
65 | boolean generateMipmaps,
66 | Texture.TextureFilter minFilter,
67 | Texture.TextureFilter magFilter) {
68 | this.format = format;
69 | this.pixmapFormat = pixmapFormat;
70 | this.generateMipmaps = generateMipmaps;
71 | this.minFilter = minFilter;
72 | this.magFilter = magFilter;
73 | }
74 |
75 | public ColorAttachmentFormat(Format format,
76 | Pixmap.Format pixmapFormat,
77 | boolean generateMipmaps,
78 | Texture.TextureFilter minFilter,
79 | Texture.TextureFilter magFilter,
80 | Texture.TextureWrap wrap) {
81 | this.format = format;
82 | this.pixmapFormat = pixmapFormat;
83 | this.generateMipmaps = generateMipmaps;
84 | this.minFilter = minFilter;
85 | this.magFilter = magFilter;
86 | this.wrap = wrap;
87 | }
88 | }
89 |
90 | private final FrameBuffer frameBuffer;
91 | private final Texture[] colorTextures;
92 | private final boolean hasStencil;
93 |
94 | private static final FloatBuffer tmpColors = BufferUtils.newFloatBuffer(4);
95 |
96 | /**
97 | * Creates a new MRT FrameBuffer with the given color buffer format and dimensions.
98 | */
99 | public static MultiTargetFrameBuffer create(Format format, int numColorBuffers,
100 | int width, int height, boolean hasDepth, boolean hasStencil) {
101 |
102 | return create(format, null, numColorBuffers, width, height, hasDepth, hasStencil);
103 | }
104 |
105 | /**
106 | * Creates a new MRT FrameBuffer with the given {@link Pixmap} format and dimensions.
107 | */
108 | public static MultiTargetFrameBuffer create(Pixmap.Format pixmapFormat, int numColorBuffers,
109 | int width, int height, boolean hasDepth, boolean hasStencil) {
110 |
111 | return create(Format.PixmapFormat, pixmapFormat, numColorBuffers, width, height, hasDepth, hasStencil);
112 | }
113 |
114 | /**
115 | * Creates a new MRT FrameBuffer with the given color buffer format and dimensions. If the format is
116 | * {@link Format#PixmapFormat}, the pixmapFormat parameter is used, otherwise it is ignored.
117 | */
118 | public static MultiTargetFrameBuffer create(Format format, Pixmap.Format pixmapFormat, int numColorBuffers,
119 | int width, int height, boolean hasDepth, boolean hasStencil) {
120 |
121 | ColorAttachmentFormat[] fbCreateFormats = new ColorAttachmentFormat[numColorBuffers];
122 |
123 | for (int i = 0; i < numColorBuffers; i++) {
124 | fbCreateFormats[i] = new ColorAttachmentFormat(format, pixmapFormat);
125 | }
126 |
127 | return create(fbCreateFormats, width, height, hasDepth, hasStencil);
128 | }
129 |
130 | /**
131 | * Creates a new MRT FrameBuffer with the given color buffer formats and dimensions.
132 | *
133 | * This function equals {@link MultiTargetFrameBuffer#create(Format, Pixmap.Format, int, int, int, boolean, boolean)}
134 | * but individually describes the format for each color buffer.
135 | */
136 | public static MultiTargetFrameBuffer create(ColorAttachmentFormat[] formats,
137 | int width,
138 | int height,
139 | boolean hasDepth,
140 | boolean hasStencil) {
141 |
142 | FrameBufferBuilder builder = new FrameBufferBuilder(width, height);
143 |
144 | for (ColorAttachmentFormat attachment : formats) {
145 |
146 | if (attachment.format == Format.PixmapFormat) {
147 | builder.addBasicColorTextureAttachment(attachment.pixmapFormat);
148 | } else {
149 | builder.addColorTextureAttachment(attachment.format.internal,
150 | attachment.format.format, attachment.format.type);
151 | }
152 | }
153 |
154 | if (hasDepth && hasStencil) {
155 | builder.addBasicStencilDepthPackedRenderBuffer();
156 | } else if (hasDepth) {
157 | builder.addDepthTextureAttachment(GL_DEPTH_COMPONENT32F, GL_FLOAT);
158 | }
159 |
160 | FrameBuffer frameBuffer = builder.build();
161 | Array textures = frameBuffer.getTextureAttachments();
162 |
163 | for (int i = 0; i < formats.length; i++) {
164 |
165 | ColorAttachmentFormat attachment = formats[i];
166 | Texture texture = textures.get(i);
167 |
168 | texture.setFilter(attachment.minFilter, attachment.magFilter);
169 | texture.setWrap(attachment.wrap, attachment.wrap);
170 | }
171 |
172 | return new MultiTargetFrameBuffer(frameBuffer, hasDepth, hasStencil);
173 | }
174 |
175 | private MultiTargetFrameBuffer(FrameBuffer frameBuffer, boolean hasDepth, boolean hasStencil) {
176 |
177 | this.frameBuffer = frameBuffer;
178 |
179 | this.colorTextures = new Texture[frameBuffer.textureAttachments.size];
180 | for (int i = 0; i < frameBuffer.textureAttachments.size; i++) {
181 | this.colorTextures[i] = frameBuffer.textureAttachments.get(i);
182 | }
183 |
184 | this.hasStencil = hasStencil;
185 | }
186 |
187 | @Override
188 | public void dispose() {
189 | frameBuffer.dispose();
190 | }
191 |
192 | public int getWidth() {
193 | return frameBuffer.getWidth();
194 | }
195 |
196 | public int getHeight() {
197 | return frameBuffer.getHeight();
198 | }
199 |
200 | public void bind() {
201 | frameBuffer.bind();
202 | }
203 |
204 | public void begin() {
205 | frameBuffer.begin();
206 | }
207 |
208 | public void end() {
209 | frameBuffer.end();
210 | }
211 |
212 | public Texture getColorBufferTexture(int index) {
213 | return colorTextures[index];
214 | }
215 |
216 | public void clampToBorder(int index, Color color) {
217 | int handle = colorTextures[index].getTextureObjectHandle();
218 | gl30.glBindTexture(GL_TEXTURE_2D, handle);
219 |
220 | gl30.glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
221 | gl30.glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
222 |
223 | synchronized (tmpColors) {
224 | tmpColors.clear();
225 | tmpColors.put(color.r);
226 | tmpColors.put(color.g);
227 | tmpColors.put(color.b);
228 | tmpColors.put(color.a);
229 | tmpColors.flip();
230 |
231 | gl30.glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, tmpColors);
232 | }
233 |
234 | gl30.glBindTexture(GL_TEXTURE_2D, 0);
235 | }
236 |
237 | public void generateMipmap(int index) {
238 | int handle = colorTextures[index].getTextureObjectHandle();
239 | gl30.glBindTexture(GL_TEXTURE_2D, handle);
240 | gl30.glGenerateMipmap(GL_TEXTURE_2D);
241 | gl30.glBindTexture(GL_TEXTURE_2D, 0);
242 | }
243 |
244 | public void clearColorBuffer(Color color, int index) {
245 | clearColorBuffer(color.r, color.g, color.b, color.a, index);
246 | }
247 |
248 | public void clearColorBuffer(float r, float g, float b, float a, int index) {
249 | synchronized (tmpColors) {
250 | tmpColors.clear();
251 | tmpColors.put(r);
252 | tmpColors.put(g);
253 | tmpColors.put(b);
254 | tmpColors.put(a);
255 | tmpColors.flip();
256 |
257 | gl30.glClearBufferfv(GL_COLOR, index, tmpColors);
258 | }
259 | }
260 |
261 | public void clearColorBuffers(Color color) {
262 | clearColorBuffers(color.r, color.g, color.b, color.a);
263 | }
264 |
265 | public void clearColorBuffers(float r, float g, float b, float a) {
266 | synchronized (tmpColors) {
267 | tmpColors.clear();
268 | tmpColors.put(r);
269 | tmpColors.put(g);
270 | tmpColors.put(b);
271 | tmpColors.put(a);
272 | tmpColors.flip();
273 |
274 | for (int index = 0; index < colorTextures.length; index++) {
275 | gl30.glClearBufferfv(GL_COLOR, index, tmpColors);
276 | }
277 | }
278 | }
279 |
280 | public void clearColorBuffers(Color color, int[] indices) {
281 | clearColorBuffers(color.r, color.g, color.b, color.a, indices);
282 | }
283 |
284 | public void clearColorBuffers(float r, float g, float b, float a, int[] indices) {
285 | synchronized (tmpColors) {
286 | tmpColors.clear();
287 | tmpColors.put(r);
288 | tmpColors.put(g);
289 | tmpColors.put(b);
290 | tmpColors.put(a);
291 | tmpColors.flip();
292 |
293 | for (int index : indices) {
294 | gl30.glClearBufferfv(GL_COLOR, index, tmpColors);
295 | }
296 | }
297 | }
298 |
299 | public void clearDepthBuffer(float depth) {
300 | synchronized (tmpColors) {
301 | tmpColors.clear();
302 | tmpColors.put(depth);
303 | tmpColors.flip();
304 |
305 | gl30.glClearBufferfv(GL_DEPTH, 0, tmpColors);
306 | }
307 | }
308 |
309 | public void clearDepthStencilBuffer(float depth, int stencil) {
310 | gl30.glClearBufferfi(GL_DEPTH_STENCIL, 0, depth, stencil);
311 | }
312 |
313 | public static void readColorBuffer(Pixmap target,
314 | MultiTargetFrameBuffer source, int srcIndex,
315 | int srcX0, int srcY0, int srcX1, int srcY1) {
316 |
317 | gl30.glBindFramebuffer(GL_READ_FRAMEBUFFER, source.frameBuffer.getFramebufferHandle());
318 | gl30.glReadBuffer(GL_COLOR_ATTACHMENT0 + srcIndex);
319 |
320 | gl30.glPixelStorei(GL_PACK_ALIGNMENT, 1);
321 |
322 | ByteBuffer pixels = target.getPixels();
323 |
324 | int glFormat = target.getGLFormat();
325 | gl30.glReadPixels(srcX0, srcY0, srcX1 - srcX0, srcY1 - srcY0, glFormat, GL_UNSIGNED_BYTE, pixels);
326 |
327 | gl30.glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
328 | gl30.glReadBuffer(GL_BACK);
329 | }
330 |
331 | public static void readFloatBuffer(FloatBuffer target,
332 | MultiTargetFrameBuffer source, int srcIndex,
333 | int srcX0, int srcY0, int srcX1, int srcY1) {
334 |
335 | gl30.glBindFramebuffer(GL_READ_FRAMEBUFFER, source.frameBuffer.getFramebufferHandle());
336 | gl30.glReadBuffer(GL_COLOR_ATTACHMENT0 + srcIndex);
337 |
338 | gl30.glPixelStorei(GL_PACK_ALIGNMENT, 4);
339 |
340 | gl30.glReadPixels(srcX0, srcY0, srcX1 - srcX0, srcY1 - srcY0, GL_RED, GL_FLOAT, target);
341 |
342 | gl30.glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
343 | gl30.glReadBuffer(GL_BACK);
344 | }
345 |
346 | public static void copyDepthStencilBuffer(MultiTargetFrameBuffer target,
347 | int destX0, int destY0, int destX1, int destY1,
348 | MultiTargetFrameBuffer source,
349 | int srcX0, int srcY0, int srcX1, int srcY1) {
350 |
351 | int mask = GL_DEPTH_BUFFER_BIT;
352 |
353 | if (source.hasStencil && target.hasStencil) {
354 | mask |= GL_STENCIL_BUFFER_BIT;
355 | }
356 |
357 | int sourceFbo = source.frameBuffer.getFramebufferHandle();
358 | int targetFbo = target.frameBuffer.getFramebufferHandle();
359 |
360 | gl30.glBindFramebuffer(GL_READ_FRAMEBUFFER, sourceFbo);
361 | gl30.glBindFramebuffer(GL_DRAW_FRAMEBUFFER, targetFbo);
362 |
363 | gl30.glBlitFramebuffer(srcX0, srcY0, srcX1, srcY1, destX0, destY0, destX1, destY1, mask, GL_NEAREST);
364 |
365 | gl30.glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
366 | gl30.glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
367 | }
368 |
369 | }
370 |
--------------------------------------------------------------------------------
/src/main/java/com/badlogic/gdx/graphics/glutils/ShaderProgramExt.java:
--------------------------------------------------------------------------------
1 | package com.badlogic.gdx.graphics.glutils;
2 |
3 | import com.badlogic.gdx.function.Consumer;
4 | import com.badlogic.gdx.utils.Disposable;
5 |
6 | /**
7 | * Extension to {@link ShaderProgram} for customized shader construction. The "onCreate" consumer function
8 | * is called during shader creation, between calls to glCreateProgram() and glLinkProgram().
9 | */
10 | public class ShaderProgramExt implements Disposable {
11 |
12 | private int handle;
13 | private Program program;
14 | private Consumer onCreate;
15 | private Runnable onDispose;
16 |
17 | public ShaderProgramExt(String vertexShader, String fragmentShader,
18 | Consumer onCreate, Runnable onDispose) {
19 |
20 | this.onCreate = onCreate;
21 | this.onDispose = onDispose;
22 | this.program = new Program(vertexShader, fragmentShader);
23 | }
24 |
25 | @Override
26 | public void dispose() {
27 | program.dispose();
28 | }
29 |
30 | public ShaderProgram getProgram() {
31 | return program;
32 | }
33 |
34 | private class Program extends ShaderProgram {
35 |
36 | public Program(String vertexShader, String fragmentShader) {
37 | super(vertexShader, fragmentShader);
38 | }
39 |
40 | @Override
41 | public void dispose() {
42 | onDispose.run();
43 | super.dispose();
44 | }
45 |
46 | @Override
47 | protected int createProgram() {
48 | handle = super.createProgram();
49 | onCreate.accept(handle);
50 | return handle;
51 | }
52 | }
53 |
54 | }
55 |
--------------------------------------------------------------------------------
/src/main/java/com/badlogic/gdx/graphics/glutils/VertexArrayObject.java:
--------------------------------------------------------------------------------
1 | package com.badlogic.gdx.graphics.glutils;
2 |
3 | import com.badlogic.gdx.utils.Disposable;
4 |
5 | import static com.badlogic.gdx.Gdx.gl30;
6 |
7 | /**
8 | * Manages VAO bindings.
9 | */
10 | public class VertexArrayObject implements Disposable {
11 |
12 | private final int[] vao = new int[1];
13 |
14 | private VertexAttributeArray attributes;
15 | private int maxLocation = -1;
16 |
17 | public VertexArrayObject() {
18 | gl30.glGenVertexArrays(1, vao, 0);
19 | }
20 |
21 | @Override
22 | public void dispose() {
23 | gl30.glDeleteVertexArrays(1, vao, 0);
24 | }
25 |
26 | public void bind() {
27 | gl30.glBindVertexArray(vao[0]);
28 | }
29 |
30 | public void bindVertexLayout(VertexAttributeArray attributes) {
31 |
32 | int[] locations = new int[attributes.size()];
33 | for (int l = 0; l < attributes.size(); l++) {
34 | locations[l] = l;
35 | }
36 |
37 | bindVertexLayout(locations, attributes);
38 | }
39 |
40 | public void bindVertexLayout(int[] locations, VertexAttributeArray attributes) {
41 |
42 | unbindLocations();
43 |
44 | this.attributes = attributes;
45 |
46 | int count = Math.min(locations.length, attributes.size());
47 |
48 | for (int i = 0; i < count; i++) {
49 | bindLocation(locations[i], i);
50 | }
51 | }
52 |
53 | private void bindLocation(int location, int attribute) {
54 |
55 | if (location < 0) {
56 | return;
57 | }
58 |
59 | gl30.glEnableVertexAttribArray(location);
60 | attributes.bind(location, attribute);
61 |
62 | maxLocation = Math.max(location, maxLocation);
63 | }
64 |
65 | private void unbindLocations() {
66 |
67 | for (int i = 0; i <= maxLocation; i++) {
68 | gl30.glDisableVertexAttribArray(i);
69 | }
70 |
71 | maxLocation = -1;
72 | }
73 |
74 | public static void unbind() {
75 | gl30.glBindVertexArray(0);
76 | }
77 |
78 | public VertexAttributeArray getAttributes() {
79 | return attributes;
80 | }
81 |
82 | }
83 |
--------------------------------------------------------------------------------
/src/main/java/com/badlogic/gdx/graphics/glutils/VertexAttributeArray.java:
--------------------------------------------------------------------------------
1 | package com.badlogic.gdx.graphics.glutils;
2 |
3 | import com.badlogic.gdx.utils.GdxRuntimeException;
4 |
5 | import java.util.Arrays;
6 |
7 | import static com.badlogic.gdx.Gdx.gl30;
8 | import static com.badlogic.gdx.graphics.GL30.*;
9 |
10 | public class VertexAttributeArray {
11 |
12 | public static class Attribute {
13 |
14 | public final int numComponents;
15 | public final int type;
16 | public final boolean normalized;
17 |
18 | private final Alias alias;
19 |
20 | protected int offset;
21 | private final int stride;
22 |
23 | public Attribute(int numComponents, int type, boolean normalized, Alias alias) {
24 | this.numComponents = numComponents;
25 | this.type = type;
26 | this.normalized = normalized;
27 | this.alias = alias;
28 | // todo:
29 | this.stride = 0;
30 | }
31 |
32 | public int getOffset() {
33 | return offset;
34 | }
35 |
36 | void bind(int location, int stride) {
37 | if (type == GL_FLOAT || normalized) {
38 | gl30.glVertexAttribPointer(location, numComponents, type, normalized, stride, offset);
39 | } else {
40 | gl30.glVertexAttribIPointer(location, numComponents, type, stride, offset);
41 | }
42 | }
43 |
44 | }
45 |
46 | public enum Alias {
47 |
48 | Position("a_position"),
49 | Normal("a_normal"),
50 | ColorPacked("a_color"),
51 |
52 | TexCoord0("a_texCoord0"),
53 |
54 | Generic(null);
55 |
56 | Alias(String alias) {
57 | this.alias = alias;
58 | }
59 |
60 | private String alias;
61 | }
62 |
63 | private final Attribute[] attributes;
64 |
65 | private final int vertexSize;
66 | private final int vertexStride;
67 |
68 | public VertexAttributeArray(Attribute... attributes) {
69 | this(0, attributes);
70 | }
71 |
72 | public VertexAttributeArray(VertexAttributeArray other) {
73 | this(other.vertexStride, other.attributes);
74 | }
75 |
76 | public VertexAttributeArray(int vertexStride, Attribute... attributes) {
77 |
78 | this.attributes = Arrays.copyOf(attributes, attributes.length);
79 |
80 | this.vertexStride = vertexStride;
81 | this.vertexSize = calculateSizeAndOffsets();
82 | }
83 |
84 | public int size() {
85 | return attributes.length;
86 | }
87 |
88 | public Attribute get(int index) {
89 | return attributes[index];
90 | }
91 |
92 | public void bind(int location, int index) {
93 | int stride = vertexSize + vertexStride;
94 | //int stride = (vertexStride != 0) ? (vertexSize + vertexStride) : 0;
95 | attributes[index].bind(location, stride);
96 | }
97 |
98 | public Attribute find(Alias alias) {
99 |
100 | for (Attribute attribute : attributes) {
101 | if (attribute.alias == alias) {
102 | return attribute;
103 | }
104 | }
105 |
106 | return null;
107 | }
108 |
109 | public int getVertexSize() {
110 | return vertexSize + vertexStride;
111 | }
112 |
113 | private int calculateSizeAndOffsets() {
114 |
115 | int offset = 0;
116 |
117 | for (Attribute attribute : attributes) {
118 |
119 | attribute.offset = offset;
120 |
121 | offset += attribute.numComponents * getTypeSize(attribute.type);
122 | offset += attribute.stride;
123 |
124 | }
125 |
126 | return offset;
127 | }
128 |
129 | private int getTypeSize(int type) {
130 |
131 | switch (type) {
132 |
133 | case GL_BYTE:
134 | case GL_UNSIGNED_BYTE:
135 | return 1;
136 |
137 | case GL_SHORT:
138 | case GL_UNSIGNED_SHORT:
139 | return 2;
140 |
141 | case GL_INT:
142 | case GL_UNSIGNED_INT:
143 | case GL_FLOAT:
144 | return 4;
145 |
146 | default:
147 | throw new GdxRuntimeException("Unsupported vertex attribute type!");
148 | }
149 | }
150 |
151 | public static Attribute Float(Alias alias, int numComponents, boolean normalized) {
152 | return new Attribute(numComponents, GL_FLOAT, normalized, alias);
153 | }
154 |
155 | public static Attribute UnsignedByte(Alias alias, int numComponents) {
156 | return new Attribute(numComponents, GL_UNSIGNED_BYTE, true, alias);
157 | }
158 |
159 | public static Attribute Int(Alias alias, int numComponents) {
160 | return new Attribute(numComponents, GL_INT, false, alias);
161 | }
162 |
163 | public static Attribute UnsignedInt(Alias alias, int numComponents) {
164 | return new Attribute(numComponents, GL_UNSIGNED_INT, false, alias);
165 | }
166 |
167 | }
168 |
--------------------------------------------------------------------------------
/src/main/java/com/badlogic/gdx/graphics/glutils/VertexBufferObjectExt.java:
--------------------------------------------------------------------------------
1 | package com.badlogic.gdx.graphics.glutils;
2 |
3 | import com.badlogic.gdx.utils.BufferUtils;
4 |
5 | import java.nio.ByteBuffer;
6 | import java.nio.FloatBuffer;
7 |
8 | import static com.badlogic.gdx.Gdx.gl30;
9 | import static com.badlogic.gdx.graphics.GL20.GL_TRIANGLES;
10 | import static com.badlogic.gdx.graphics.GL30.GL_ARRAY_BUFFER;
11 |
12 | /**
13 | * A lean replacement for {@link VertexBufferObjectWithVAO}.
14 | * Designed to work with {@link VertexArrayObject}.
15 | */
16 | public class VertexBufferObjectExt extends GLBufferObject {
17 |
18 | public VertexBufferObjectExt(boolean isStatic, int numVertices, int vertexSize) {
19 | super(GL_ARRAY_BUFFER, isStatic, numVertices, vertexSize);
20 | }
21 |
22 | public void setVertices(float[] vertices, int offset, int count) {
23 | buffer.position(0);
24 | BufferUtils.copy(vertices, buffer, count, offset);
25 | buffer.position(buffer.limit());
26 | buffer.limit(buffer.capacity());
27 | }
28 |
29 | public void addVertices(int count, float... vertices) {
30 | buffer.put(vertices, 0, count);
31 | }
32 |
33 | public void drawArrays(int count, int offset) {
34 | gl30.glDrawArrays(GL_TRIANGLES, offset, count);
35 | }
36 |
37 | @Override
38 | protected void createElementBuffer(ByteBuffer byteBuffer) {
39 | buffer = byteBuffer.asFloatBuffer();
40 | wordSize = 4;
41 | }
42 |
43 | public static boolean fitsElements(VertexBufferObjectExt bufferObject, int numElements, int elementSize) {
44 | return (bufferObject != null)
45 | && (numElements <= bufferObject.getMaxElements())
46 | && (elementSize == bufferObject.elementSize);
47 | }
48 |
49 | }
50 |
--------------------------------------------------------------------------------
/src/main/java/com/badlogic/gdx/json/AnnotatedJson.java:
--------------------------------------------------------------------------------
1 | package com.badlogic.gdx.json;
2 |
3 | import com.badlogic.gdx.files.FileHandle;
4 | import com.badlogic.gdx.function.Predicate;
5 | import com.badlogic.gdx.function.*;
6 | import com.badlogic.gdx.lang.ClassFinder;
7 | import com.badlogic.gdx.utils.*;
8 |
9 | import java.io.*;
10 | import java.nio.charset.StandardCharsets;
11 | import java.util.zip.GZIPInputStream;
12 | import java.util.zip.GZIPOutputStream;
13 |
14 | /**
15 | * Utility functions to read or write a hierarchy of objects annotated with
16 | * {@link com.badlogic.gdx.json.annotations.JsonSerializable} and {@link com.badlogic.gdx.json.annotations.JsonSerialize}.
17 | */
18 | public class AnnotatedJson {
19 |
20 | public static Json newReader(Class clazz, Consumer setupJson) {
21 |
22 | Json json = new Json();
23 | json.setSerializer(clazz, new AnnotatedJsonSerializer<>(json, clazz));
24 |
25 | if (setupJson != null) {
26 | setupJson.accept(json);
27 | }
28 |
29 | return json;
30 | }
31 |
32 | public static T read(FileHandle path, Class clazz, Consumer setupJson) throws IOException {
33 | Json json = newReader(clazz, setupJson);
34 | return read(path, clazz, json);
35 | }
36 |
37 | public static T read(FileHandle path, Class clazz, Json json) throws IOException {
38 | try {
39 | InputStream fileStream = path.read();
40 | BufferedInputStream stream = new BufferedInputStream(fileStream);
41 | Reader reader = new InputStreamReader(stream, StandardCharsets.UTF_8);
42 | return json.fromJson(clazz, reader);
43 | } catch (SerializationException e) {
44 | GdxSnippets.log.error("Error while serializing class " + clazz.getName(), e);
45 | throw new IOException(e.getCause());
46 | } catch (RuntimeException e) {
47 | throw new IOException(e);
48 | }
49 | }
50 |
51 | public static T read(byte[] bytes, Class clazz, Json json) throws IOException {
52 | try {
53 | InputStream bais = new ByteArrayInputStream(bytes);
54 | Reader reader = new InputStreamReader(bais, StandardCharsets.UTF_8);
55 | return json.fromJson(clazz, reader);
56 | } catch (SerializationException e) {
57 | GdxSnippets.log.error("Error while serializing class " + clazz.getName(), e);
58 | throw new IOException(e.getCause());
59 | } catch (RuntimeException e) {
60 | throw new IOException(e);
61 | }
62 | }
63 |
64 | public static T readGZip(FileHandle path, Class clazz, Json json) throws IOException {
65 | try {
66 | InputStream fileStream = path.read();
67 | InputStream stream = new GZIPInputStream(fileStream);
68 | Reader reader = new InputStreamReader(stream, StandardCharsets.UTF_8);
69 | return json.fromJson(clazz, reader);
70 | } catch (SerializationException e) {
71 | GdxSnippets.log.error("Error while serializing class " + clazz.getName(), e);
72 | throw new IOException(e.getCause());
73 | } catch (RuntimeException e) {
74 | throw new IOException(e);
75 | }
76 | }
77 |
78 | public static T readGZip(byte[] bytes, Class clazz, Json json) throws IOException {
79 | try {
80 | InputStream bais = new ByteArrayInputStream(bytes);
81 | InputStream gzip = new GZIPInputStream(bais);
82 | Reader reader = new InputStreamReader(gzip, StandardCharsets.UTF_8);
83 | return json.fromJson(clazz, reader);
84 | } catch (SerializationException e) {
85 | GdxSnippets.log.error("Error while serializing class " + clazz.getName(), e);
86 | throw new IOException(e.getCause());
87 | } catch (RuntimeException e) {
88 | throw new IOException(e);
89 | }
90 | }
91 |
92 | public static Json newWriter(Class clazz, Consumer setupJson) {
93 |
94 | Json json = new Json(JsonWriter.OutputType.json);
95 | json.setSerializer(clazz, new AnnotatedJsonSerializer<>(json, clazz));
96 |
97 | if (setupJson != null) {
98 | setupJson.accept(json);
99 | }
100 |
101 | return json;
102 | }
103 |
104 | public static void write(FileHandle path, T object, Class clazz, Consumer setupJson) throws IOException {
105 | Json json = newWriter(clazz, setupJson);
106 | write(path, false, object, json);
107 | }
108 |
109 | public static void write(FileHandle path, boolean compact, T object, Class clazz, Consumer setupJson) throws IOException {
110 | Json json = newWriter(clazz, setupJson);
111 | write(path, compact, object, json);
112 | }
113 |
114 | public static void write(FileHandle path, T object, Json json) throws IOException {
115 | write(path, false, object, json);
116 | }
117 |
118 | public static void write(FileHandle path, boolean compact, T object, Json json) throws IOException {
119 |
120 | String output = json.toJson(object);
121 | String prettyOutput = compact ? output : json.prettyPrint(output);
122 |
123 | try (FileOutputStream fos = new FileOutputStream(path.file(), false)) {
124 | try (Writer writer = new OutputStreamWriter(fos, StandardCharsets.UTF_8)) {
125 | writer.write(prettyOutput);
126 | writer.flush();
127 | }
128 | }
129 | }
130 |
131 | public static byte[] write(T object, Json json) throws IOException {
132 |
133 | ByteArrayOutputStream baos = new ByteArrayOutputStream(65536);
134 |
135 | try (Writer writer = new OutputStreamWriter(baos, StandardCharsets.UTF_8)) {
136 | json.toJson(object, writer);
137 | return baos.toByteArray();
138 | }
139 | }
140 |
141 | public static void writeGZip(FileHandle path, boolean compact, T object, Json json) throws IOException {
142 |
143 | String output = json.toJson(object);
144 | String prettyOutput = compact ? output : json.prettyPrint(output);
145 |
146 | try (OutputStream gzip = new GZIPOutputStream(new FileOutputStream(path.file(), false), true)) {
147 | try (Writer writer = new OutputStreamWriter(gzip, StandardCharsets.UTF_8)) {
148 | writer.write(prettyOutput);
149 | writer.flush();
150 | }
151 | }
152 | }
153 |
154 | public static byte[] writeGZip(T object, Json json) throws IOException {
155 |
156 | ByteArrayOutputStream baos = new ByteArrayOutputStream(65536);
157 |
158 | try (OutputStream gzip = new GZIPOutputStream(baos, true)) {
159 | try (Writer writer = new OutputStreamWriter(gzip, StandardCharsets.UTF_8)) {
160 | json.toJson(object, writer);
161 | return baos.toByteArray();
162 | }
163 | }
164 | }
165 |
166 | /**
167 | * Convenience function to register another annotated Json serializer.
168 | */
169 | public static void register(Json json, Class clazz) {
170 | json.setSerializer(clazz, new AnnotatedJsonSerializer<>(json, clazz));
171 | }
172 |
173 | /**
174 | * Convenience function to register another Json serializer.
175 | */
176 | public static void register(Json json, Class clazz, Json.Serializer serializer) {
177 | json.setSerializer(clazz, serializer);
178 | }
179 |
180 | /**
181 | * Scans for subclasses of the given class, and adds annotated serializers for each
182 | * of them. Only classes located the same URL as the given class are searched.
183 | */
184 | @Deprecated
185 | public static void registerSubclasses(Json json, Class clazz,
186 | Predicate clazzNameFilter) {
187 |
188 | if (clazzNameFilter == null) {
189 | clazzNameFilter = (name) -> true;
190 | }
191 |
192 | new ClassFinder()
193 | .filterURLforClass(clazz)
194 | .process(clazzNameFilter, aClazz -> {
195 | if (!clazz.equals(aClazz) && clazz.isAssignableFrom(aClazz)) {
196 | register(json, aClazz.asSubclass(clazz));
197 | }
198 | });
199 | }
200 |
201 | public static void enumerateAllClasses(Array allClassNames,
202 | Class referenceClass,
203 | Predicate classNameFilter) {
204 |
205 | new ClassFinder()
206 | .filterURLforClass(referenceClass)
207 | .process(
208 | name -> {
209 | if (classNameFilter.test(name)) {
210 | allClassNames.add(name);
211 | }
212 | return false;
213 | },
214 | clazz -> {
215 | });
216 | }
217 |
218 | public static void registerSubclasses(Json json, Class clazz,
219 | Array allClassNames,
220 | Predicate classNameFilter) {
221 |
222 | Predicate filter = classNameFilter != null ? classNameFilter : (name -> true);
223 |
224 | for (int i = 0; i < allClassNames.size; i++) {
225 |
226 | String name = allClassNames.get(i);
227 |
228 | if (filter.test(name)) {
229 | try {
230 | Class> aClazz = Class.forName(name);
231 | if (!clazz.equals(aClazz) && clazz.isAssignableFrom(aClazz)) {
232 | register(json, aClazz.asSubclass(clazz));
233 | }
234 | } catch (ClassNotFoundException ignored) {
235 |
236 | }
237 | }
238 | }
239 | }
240 |
241 | }
242 |
--------------------------------------------------------------------------------
/src/main/java/com/badlogic/gdx/json/AnnotatedJsonObject.java:
--------------------------------------------------------------------------------
1 | package com.badlogic.gdx.json;
2 |
3 | /**
4 | * Optional interface to customize JSON serialization. Classes annotated with
5 | * {@link com.badlogic.gdx.json.annotations.JsonSerializable} can implement this
6 | * interface to hook into the serialization process.
7 | */
8 | public interface AnnotatedJsonObject {
9 |
10 | /**
11 | * This function is called before writing the annotated object.
12 | */
13 | void onJsonWrite();
14 |
15 | /**
16 | * This function is called after reading the annotated object.
17 | */
18 | void onJsonRead();
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/src/main/java/com/badlogic/gdx/json/JsonArraySerializer.java:
--------------------------------------------------------------------------------
1 | package com.badlogic.gdx.json;
2 |
3 | import com.badlogic.gdx.json.annotations.JsonArray;
4 | import com.badlogic.gdx.utils.*;
5 | import com.badlogic.gdx.utils.reflect.ArrayReflection;
6 |
7 | /**
8 | * Implementation of {@link com.badlogic.gdx.utils.Json.Serializer} to serialize {@link Array} containers.
9 | *
10 | * This is used internally by {@link AnnotatedJsonSerializer}.
11 | */
12 | class JsonArraySerializer implements Json.Serializer> {
13 |
14 | private String name;
15 | private JsonArray array;
16 |
17 | JsonArraySerializer(String name, JsonArray array) {
18 | this.name = name;
19 | this.array = array;
20 | }
21 |
22 | @Override
23 | public void write(Json json, Array> object, Class knownType) {
24 |
25 | json.writeArrayStart(name);
26 |
27 | for (int i = 0; i < object.size; i++) {
28 | json.writeValue(object.get(i), array.value());
29 | }
30 |
31 | json.writeArrayEnd();
32 | }
33 |
34 | @Override
35 | @SuppressWarnings("unchecked")
36 | public Array> read(Json json, JsonValue jsonData, Class type) {
37 |
38 | JsonValue entry = jsonData.getChild(name);
39 | JsonValue entry2 = entry;
40 |
41 | // pre-scan size of array
42 |
43 | int size = 0;
44 | while (entry2 != null) {
45 | size++;
46 | entry2 = entry2.next;
47 | }
48 |
49 | // create array, read data
50 |
51 | Array values;
52 |
53 | try {
54 |
55 | V[] items = (V[]) ArrayReflection.newInstance(array.value(), size);
56 |
57 | values = (Array) array.array().newInstance();
58 | values.items = items;
59 | values.ordered = array.ordered();
60 |
61 | } catch (ClassCastException | InstantiationException | IllegalAccessException e) {
62 | throw new GdxRuntimeException(e);
63 | }
64 |
65 | while (entry != null) {
66 |
67 | V value = json.readValue((Class) array.value(), entry);
68 |
69 | values.add(value);
70 |
71 | entry = entry.next;
72 | }
73 |
74 | return values;
75 | }
76 |
77 | }
78 |
--------------------------------------------------------------------------------
/src/main/java/com/badlogic/gdx/json/JsonFloatSerializer.java:
--------------------------------------------------------------------------------
1 | package com.badlogic.gdx.json;
2 |
3 | import com.badlogic.gdx.concurrent.ThreadLocalInstance;
4 | import com.badlogic.gdx.utils.GdxRuntimeException;
5 | import com.badlogic.gdx.utils.StringBuilder;
6 |
7 | import java.util.regex.Matcher;
8 | import java.util.regex.Pattern;
9 |
10 | /**
11 | * Utility functions to (optionally) store floats and doubles using a IEEE-754 bit masks.
12 | *
13 | * Reference: http://the-witness.net/news/2011/12/engine-tech-concurrent-world-editing/
14 | */
15 | public class JsonFloatSerializer {
16 |
17 | private static final String fpRegEx = "(-?[0-9.]+(E[+-][0-9]+)?)";
18 | private static final Pattern purePattern = Pattern.compile("^" + fpRegEx + "$");
19 | private static final Pattern ieeePattern = Pattern.compile("^0x([a-fA-F0-9]+)\\|" + fpRegEx + "$");
20 |
21 | private static final ThreadLocal stringBuilder =
22 | new ThreadLocalInstance<>(() -> new StringBuilder(32));
23 |
24 | public static String encodeFloatBits(float value) {
25 |
26 | StringBuilder builder = stringBuilder.get();
27 |
28 | builder.setLength(0);
29 | builder.append("0x");
30 | builder.append(String.format("%08X", Float.floatToRawIntBits(value)));
31 | builder.append("|");
32 | builder.append(value);
33 |
34 | return builder.toString();
35 | }
36 |
37 | public static float decodeFloatBits(String value, float defaultValue) {
38 |
39 | if (value == null) {
40 | return defaultValue;
41 | }
42 |
43 | Matcher matcher = ieeePattern.matcher(value);
44 |
45 | if (matcher.matches()) {
46 | String group = matcher.group(1);
47 | return Float.intBitsToFloat((int) Long.parseLong(group, 16));
48 | } else {
49 | matcher = purePattern.matcher(value);
50 | if (matcher.matches()) {
51 | String group = matcher.group(1);
52 | return Float.parseFloat(group);
53 | }
54 | }
55 |
56 | throw new GdxRuntimeException("Error parsing float from string: " + value);
57 | }
58 |
59 | public static String encodeDoubleBits(double value) {
60 |
61 | StringBuilder builder = stringBuilder.get();
62 |
63 | builder.setLength(0);
64 | builder.append("0x");
65 | builder.append(String.format("%016X", Double.doubleToRawLongBits(value)));
66 | builder.append("|");
67 | builder.append(value);
68 |
69 | return builder.toString();
70 | }
71 |
72 | public static double decodeDoubleBits(String value, double defaultValue) {
73 |
74 | if (value == null) {
75 | return defaultValue;
76 | }
77 |
78 | Matcher matcher = ieeePattern.matcher(value);
79 |
80 | if (matcher.matches()) {
81 | String group = matcher.group(1);
82 | return Double.longBitsToDouble(Long.parseLong(group, 16));
83 | } else {
84 | matcher = purePattern.matcher(value);
85 | if (matcher.matches()) {
86 | String group = matcher.group(1);
87 | return Double.parseDouble(group);
88 | }
89 | }
90 |
91 | throw new GdxRuntimeException("Error parsing double from string: " + value);
92 | }
93 |
94 | }
95 |
--------------------------------------------------------------------------------
/src/main/java/com/badlogic/gdx/json/JsonMapSerializer.java:
--------------------------------------------------------------------------------
1 | package com.badlogic.gdx.json;
2 |
3 | import com.badlogic.gdx.function.Function;
4 | import com.badlogic.gdx.json.annotations.JsonMap;
5 | import com.badlogic.gdx.utils.*;
6 |
7 | import java.util.Iterator;
8 | import java.util.Map;
9 | import com.badlogic.gdx.function.Supplier;
10 |
11 | /**
12 | * Implementation of {@link com.badlogic.gdx.utils.Json.Serializer} to serialize {@link Map} containers.
13 | *
14 | * This is used internally by {@link AnnotatedJsonSerializer}.
15 | */
16 | class JsonMapSerializer implements Json.Serializer> {
17 |
18 | private String name;
19 | private JsonMap map;
20 |
21 | JsonMapSerializer(String name, JsonMap map) {
22 | this.name = name;
23 | this.map = map;
24 | }
25 |
26 | @Override
27 | public void write(Json json, Iterable> object, Class knownType) {
28 | throw new GdxRuntimeException("Not implemented!");
29 | }
30 |
31 | public void write(Json json, Iterable entries,
32 | Function getKey, Function getValue) {
33 |
34 | Iterator it = entries.iterator();
35 |
36 | json.writeArrayStart(name);
37 |
38 | while (it.hasNext()) {
39 |
40 | E entry = it.next();
41 |
42 | json.writeObjectStart();
43 |
44 | json.writeValue("key", getKey.apply(entry), map.key());
45 | json.writeValue("value", getValue.apply(entry), map.value());
46 |
47 | json.writeObjectEnd();
48 | }
49 |
50 | json.writeArrayEnd();
51 | }
52 |
53 | @Override
54 | public Iterable> read(Json json, JsonValue jsonData, Class type) {
55 | throw new GdxRuntimeException("Not implemented!");
56 | }
57 |
58 | @SuppressWarnings("unchecked")
59 | public Map, ?> read(Json json, JsonValue jsonData) {
60 |
61 | Map values;
62 |
63 | try {
64 | values = (Map) map.map().newInstance();
65 | } catch (InstantiationException | IllegalAccessException e) {
66 | throw new GdxRuntimeException(e);
67 | }
68 |
69 | JsonValue entry = jsonData.getChild(name);
70 |
71 | while (entry != null) {
72 |
73 | JsonValue keyValue = entry.get("key");
74 | K key = json.readValue((Class) map.key(), keyValue);
75 |
76 | JsonValue valueValue = entry.get("value");
77 | V value = json.readValue((Class) map.value(), valueValue);
78 |
79 | values.put(key, value);
80 |
81 | entry = entry.next;
82 | }
83 |
84 | return values;
85 | }
86 |
87 | interface KeyValueConsumer {
88 |
89 | void accept(M map, K key, V value);
90 | }
91 |
92 | @SuppressWarnings("unchecked")
93 | public M read(Json json, JsonValue jsonData,
94 | Supplier newInstance, KeyValueConsumer put) {
95 |
96 | M values = newInstance.get();
97 |
98 | JsonValue entry = jsonData.getChild(name);
99 |
100 | while (entry != null) {
101 |
102 | JsonValue keyValue = entry.get("key");
103 | K key = json.readValue((Class) map.key(), keyValue);
104 |
105 | JsonValue valueValue = entry.get("value");
106 | V value = json.readValue((Class) map.value(), valueValue);
107 |
108 | put.accept(values, key, value);
109 |
110 | entry = entry.next;
111 | }
112 |
113 | return values;
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/src/main/java/com/badlogic/gdx/json/annotations/JsonArray.java:
--------------------------------------------------------------------------------
1 | package com.badlogic.gdx.json.annotations;
2 |
3 | import com.badlogic.gdx.utils.Array;
4 |
5 | import java.lang.annotation.*;
6 |
7 | @Target(ElementType.FIELD)
8 | @Retention(RetentionPolicy.RUNTIME)
9 | public @interface JsonArray {
10 |
11 | /**
12 | * Used to construct the correct container class which must be derived from {@link Array}.
13 | */
14 | Class> array() default Array.class;
15 |
16 | /**
17 | * Specifies class type of array elements.
18 | */
19 | Class> value();
20 |
21 | /**
22 | * Used as parameter during {@link com.badlogic.gdx.utils.Array#Array(boolean, int)}
23 | * construction on deserialization.
24 | */
25 | boolean ordered() default true;
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/src/main/java/com/badlogic/gdx/json/annotations/JsonMap.java:
--------------------------------------------------------------------------------
1 | package com.badlogic.gdx.json.annotations;
2 |
3 | import java.lang.annotation.*;
4 |
5 | @Target(ElementType.FIELD)
6 | @Retention(RetentionPolicy.RUNTIME)
7 | public @interface JsonMap {
8 | Class> map();
9 | Class> key();
10 | Class> value();
11 | }
12 |
--------------------------------------------------------------------------------
/src/main/java/com/badlogic/gdx/json/annotations/JsonSerializable.java:
--------------------------------------------------------------------------------
1 | package com.badlogic.gdx.json.annotations;
2 |
3 | import java.lang.annotation.*;
4 |
5 | /**
6 | * Class annotation used to prepare classes for use with
7 | * {@link com.badlogic.gdx.json.AnnotatedJsonSerializer}.
8 | */
9 | @Inherited
10 | @Target(ElementType.TYPE)
11 | @Retention(RetentionPolicy.RUNTIME)
12 | public @interface JsonSerializable {
13 |
14 | /**
15 | * If dynamic == true, {@link com.badlogic.gdx.json.AnnotatedJsonSerializer} writes additional
16 | * type information for each instance of the annotated class. This type information is then used
17 | * during deserialization to reconstruct the correct (derived) instance type.
18 | */
19 | boolean dynamic() default false;
20 |
21 | /**
22 | * If fullyQualifiedClassTag == true, this class does not register a tag, which means that the fully
23 | * qualified class name is written if dynamic == true.
24 | */
25 | boolean fullyQualifiedClassTag() default false;
26 |
27 | /**
28 | * If encodeFP == true, floats and doubles are written to (and parsed from) JSON as special format strings.
29 | * @see com.badlogic.gdx.json.JsonFloatSerializer
30 | */
31 | boolean encodeFP() default false;
32 |
33 | /**
34 | * By default, null values are not written to JSON.
35 | */
36 | boolean writeNull() default false;
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/src/main/java/com/badlogic/gdx/json/annotations/JsonSerialize.java:
--------------------------------------------------------------------------------
1 | package com.badlogic.gdx.json.annotations;
2 |
3 | import java.lang.annotation.*;
4 |
5 | /**
6 | * Field annotation to mark members of a {@link JsonSerializable} class as subject to serialization.
7 | *
8 | * If the optional "name" property is not set, the identifier of the field is used to name the JSON object.
9 | *
10 | * The {@link JsonArray} property must be set to add type information to serializable fields of type
11 | * {@link com.badlogic.gdx.utils.Array}.
12 | *
13 | * The {@link JsonMap} property must be set to add type information for {@link java.util.Map} containers.
14 | */
15 | @Target(ElementType.FIELD)
16 | @Retention(RetentionPolicy.RUNTIME)
17 | public @interface JsonSerialize {
18 |
19 | String name() default "";
20 |
21 | JsonArray[] array() default {};
22 |
23 | JsonMap[] map() default {};
24 |
25 | /**
26 | * If the JSON value is 'null', or not present at all, an instance is still
27 | * created by default if the annotated field is serializable.
28 | *
29 | * Set this to property to false to prevent that.
30 | */
31 | boolean createIfNull() default true;
32 |
33 | boolean writeIfDefaultValue() default true;
34 |
35 | int defaultIntValue() default 0;
36 |
37 | float defaultFloatValue() default 0.0f;
38 |
39 | double defaultDoubleValue() default 0.0;
40 |
41 | boolean defaultBooleanValue() default false;
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/src/main/java/com/badlogic/gdx/lang/Box.java:
--------------------------------------------------------------------------------
1 | package com.badlogic.gdx.lang;
2 |
3 | import com.badlogic.gdx.concurrent.ThreadLocalInstance;
4 | import com.badlogic.gdx.function.Consumer;
5 |
6 | import java.lang.reflect.Array;
7 |
8 | /**
9 | * Utility class to box typed values. Can be used to capture non-final
10 | * variables for lambda functions.
11 | *
12 | *
13 | * {@code
14 | * int getSum(Iterable container) {
15 | * final Box.Integer sum = new Box.Integer(0);
16 | * container.forEach(element -> sum.set(sum.get() + element));
17 | * return sum.get();
18 | * }
19 | * }
20 | *
21 | *
22 | * For boxed primitive types there is a small, thread-local storage which holds
23 | * one per-thread instance of each sub-class.
24 | *
25 | *
26 | * {@code
27 | * int getSum(Iterable container) {
28 | * final Box.Integer sum = Box.borrowInteger();
29 | * sum.set(0);
30 | * container.forEach(element -> sum.set(sum.get() + element));
31 | * int result = sum.get();
32 | * Box.releaseInteger();
33 | * return result;
34 | * }
35 | * }
36 | *
37 | *
38 | * There's also a version which uses try-with-resources internally.
39 | *
40 | *
41 | * {@code
42 | * int getSum(Iterable container) {
43 | * return Box.withInteger(sum -> {
44 | * sum.set(0);
45 | * container.forEach(element -> sum.set(sum.get() + element));
46 | * });
47 | * }
48 | * }
49 | *
50 | *
51 | */
52 | public final class Box {
53 |
54 | public static final class Boolean {
55 |
56 | private boolean value;
57 |
58 | @SuppressWarnings("unused")
59 | Boolean() {
60 | this.value = false;
61 | }
62 |
63 | public Boolean(boolean value) {
64 | this.value = value;
65 | }
66 |
67 | public boolean get() {
68 | return value;
69 | }
70 |
71 | public Boolean set(boolean value) {
72 | this.value = value;
73 | return this;
74 | }
75 |
76 | }
77 |
78 | public static final class Integer {
79 |
80 | private int value;
81 |
82 | @SuppressWarnings("unused")
83 | Integer() {
84 | this.value = 0;
85 | }
86 |
87 | public Integer(int value) {
88 | this.value = value;
89 | }
90 |
91 | public int get() {
92 | return value;
93 | }
94 |
95 | public int getAndIncrement() {
96 | return value++;
97 | }
98 |
99 | public Integer set(int value) {
100 | this.value = value;
101 | return this;
102 | }
103 |
104 | public Integer add(int value) {
105 | this.value += value;
106 | return this;
107 | }
108 |
109 | }
110 |
111 | public static final class Float {
112 |
113 | private float value;
114 |
115 | @SuppressWarnings("unused")
116 | Float() {
117 | this.value = 0.0f;
118 | }
119 |
120 | public Float(float value) {
121 | this.value = value;
122 | }
123 |
124 | public float get() {
125 | return value;
126 | }
127 |
128 | public Float set(float value) {
129 | this.value = value;
130 | return this;
131 | }
132 |
133 | }
134 |
135 | public static final class Reference {
136 |
137 | private R value;
138 |
139 | public Reference(R value) {
140 | this.value = value;
141 | }
142 |
143 | public R get() {
144 | return value;
145 | }
146 |
147 | public boolean isNull() {
148 | return value == null;
149 | }
150 |
151 | public Reference set(R value) {
152 | this.value = value;
153 | return this;
154 | }
155 |
156 | }
157 |
158 | private static class BorrowChecker implements AutoCloseable {
159 |
160 | private final B[] references;
161 | private int locks = 0;
162 |
163 | private static final int cacheSize = 4;
164 |
165 | @SuppressWarnings("unchecked")
166 | private BorrowChecker(Class clazz) {
167 | try {
168 | references = (B[]) Array.newInstance(clazz, cacheSize);
169 | for (int i = 0; i < cacheSize; i++) {
170 | references[i] = clazz.newInstance();
171 | }
172 | } catch (InstantiationException | IllegalAccessException e) {
173 | throw new RuntimeException(e);
174 | }
175 | }
176 |
177 | BorrowChecker borrow() {
178 | if (locks >= cacheSize) {
179 | throw new RuntimeException("Too many nested borrows!");
180 | }
181 | locks++;
182 | return this;
183 | }
184 |
185 | B reference() {
186 | return references[locks - 1];
187 | }
188 |
189 | @Override
190 | public void close() {
191 | locks--;
192 | }
193 |
194 | }
195 |
196 | private static final ThreadLocal> tlsBoolean =
197 | new ThreadLocalInstance<>(() -> new BorrowChecker<>(Boolean.class));
198 |
199 | private static final ThreadLocal> tlsInteger =
200 | new ThreadLocalInstance<>(() -> new BorrowChecker<>(Integer.class));
201 |
202 | private static final ThreadLocal> tlsFloat =
203 | new ThreadLocalInstance<>(() -> new BorrowChecker<>(Float.class));
204 |
205 | public static Boolean borrowBoolean() {
206 | return tlsBoolean.get().borrow().reference();
207 | }
208 |
209 | public static boolean releaseBoolean() {
210 | BorrowChecker value = tlsBoolean.get();
211 | boolean result = value.reference().get();
212 | value.close();
213 | return result;
214 | }
215 |
216 | public static boolean withBoolean(Consumer consumer) {
217 | try (BorrowChecker value = tlsBoolean.get().borrow()) {
218 | consumer.accept(value.reference());
219 | return value.reference().get();
220 | }
221 | }
222 |
223 | public static Integer borrowInteger() {
224 | return tlsInteger.get().borrow().reference();
225 | }
226 |
227 | public static int releaseInteger() {
228 | BorrowChecker value = tlsInteger.get();
229 | int result = value.reference().get();
230 | value.close();
231 | return result;
232 | }
233 |
234 | public static int withInteger(Consumer consumer) {
235 | try (BorrowChecker value = tlsInteger.get().borrow()) {
236 | consumer.accept(value.reference());
237 | return value.reference().get();
238 | }
239 | }
240 |
241 | public static float withFloat(Consumer consumer) {
242 | try (BorrowChecker value = tlsFloat.get().borrow()) {
243 | consumer.accept(value.reference());
244 | return value.reference().get();
245 | }
246 | }
247 |
248 | }
249 |
--------------------------------------------------------------------------------
/src/main/java/com/badlogic/gdx/lang/ClassFinder.java:
--------------------------------------------------------------------------------
1 | package com.badlogic.gdx.lang;
2 |
3 | import com.badlogic.gdx.files.FileHandle;
4 | import com.badlogic.gdx.function.Consumer;
5 | import com.badlogic.gdx.function.Predicate;
6 | import com.badlogic.gdx.utils.Array;
7 | import com.badlogic.gdx.utils.GdxRuntimeException;
8 |
9 | import java.io.File;
10 | import java.io.IOException;
11 | import java.net.URISyntaxException;
12 | import java.net.URL;
13 | import java.nio.file.Path;
14 | import java.nio.file.Paths;
15 | import java.util.Enumeration;
16 | import java.util.jar.JarEntry;
17 | import java.util.jar.JarFile;
18 |
19 | /**
20 | * Utility class to filter and iterate classes registered to a {@link ClassLoader}.
21 | */
22 | public class ClassFinder {
23 |
24 | private Array urls = new Array<>();
25 |
26 | /**
27 | * Only uses the same URL as the given class was loaded from.
28 | *
29 | * Must be called before {@link ClassFinder#process(Predicate, Consumer)}.
30 | */
31 | public ClassFinder filterURLforClass(Class> clazz) {
32 |
33 | // todo: this may not work because of SecurityManager
34 | URL url = clazz.getProtectionDomain().getCodeSource().getLocation();
35 | urls.add(url);
36 |
37 | return this;
38 | }
39 |
40 | /**
41 | * Iterates classes of all URLs filtered by a previous call of {@link ClassFinder#filterURLforClass(Class)}.
42 | */
43 | public ClassFinder process(Predicate filter, Consumer> processor) {
44 |
45 | for (URL url : urls) {
46 |
47 | Path path;
48 |
49 | // required for Windows paths with spaces
50 | try {
51 | path = Paths.get(url.toURI());
52 | } catch (URISyntaxException e) {
53 | throw new GdxRuntimeException(e);
54 | }
55 |
56 | FileHandle file = new FileHandle(path.toFile());
57 |
58 | if (file.isDirectory()) {
59 |
60 | processDirectory(file, file, filter, processor);
61 |
62 | } else if (file.extension().equals("jar")) {
63 |
64 | try (JarFile jar = new JarFile(file.file())) {
65 |
66 | Enumeration entries = jar.entries();
67 |
68 | while (entries.hasMoreElements()) {
69 |
70 | JarEntry entry = entries.nextElement();
71 |
72 | if (entry.isDirectory()) {
73 | continue;
74 | }
75 |
76 | FileHandle entryFile = new FileHandle(entry.getName());
77 |
78 | process(null, entryFile, filter, processor);
79 | }
80 |
81 | } catch (IOException ignored) {
82 |
83 | }
84 | }
85 |
86 | }
87 |
88 | return this;
89 | }
90 |
91 | /**
92 | * Iterates classes from a {@link ClassFinderCache}.
93 | *
94 | * This is faster than walking URLs/directories. It's also more secure/portable.
95 | */
96 | public ClassFinder process(ClassFinderCache cache, String groupName,
97 | Predicate filter, Consumer> processor) {
98 |
99 | Array classNames = cache.get(groupName);
100 |
101 | if (classNames == null) {
102 | return this;
103 | }
104 |
105 | for (String className : classNames) {
106 | processClass(className, filter, processor);
107 | }
108 |
109 | return this;
110 | }
111 |
112 | private void processDirectory(FileHandle root, FileHandle directory,
113 | Predicate filter, Consumer> processor) {
114 |
115 | FileHandle[] files = directory.list();
116 |
117 | for (FileHandle file : files) {
118 |
119 | if (file.isDirectory()) {
120 | processDirectory(root, new FileHandle(new File(directory.file(), file.name())), filter, processor);
121 | } else {
122 | process(root, file, filter, processor);
123 | }
124 |
125 | }
126 | }
127 |
128 | private void process(FileHandle root, FileHandle file, Predicate filter, Consumer> processor) {
129 |
130 | if (!file.extension().equals("class")) {
131 | return;
132 | }
133 |
134 | FileHandle relative = relativeTo(file, root);
135 |
136 | String className = relative.pathWithoutExtension()
137 | .replace("/", ".");//.replaceAll("\\$.", "");
138 |
139 | processClass(className, filter, processor);
140 | }
141 |
142 | private void processClass(String className, Predicate filter, Consumer> processor) {
143 |
144 | if (!filter.test(className)) {
145 | return;
146 | }
147 |
148 | try {
149 |
150 | Class> clazz = Class.forName(className);
151 | processClass(clazz, filter, processor);
152 |
153 | } catch (ClassNotFoundException ignored) {
154 |
155 | }
156 |
157 | }
158 |
159 | private void processClass(Class> clazz, Predicate filter, Consumer> processor) {
160 |
161 | if (!filter.test(clazz.getName())) {
162 | return;
163 | }
164 |
165 | processor.accept(clazz);
166 |
167 | for (Class> inner : clazz.getDeclaredClasses()) {
168 | processClass(inner, filter, processor);
169 | }
170 |
171 | }
172 |
173 | private FileHandle relativeTo(FileHandle file, FileHandle root) {
174 |
175 | if (root == null) {
176 | return file;
177 | }
178 |
179 | String r = root.path();
180 | String f = file.path();
181 |
182 | if (!f.startsWith(r)) {
183 | return file;
184 | }
185 |
186 | return new FileHandle(f.substring(r.length() + 1));
187 | }
188 | }
189 |
--------------------------------------------------------------------------------
/src/main/java/com/badlogic/gdx/lang/ClassFinderCache.java:
--------------------------------------------------------------------------------
1 | package com.badlogic.gdx.lang;
2 |
3 | import com.badlogic.gdx.files.FileHandle;
4 | import com.badlogic.gdx.files.TextFileUtils;
5 | import com.badlogic.gdx.function.Supplier;
6 | import com.badlogic.gdx.utils.Array;
7 | import com.badlogic.gdx.utils.ObjectMap;
8 |
9 | import java.io.IOException;
10 | import java.io.Writer;
11 |
12 | public class ClassFinderCache {
13 |
14 | private final ObjectMap> classNames = new ObjectMap<>();
15 |
16 | public ClassFinderCache() {
17 |
18 | }
19 |
20 | public ClassFinderCache(FileHandle file) throws IOException {
21 | readFromFile(file);
22 | }
23 |
24 | public Array get(String groupName) {
25 | return classNames.get(groupName);
26 | }
27 |
28 | public void add(String groupName, Supplier> supplier) {
29 | classNames.put(groupName, new Array<>());
30 | classNames.get(groupName).addAll(supplier.get());
31 | }
32 |
33 | public void writeToFile(FileHandle file) throws IOException {
34 |
35 | try (Writer writer = file.writer(false, "UTF-8")) {
36 |
37 | for (ObjectMap.Entry> group : classNames) {
38 |
39 | writer.append(':').append(group.key).append('\n');
40 |
41 | for (String name : group.value) {
42 | writer.append(name).append('\n');
43 | }
44 | }
45 |
46 | writer.flush();
47 | }
48 | }
49 |
50 | private void readFromFile(FileHandle file) throws IOException {
51 |
52 | if (!file.exists()) {
53 | return;
54 | }
55 |
56 | Box.Reference groupName = new Box.Reference<>(null);
57 |
58 | TextFileUtils.readLines(file, line -> {
59 | if (line.startsWith(":")) {
60 | groupName.set(line.substring(1));
61 | classNames.put(groupName.get(), new Array<>());
62 | } else if (!groupName.isNull()) {
63 | classNames.get(groupName.get()).add(line);
64 | }
65 | });
66 | }
67 |
68 | }
69 |
--------------------------------------------------------------------------------
/src/main/java/com/badlogic/gdx/lang/ClassUtils.java:
--------------------------------------------------------------------------------
1 | package com.badlogic.gdx.lang;
2 |
3 | import com.badlogic.gdx.checksum.SHA1;
4 | import com.badlogic.gdx.files.FileStreamReader;
5 | import com.badlogic.gdx.utils.GdxRuntimeException;
6 |
7 | import java.io.IOException;
8 | import java.io.InputStream;
9 | import java.lang.reflect.ParameterizedType;
10 | import java.lang.reflect.Type;
11 |
12 | public class ClassUtils {
13 |
14 | /**
15 | * Uses reflection to obtain the class type of a generic type parameter.
16 | */
17 | public static Class> getClassOfGenericType(Class> genericType, int typeArgument) {
18 |
19 | Type generic = genericType.getGenericSuperclass();
20 | Type type = ((ParameterizedType) generic).getActualTypeArguments()[typeArgument];
21 |
22 | String[] types = type.toString().split(" ");
23 | if (types.length < 2) {
24 | return null;
25 | }
26 |
27 | String className = types[1];
28 |
29 | try {
30 | return Class.forName(className);
31 | } catch (ClassNotFoundException e) {
32 | throw new GdxRuntimeException(e);
33 | }
34 |
35 | }
36 |
37 | /**
38 | * Calculates the SHA-1 hash on the byte code (the .class file content) of the given {@link Class}.
39 | */
40 | public static SHA1 getClassHash(Class> classType) throws IOException {
41 |
42 | String resourcePath = "/" + classType.getName().replace('.', '/') + ".class";
43 | InputStream resource = classType.getResourceAsStream(resourcePath);
44 |
45 | return FileStreamReader.hashStream(resource);
46 | }
47 |
48 | /**
49 | * Calculates the SHA-1 hash on the byte code (the .class file content) of the given {@link Class}.
50 | *
51 | * This version returns {@code defaultHash} if an {@link IOException} is thrown.
52 | */
53 | public static SHA1 getClassHash(Class> classType, SHA1 defaultHash) {
54 | try {
55 | return getClassHash(classType);
56 | } catch (IOException e) {
57 | return defaultHash;
58 | }
59 | }
60 |
61 | }
62 |
--------------------------------------------------------------------------------
/src/main/java/com/badlogic/gdx/lang/FourCC.java:
--------------------------------------------------------------------------------
1 | package com.badlogic.gdx.lang;
2 |
3 | public final class FourCC {
4 |
5 | private int value;
6 |
7 | public FourCC(CharSequence name) {
8 | this(convert(name));
9 | }
10 |
11 | public FourCC(char c0, char c1, char c2, char c3) {
12 | this(convert(c0, c1, c2, c3));
13 | }
14 |
15 | public FourCC(int value) {
16 | this.value = value;
17 | }
18 |
19 | @Override
20 | public int hashCode() {
21 | return value;
22 | }
23 |
24 | @Override
25 | public boolean equals(Object obj) {
26 | if (obj instanceof FourCC) {
27 | return value == ((FourCC) obj).value;
28 | }
29 | return false;
30 | }
31 |
32 | @Override
33 | public String toString() {
34 | return Character.toString((char) ((value >>> 24) & 0xff))
35 | + Character.toString((char) ((value >>> 16) & 0xff))
36 | + Character.toString((char) ((value >>> 8) & 0xff))
37 | + Character.toString((char) (value & 0xff));
38 | }
39 |
40 | private static int convert(CharSequence name) {
41 | return convert(name.charAt(0), name.charAt(1), name.charAt(2), name.charAt(3));
42 | }
43 |
44 | private static int convert(char c0, char c1, char c2, char c3) {
45 | return (c0 << 24) | (c1 << 16) | (c2 << 8) | c3;
46 | }
47 |
48 | }
49 |
--------------------------------------------------------------------------------
/src/main/java/com/badlogic/gdx/profiler/Profiler.java:
--------------------------------------------------------------------------------
1 | package com.badlogic.gdx.profiler;
2 |
3 | import com.badlogic.gdx.function.Consumer;
4 |
5 | /**
6 | * Simple functional interface to scope (wrap begin/end) sample calls.
7 | */
8 | public interface Profiler {
9 |
10 | Sample sampleCPU(String name, boolean aggregate);
11 |
12 | default void sampleCPU(String name, boolean aggregate, T context, Consumer consumer) {
13 | consumer.accept(context);
14 | }
15 |
16 | Sample sampleOpenGL(String name);
17 |
18 | default void sampleOpenGL(String name, T context, Consumer consumer) {
19 | consumer.accept(context);
20 | }
21 |
22 | default void setThreadName(CharSequence name) {
23 |
24 | }
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/src/main/java/com/badlogic/gdx/profiler/RemoteryLwjgl.java:
--------------------------------------------------------------------------------
1 | package com.badlogic.gdx.profiler;
2 |
3 | import com.badlogic.gdx.function.Consumer;
4 | import org.lwjgl.PointerBuffer;
5 | import org.lwjgl.system.MemoryStack;
6 | import org.lwjgl.util.remotery.Remotery;
7 | import org.lwjgl.util.remotery.RemoteryGL;
8 |
9 | public class RemoteryLwjgl implements Profiler {
10 |
11 | private static class SampleCPU extends Sample {
12 |
13 | @Override
14 | public void end() {
15 | Remotery.rmt_EndCPUSample();
16 | }
17 | }
18 |
19 | private static class SampleOGL extends Sample {
20 |
21 | @Override
22 | public void end() {
23 | RemoteryGL.rmt_EndOpenGLSample();
24 | }
25 | }
26 |
27 | private static long globalInstance = 0L;
28 | private static final Sample defaultSampleCPU = new SampleCPU();
29 | private static final Sample defaultSampleOGL = new SampleOGL();
30 |
31 | public static void initialize() {
32 |
33 | try (MemoryStack stack = MemoryStack.stackPush()) {
34 |
35 | PointerBuffer pb = stack.callocPointer(1);
36 | Remotery.rmt_CreateGlobalInstance(pb);
37 |
38 | globalInstance = pb.get(0);
39 |
40 | RemoteryGL.rmt_BindOpenGL();
41 |
42 | Remotery.rmt_SetCurrentThreadName("Render");
43 | }
44 | }
45 |
46 | public static void shutdown() {
47 |
48 | if (globalInstance != 0L) {
49 |
50 | RemoteryGL.rmt_UnbindOpenGL();
51 |
52 | Remotery.rmt_DestroyGlobalInstance(globalInstance);
53 | globalInstance = 0L;
54 | }
55 | }
56 |
57 | @Override
58 | public Sample sampleCPU(String name, boolean aggregate) {
59 | int flags = aggregate ? Remotery.RMTSF_Aggregate : Remotery.RMTSF_None;
60 | Remotery.rmt_BeginCPUSample(name, flags, null);
61 | return defaultSampleCPU;
62 | }
63 |
64 | @Override
65 | public void sampleCPU(String name, boolean aggregate, T context, Consumer consumer) {
66 |
67 | int flags = aggregate ? Remotery.RMTSF_Aggregate : Remotery.RMTSF_None;
68 | Remotery.rmt_BeginCPUSample(name, flags, null);
69 |
70 | consumer.accept(context);
71 |
72 | Remotery.rmt_EndCPUSample();
73 | }
74 |
75 | @Override
76 | public Sample sampleOpenGL(String name) {
77 | RemoteryGL.rmt_BeginOpenGLSample(name, null);
78 | return defaultSampleOGL;
79 | }
80 |
81 | @Override
82 | public void sampleOpenGL(String name, T context, Consumer consumer) {
83 |
84 | RemoteryGL.rmt_BeginOpenGLSample(name, null);
85 |
86 | consumer.accept(context);
87 |
88 | RemoteryGL.rmt_EndOpenGLSample();
89 | }
90 |
91 | @Override
92 | public void setThreadName(CharSequence name) {
93 | Remotery.rmt_SetCurrentThreadName(name);
94 | }
95 |
96 | }
97 |
--------------------------------------------------------------------------------
/src/main/java/com/badlogic/gdx/profiler/Sample.java:
--------------------------------------------------------------------------------
1 | package com.badlogic.gdx.profiler;
2 |
3 | public abstract class Sample implements AutoCloseable {
4 |
5 | @Override
6 | public void close() {
7 | end();
8 | }
9 |
10 | public abstract void end();
11 |
12 | }
13 |
--------------------------------------------------------------------------------
/src/main/java/com/badlogic/gdx/random/RandomNumberGenerator.java:
--------------------------------------------------------------------------------
1 | package com.badlogic.gdx.random;
2 |
3 | public interface RandomNumberGenerator {
4 |
5 | long next();
6 |
7 | void seed(long s0, long s1);
8 |
9 | void getSeed(long[] seed);
10 |
11 | }
12 |
--------------------------------------------------------------------------------
/src/main/java/com/badlogic/gdx/random/RandomNumbers.java:
--------------------------------------------------------------------------------
1 | package com.badlogic.gdx.random;
2 |
3 | import com.badlogic.gdx.math.RandomXS128;
4 | import com.badlogic.gdx.utils.Array;
5 |
6 | public final class RandomNumbers {
7 |
8 | private final RandomNumberGenerator generator;
9 |
10 | public RandomNumbers(RandomXS128 generator) {
11 | this(new RandomNumberGenerator() {
12 | @Override
13 | public long next() {
14 | return generator.nextLong();
15 | }
16 |
17 | @Override
18 | public void seed(long s0, long s1) {
19 | generator.setState(s0, s1);
20 | }
21 |
22 | @Override
23 | public void getSeed(long[] seed) {
24 | seed[0] = generator.getState(0);
25 | seed[1] = generator.getState(1);
26 | }
27 | });
28 | }
29 |
30 | public RandomNumbers(RandomNumberGenerator generator) {
31 | this.generator = generator;
32 | }
33 |
34 | public int nextInt() {
35 | return (int) generator.next();
36 | }
37 |
38 | public int nextInt(final int n) {
39 | return (int) nextLongExclusive(n + 1);
40 | }
41 |
42 | /**
43 | * @return random number between start and end (including end value)
44 | */
45 | public int nextInt(int start, int end) {
46 | return start + nextInt(end - start);
47 | }
48 |
49 | public long nextLong() {
50 | return generator.next();
51 | }
52 |
53 | public long nextLong(long n) {
54 | return nextLongExclusive(n + 1);
55 | }
56 |
57 | private long nextLongExclusive(final long n) {
58 |
59 | if (n <= 0) {
60 | throw new IllegalArgumentException("n must be positive");
61 | }
62 |
63 | long t = generator.next();
64 | final long nMinus1 = n - 1;
65 |
66 | if ((n & nMinus1) == 0) {
67 | return t & nMinus1;
68 | }
69 |
70 | for (long u = t >>> 1; u + nMinus1 - (t = u % n) < 0; u = generator.next() >>> 1) {
71 | /* empty */
72 | }
73 |
74 | return t;
75 | }
76 |
77 | public double nextDouble() {
78 | return Double.longBitsToDouble(generator.next() >>> 12 | 0x3FFL << 52) - 1.0;
79 | }
80 |
81 | public float nextFloat() {
82 | return Float.intBitsToFloat((int) (generator.next() >>> 41) | 0x3F8 << 20) - 1.0f;
83 | }
84 |
85 | public boolean nextBoolean() {
86 | return (generator.next() & 1) != 0;
87 | }
88 |
89 | /**
90 | * Pick a random array member. Returns null if the array is empty.
91 | */
92 | public T nextInArray(Array array) {
93 |
94 | if (array.size == 0) {
95 | return null;
96 | }
97 |
98 | return array.get(nextInt(array.size - 1));
99 | }
100 |
101 | /**
102 | * Uses reflection to return a random enum value. Has a relatively large runtime overhead.
103 | */
104 | public > T nextEnum(Class enumType) {
105 | T[] values = enumType.getEnumConstants();
106 | return values[nextInt(values.length - 1)];
107 | }
108 |
109 | public void seed(long s0, long s1) {
110 | generator.seed(s0, s1);
111 | }
112 |
113 | public void getSeed(long[] seed) {
114 | generator.getSeed(seed);
115 | }
116 |
117 | }
118 |
--------------------------------------------------------------------------------
/src/main/java/com/badlogic/gdx/random/XoRoShiRo128Plus.java:
--------------------------------------------------------------------------------
1 | package com.badlogic.gdx.random;
2 |
3 | /**
4 | * Slim Java implementation of the xoroshiro128plus pseudo-random number generator
5 | * written in 2016 by David Blackman and Sebastiano Vigna.
6 | *
7 | * http://xoroshiro.di.unimi.it/
8 | * http://dsiutils.di.unimi.it/
9 | */
10 | public final class XoRoShiRo128Plus implements RandomNumberGenerator {
11 |
12 | private long s0, s1;
13 | private final static long[] JUMP = { 0xbeac0467eba5facbL, 0xd86b048b86aa9922L };
14 |
15 | /**
16 | * "The state must be seeded so that it is not everywhere zero."
17 | */
18 | public XoRoShiRo128Plus(long s0, long s1) {
19 | seed(s0, s1);
20 | }
21 |
22 | @Override
23 | public long next() {
24 | long s0 = this.s0;
25 | long s1 = this.s1;
26 | long result = s0 + s1;
27 |
28 | s1 ^= s0;
29 | this.s0 = Long.rotateLeft(s0, 55) ^ s1 ^ (s1 << 14);
30 | this.s1 = Long.rotateLeft(s1, 36);
31 |
32 | return result;
33 | }
34 |
35 | public void jump() {
36 | long s0 = 0;
37 | long s1 = 0;
38 |
39 | for (int i = 0; i < JUMP.length; i++) {
40 | for (int b = 0; b < 64; b++) {
41 | if ((JUMP[i] & 1L << b) != 0) {
42 | s0 ^= this.s0;
43 | s1 ^= this.s1;
44 | }
45 | next();
46 | }
47 | }
48 |
49 | this.s0 = s0;
50 | this.s1 = s1;
51 | }
52 |
53 | public void seed(long s0, long s1) {
54 | this.s0 = s0;
55 | this.s1 = s1;
56 | }
57 |
58 | @Override
59 | public void getSeed(long[] out) {
60 | out[0] = s0;
61 | out[1] = s1;
62 | }
63 |
64 | }
65 |
66 |
--------------------------------------------------------------------------------
/src/main/java/com/badlogic/gdx/scenes/scene2d/ui/ActorLayout.java:
--------------------------------------------------------------------------------
1 | package com.badlogic.gdx.scenes.scene2d.ui;
2 |
3 |
4 | import com.badlogic.gdx.checksum.CRC32;
5 | import com.badlogic.gdx.json.AnnotatedJsonObject;
6 | import com.badlogic.gdx.json.annotations.JsonSerializable;
7 | import com.badlogic.gdx.json.annotations.JsonSerialize;
8 | import com.badlogic.gdx.scenes.scene2d.Actor;
9 | import com.badlogic.gdx.scenes.scene2d.Touchable;
10 | import com.badlogic.gdx.utils.GdxRuntimeException;
11 |
12 | @JsonSerializable(dynamic = true)
13 | public abstract class ActorLayout implements AnnotatedJsonObject {
14 |
15 | @JsonSerialize
16 | public String name;
17 |
18 | @JsonSerialize
19 | public String style = "default";
20 |
21 | @JsonSerialize
22 | public BaseLayout layout;
23 |
24 | @JsonSerialize
25 | public boolean visible = true;
26 |
27 | ActorLayout(Class actorClass) {
28 | this.actorClass = actorClass;
29 | }
30 |
31 | @Override
32 | public void onJsonWrite() {
33 | /* not implemented */
34 | }
35 |
36 | public int nameId;
37 | Class actorClass;
38 |
39 | @Override
40 | public void onJsonRead() {
41 |
42 | if (layout == null) {
43 | layout = new BaseLayout();
44 | }
45 |
46 | if (name == null) {
47 | throw new GdxRuntimeException("ActorLayout name is null. It needs a name field!");
48 | }
49 |
50 | nameId = CRC32.calculateString(name).hashCode();
51 | }
52 |
53 | protected Actor create(Skin skin, StageLayoutListener listener) {
54 |
55 | // create actor, using layout data
56 | T actor = createActor(skin, listener);
57 |
58 | // set name for later lookups
59 | actor.setName(name);
60 |
61 | actor.setTouchable(layout.touchable ? Touchable.enabled : Touchable.disabled);
62 | actor.setVisible(visible);
63 |
64 | return actor;
65 | }
66 |
67 | protected abstract T createActor(Skin skin, StageLayoutListener listener);
68 |
69 | }
--------------------------------------------------------------------------------
/src/main/java/com/badlogic/gdx/scenes/scene2d/ui/BaseLayout.java:
--------------------------------------------------------------------------------
1 | package com.badlogic.gdx.scenes.scene2d.ui;
2 |
3 | import com.badlogic.gdx.json.annotations.JsonSerializable;
4 | import com.badlogic.gdx.json.annotations.JsonSerialize;
5 | import com.badlogic.gdx.math.MathUtils;
6 | import com.badlogic.gdx.math.Vector2;
7 | import com.badlogic.gdx.scenes.scene2d.*;
8 | import com.badlogic.gdx.utils.viewport.Viewport;
9 |
10 | @JsonSerializable
11 | public class BaseLayout {
12 |
13 | @JsonSerialize
14 | public int x = 0;
15 |
16 | @JsonSerialize
17 | public int y = 0;
18 |
19 | @JsonSerialize
20 | public int width = 0;
21 |
22 | @JsonSerialize
23 | public int height = 0;
24 |
25 | @JsonSerialize
26 | public Align anchor = Align.Center;
27 |
28 | @JsonSerialize
29 | public boolean touchable = true;
30 |
31 | public enum Align {
32 | Center,
33 | Left,
34 | Right,
35 | Bottom,
36 | Top,
37 | BottomLeft,
38 | BottomRight,
39 | TopLeft,
40 | TopRight
41 | }
42 |
43 | void resize(Stage stage, Actor actor, boolean root) {
44 |
45 | int parentWidth, parentHeight;
46 |
47 | if (root) {
48 | Viewport viewport = stage.getViewport();
49 |
50 | parentWidth = viewport.getScreenWidth();
51 | parentHeight = viewport.getScreenHeight();
52 |
53 | } else {
54 | Actor parent = actor.getParent();
55 |
56 | parentWidth = MathUtils.floor(parent.getWidth());
57 | parentHeight = MathUtils.floor(parent.getHeight());
58 | }
59 |
60 | int actorWidth = MathUtils.floor(actor.getWidth());
61 | int actorHeight = MathUtils.floor(actor.getHeight());
62 |
63 | /** Dynamic sizing of actor if we want percentage of parent (indicated by negative width) */
64 | if(width < 0){
65 | actorWidth = MathUtils.clamp(-width, 1, 100) * parentWidth / 100;
66 |
67 | actor.setWidth(actorWidth);
68 | }
69 | if(height < 0){
70 | actorHeight = MathUtils.clamp(-height, 1, 100) * parentHeight / 100;
71 |
72 | actor.setHeight(actorHeight);
73 | }
74 |
75 | int px = 0, py = 0;
76 |
77 | switch (anchor) {
78 |
79 | case Center:
80 | px = parentWidth / 2 - actorWidth / 2;
81 | py = parentHeight / 2 - actorHeight / 2;
82 | break;
83 |
84 | case Left:
85 | px = 0;
86 | py = parentHeight / 2 - actorHeight / 2;
87 | break;
88 |
89 | case Right:
90 | px = parentWidth - actorWidth;
91 | py = parentHeight / 2 - actorHeight / 2;
92 | break;
93 |
94 | case Bottom:
95 | px = parentWidth / 2 - actorWidth / 2;
96 | py = 0;
97 | break;
98 |
99 | case Top:
100 | px = parentWidth / 2 - actorWidth / 2;
101 | py = parentHeight - actorHeight;
102 | break;
103 |
104 | case BottomLeft:
105 | px = 0;
106 | py = 0;
107 | break;
108 |
109 | case BottomRight:
110 | px = parentWidth - actorWidth;
111 | py = 0;
112 | break;
113 |
114 | case TopLeft:
115 | px = 0;
116 | py = parentHeight - actorHeight;
117 | break;
118 |
119 | case TopRight:
120 | px = parentWidth - actorWidth;
121 | py = parentHeight - actorHeight;
122 | break;
123 | }
124 |
125 | px += x;
126 | py += y;
127 |
128 | actor.setPosition(px, py);
129 | }
130 |
131 | }
132 |
--------------------------------------------------------------------------------
/src/main/java/com/badlogic/gdx/scenes/scene2d/ui/ButtonLayout.java:
--------------------------------------------------------------------------------
1 | package com.badlogic.gdx.scenes.scene2d.ui;
2 |
3 | import com.badlogic.gdx.scenes.scene2d.InputEvent;
4 | import com.badlogic.gdx.scenes.scene2d.utils.ClickListener;
5 |
6 | @SuppressWarnings("unused")
7 | public class ButtonLayout extends ActorLayout {
8 |
9 | public ButtonLayout() {
10 | super(Button.class);
11 | }
12 |
13 | @Override
14 | protected Button createActor(Skin skin, StageLayoutListener listener) {
15 |
16 | Button button = new Button(skin, style);
17 | button.addListener(new StageLayoutClickListener(listener, nameId));
18 |
19 | return button;
20 | }
21 |
22 | public static class StageLayoutClickListener extends ClickListener {
23 |
24 | private StageLayoutListener listener;
25 | private int nameId;
26 |
27 | public StageLayoutClickListener(StageLayoutListener listener, int nameId) {
28 | this.listener = listener;
29 | this.nameId = nameId;
30 | }
31 |
32 | @Override
33 | public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) {
34 | if (super.touchDown(event, x, y, pointer, button)) {
35 | listener.onButtonDown(nameId);
36 | return true;
37 | }
38 | return false;
39 | }
40 |
41 | @Override
42 | public void touchUp(InputEvent event, float x, float y, int pointer, int button) {
43 | super.touchUp(event, x, y, pointer, button);
44 | listener.onButtonUp(nameId);
45 | }
46 |
47 | @Override
48 | public boolean mouseMoved(InputEvent event, float x, float y) {
49 | listener.onMouseOver(nameId);
50 | return super.mouseMoved(event, x, y);
51 | }
52 | }
53 |
54 | }
55 |
--------------------------------------------------------------------------------
/src/main/java/com/badlogic/gdx/scenes/scene2d/ui/ContainerLayout.java:
--------------------------------------------------------------------------------
1 | package com.badlogic.gdx.scenes.scene2d.ui;
2 |
3 | @SuppressWarnings("unused")
4 | public class ContainerLayout extends ActorLayout {
5 |
6 | public ContainerLayout() {
7 | super(Container.class);
8 | }
9 |
10 | @Override
11 | protected Container createActor(Skin skin, StageLayoutListener listener) {
12 | Container container;
13 |
14 | container = new Container();
15 | container.setSize(layout.width, layout.height);
16 |
17 | return container;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/main/java/com/badlogic/gdx/scenes/scene2d/ui/GroupLayout.java:
--------------------------------------------------------------------------------
1 | package com.badlogic.gdx.scenes.scene2d.ui;
2 |
3 | import com.badlogic.gdx.json.annotations.JsonArray;
4 | import com.badlogic.gdx.json.annotations.JsonSerialize;
5 | import com.badlogic.gdx.scenes.scene2d.Actor;
6 | import com.badlogic.gdx.scenes.scene2d.Group;
7 | import com.badlogic.gdx.utils.Array;
8 |
9 | public class GroupLayout extends ActorLayout {
10 |
11 | @JsonSerialize(array = @JsonArray(value = ActorLayout.class))
12 | public Array> actors;
13 |
14 | @JsonSerialize
15 | public String sizeToActor = null;
16 |
17 | public GroupLayout() {
18 | super(LayoutGroup.class);
19 | }
20 |
21 | @Override
22 | protected Actor create(Skin skin, StageLayoutListener listener) {
23 | Group group = (Group)super.create(skin, listener);
24 |
25 | for(int i=0; i 0)
40 | group.setWidth(layout.width);
41 | if(layout.height > 0)
42 | group.setHeight(layout.height);
43 | }
44 |
45 | return group;
46 | }
47 |
48 | @Override
49 | protected LayoutGroup createActor(Skin skin, StageLayoutListener listener) {
50 | return new LayoutGroup();
51 | }
52 |
53 | }
54 |
--------------------------------------------------------------------------------
/src/main/java/com/badlogic/gdx/scenes/scene2d/ui/ImageLayout.java:
--------------------------------------------------------------------------------
1 | package com.badlogic.gdx.scenes.scene2d.ui;
2 |
3 | import com.badlogic.gdx.json.annotations.JsonSerialize;
4 |
5 | @SuppressWarnings("unused")
6 | public class ImageLayout extends ActorLayout {
7 |
8 | @JsonSerialize
9 | public String drawable;
10 |
11 | public ImageLayout() {
12 | super(Image.class);
13 | }
14 |
15 | @Override
16 | protected Image createActor(Skin skin, StageLayoutListener listener) {
17 | Image image = new Image(skin, drawable);
18 |
19 | if (layout.width > 0) {
20 | image.setWidth(layout.width);
21 | }
22 |
23 | if (layout.height > 0) {
24 | image.setHeight(layout.height);
25 | }
26 |
27 | return image;
28 | }
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/src/main/java/com/badlogic/gdx/scenes/scene2d/ui/LabelLayout.java:
--------------------------------------------------------------------------------
1 | package com.badlogic.gdx.scenes.scene2d.ui;
2 |
3 | import com.badlogic.gdx.json.annotations.JsonSerialize;
4 |
5 | import static com.badlogic.gdx.utils.Align.*;
6 |
7 | @SuppressWarnings("unused")
8 | public class LabelLayout extends ActorLayout {
9 |
10 | @JsonSerialize
11 | public String label;
12 |
13 | @JsonSerialize
14 | public Align align = Align.Center;
15 |
16 | @JsonSerialize
17 | public boolean wrap = false;
18 |
19 | public enum Align {
20 | Center,
21 | Left,
22 | Right
23 | }
24 |
25 | public LabelLayout() {
26 | super(Label.class);
27 | }
28 |
29 | @Override
30 | protected Label createActor(Skin skin,
31 | StageLayoutListener listener) {
32 |
33 | String text = listener.getTranslation(label);
34 | Label actor = new Label(text, skin, style);
35 |
36 | switch (align) {
37 | case Center:
38 | actor.setAlignment(center);
39 | break;
40 | case Left:
41 | actor.setAlignment(left);
42 | break;
43 | case Right:
44 | actor.setAlignment(right);
45 | break;
46 | }
47 |
48 | actor.setWrap(wrap);
49 |
50 | if (layout.width > 0)
51 | actor.setWidth(layout.width);
52 | if (layout.height > 0)
53 | actor.setHeight(layout.height);
54 |
55 | return actor;
56 | }
57 |
58 | }
59 |
--------------------------------------------------------------------------------
/src/main/java/com/badlogic/gdx/scenes/scene2d/ui/LayoutGroup.java:
--------------------------------------------------------------------------------
1 | package com.badlogic.gdx.scenes.scene2d.ui;
2 |
3 | import com.badlogic.gdx.scenes.scene2d.Group;
4 |
5 | public class LayoutGroup extends Group {
6 |
7 | }
8 |
--------------------------------------------------------------------------------
/src/main/java/com/badlogic/gdx/scenes/scene2d/ui/StageLayout.java:
--------------------------------------------------------------------------------
1 | package com.badlogic.gdx.scenes.scene2d.ui;
2 |
3 | import com.badlogic.gdx.files.FileHandle;
4 | import com.badlogic.gdx.json.AnnotatedJson;
5 | import com.badlogic.gdx.json.annotations.*;
6 | import com.badlogic.gdx.scenes.scene2d.Actor;
7 | import com.badlogic.gdx.scenes.scene2d.Stage;
8 | import com.badlogic.gdx.utils.*;
9 |
10 | import java.io.IOException;
11 |
12 | public class StageLayout {
13 |
14 | private final Stage stage;
15 | private final StageLayout.JsonData json;
16 | private final StageLayoutListener listener;
17 |
18 | private Array rootActors = new Array<>();
19 |
20 | public StageLayout(Stage stage, FileHandle layoutPath, StageLayoutListener listener) throws IOException {
21 | this.stage = stage;
22 | this.json = readJson(layoutPath);
23 | this.listener = listener;
24 | }
25 |
26 | public void create(Skin skin) {
27 | rootActors.clear();
28 | for (ActorLayout> layout : json.actors) {
29 | Actor actor = create(skin, layout, layout.actorClass);
30 | stage.addActor(actor);
31 | }
32 | }
33 |
34 | private T create(Skin skin, ActorLayout layout, Class clazz) {
35 | Actor actor = layout.create(skin, listener);
36 |
37 | if (!clazz.isAssignableFrom(actor.getClass())) {
38 | throw new GdxRuntimeException("Type mismatch for actor layout: " + layout.name);
39 | }
40 |
41 | rootActors.add(actor);
42 |
43 | return (T) actor;
44 | }
45 |
46 | public T get(String name, Class clazz) {
47 | return get(rootActors, name, clazz);
48 | }
49 |
50 | private T get(Array actors, String name, Class clazz) {
51 | for (Actor actor : actors) {
52 | if(actor.getName() == null)
53 | continue;
54 |
55 | if (actor.getName().equals(name)) {
56 | if (!clazz.isAssignableFrom(actor.getClass())) {
57 | throw new GdxRuntimeException("Type mismatch for actor layout: " + name);
58 | }
59 |
60 | return (T) actor;
61 | }
62 |
63 | if (actor instanceof LayoutGroup) {
64 | LayoutGroup group = (LayoutGroup) actor;
65 |
66 | T foundActor = get(group.getChildren(), name, clazz);
67 | if (foundActor != null)
68 | return foundActor;
69 | }
70 | }
71 |
72 | return null;
73 | }
74 |
75 | public Array getRootActors() {
76 | return rootActors;
77 | }
78 |
79 | public void resizeAll() {
80 | for (Actor actor : rootActors) {
81 | resize(actor, true);
82 | }
83 | }
84 |
85 | public void resize(Actor actor, boolean root) {
86 | if(actor.getName() == null)
87 | return;
88 |
89 | // Resize this actor layout
90 | ActorLayout> actorLayout = getLayoutByName(json.actors, actor.getName());
91 |
92 | if (actorLayout == null) {
93 | throw new GdxRuntimeException("No actor layout found for: " + actor.getName());
94 | }
95 |
96 | actorLayout.layout.resize(stage, actor, root);
97 |
98 | // If it happens to be a group, also resize any children
99 | if (actor instanceof LayoutGroup) {
100 | LayoutGroup groupActor = (LayoutGroup) actor;
101 |
102 | SnapshotArray children = groupActor.getChildren();
103 |
104 | for (int i = 0; i < children.size; i++) {
105 | resize(children.get(i), false);
106 | }
107 | }
108 | }
109 |
110 | public ActorLayout> getLayoutByName(String name) {
111 | return getLayoutByName(json.actors, name);
112 | }
113 |
114 | private ActorLayout> getLayoutByName(Array> layouts, String name) {
115 | if (name == null)
116 | return null;
117 |
118 | for (ActorLayout layout : layouts) {
119 | if (name.equals(layout.name)) {
120 | return layout;
121 | }
122 |
123 | if (layout instanceof GroupLayout) {
124 | GroupLayout groupLayout = (GroupLayout) layout;
125 |
126 | ActorLayout actorLayout = getLayoutByName(groupLayout.actors, name);
127 | if (actorLayout != null)
128 | return actorLayout;
129 | }
130 | }
131 |
132 | return null;
133 | }
134 |
135 | @JsonSerializable
136 | @SuppressWarnings("WeakerAccess")
137 | public static class JsonData {
138 |
139 | @JsonSerialize(array = @JsonArray(value = ActorLayout.class))
140 | public Array> actors;
141 | }
142 |
143 | private static final Json reader = AnnotatedJson.newReader(JsonData.class, StageLayout::setupJson);
144 |
145 | private static void setupJson(Json json) {
146 | AnnotatedJson.registerSubclasses(json, ActorLayout.class,
147 | className -> className.startsWith("com.badlogic.gdx.scenes.scene2d.ui"));
148 | }
149 |
150 | private static JsonData readJson(FileHandle path) throws IOException {
151 | return AnnotatedJson.read(path, JsonData.class, reader);
152 | }
153 | }
154 |
--------------------------------------------------------------------------------
/src/main/java/com/badlogic/gdx/scenes/scene2d/ui/StageLayoutAdapter.java:
--------------------------------------------------------------------------------
1 | package com.badlogic.gdx.scenes.scene2d.ui;
2 |
3 | public class StageLayoutAdapter implements StageLayoutListener {
4 |
5 | protected StageLayoutListener listener;
6 |
7 | public StageLayoutAdapter(StageLayoutListener listener) {
8 | this.listener = listener;
9 | }
10 |
11 | @Override
12 | public void onButtonDown(int layoutId) {
13 | listener.onButtonDown(layoutId);
14 | }
15 |
16 | @Override
17 | public void onButtonUp(int layoutId) {
18 | listener.onButtonUp(layoutId);
19 | }
20 |
21 | @Override
22 | public void onMouseOver(int layoutId) {
23 | listener.onMouseOver(layoutId);
24 | }
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/src/main/java/com/badlogic/gdx/scenes/scene2d/ui/StageLayoutListener.java:
--------------------------------------------------------------------------------
1 | package com.badlogic.gdx.scenes.scene2d.ui;
2 |
3 | public interface StageLayoutListener {
4 |
5 | default void onButtonDown(int layoutId) {
6 |
7 | }
8 |
9 | default void onButtonUp(int layoutId) {
10 |
11 | }
12 |
13 | /**
14 | * Called on mouseMoved() events when mouse pointer is hovering this button.
15 | */
16 | default void onMouseOver(int layoutId) {
17 |
18 | }
19 |
20 | /**
21 | * Used by {@link LabelLayout} to localize the label text.
22 | */
23 | default String getTranslation(String text) {
24 | return text;
25 | }
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/src/main/java/com/badlogic/gdx/scenes/scene2d/ui/TextButtonLayout.java:
--------------------------------------------------------------------------------
1 | package com.badlogic.gdx.scenes.scene2d.ui;
2 |
3 | import com.badlogic.gdx.json.annotations.JsonSerialize;
4 |
5 | @SuppressWarnings("unused")
6 | public class TextButtonLayout extends ActorLayout {
7 |
8 | @JsonSerialize
9 | public String label;
10 |
11 | public TextButtonLayout() {
12 | super(TextButton.class);
13 | }
14 |
15 | @Override
16 | protected TextButton createActor(Skin skin, StageLayoutListener listener) {
17 | return new TextButton(label, skin, style);
18 | }
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/src/main/java/com/badlogic/gdx/scenes/scene2d/ui/TreeTools.java:
--------------------------------------------------------------------------------
1 | package com.badlogic.gdx.scenes.scene2d.ui;
2 |
3 | import com.badlogic.gdx.scenes.scene2d.InputEvent;
4 | import com.badlogic.gdx.scenes.scene2d.utils.ClickListener;
5 |
6 | public final class TreeTools {
7 |
8 | public static ClickListener createNodeClickListener(Tree tree, Runnable clicked) {
9 |
10 | return new ClickListener() {
11 |
12 | @Override
13 | public void clicked(InputEvent event, float x, float y) {
14 | super.clicked(event, x, y);
15 |
16 | Tree.Node node = tree.getNodeAt(y);
17 |
18 | if (node != null && node.getChildren().size > 0) {
19 | node.setExpanded(!node.isExpanded());
20 | tree.getClickListener().clicked(event, x, y);
21 | }
22 |
23 | clicked.run();
24 | }
25 | };
26 | }
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/src/main/java/com/badlogic/gdx/utils/ArrayUtils.java:
--------------------------------------------------------------------------------
1 | package com.badlogic.gdx.utils;
2 |
3 | import com.badlogic.gdx.function.Consumer;
4 | import com.badlogic.gdx.function.Predicate;
5 | import com.badlogic.gdx.random.RandomNumbers;
6 |
7 | import java.util.*;
8 |
9 | /**
10 | * Array utility functions.
11 | *
12 | * Note: The functions to modify arrays are mostly written for convenience, not for efficiency. They should not be
13 | * used in code sensitive to memory consumption or execution speed.
14 | */
15 | public class ArrayUtils {
16 |
17 | /**
18 | * Convenience function to iterate an array.
19 | */
20 | public static void forEach(T[] array, Consumer action) {
21 | for (T t : array) {
22 | action.accept(t);
23 | }
24 | }
25 |
26 | /**
27 | * Convenience function to iterate an array. Stops if user-supplied function returns false.
28 | *
29 | * @return true if end of array was reached
30 | */
31 | public static