gltrace: manage texture image state changes
Texture Data is provided via glTexImage2D. Parts of a created
texture may then be updated using glTexSubImage2D. This patch
adds a GL state variable that maintains a path to the current
texture image for each texture.
This patch also includes a few other misc. changes:
- Duration minimap: do not create back buffer image of size 0.
In such a case, just don't draw anything.
- In the function trace view, selecting a particular item in
the table doesn't also scroll it into view on Mac. Add
a setTopIndex to fix this.
- add Guava is a dependency
- Apply the state transformations in a separate Eclipse job.
Change-Id: I53f5a0438217d9d086b844f7d333306f7c9fbccd
diff --git a/eclipse/plugins/com.android.ide.eclipse.gldebugger/.classpath b/eclipse/plugins/com.android.ide.eclipse.gldebugger/.classpath
index 06be95e..39c4185 100755
--- a/eclipse/plugins/com.android.ide.eclipse.gldebugger/.classpath
+++ b/eclipse/plugins/com.android.ide.eclipse.gldebugger/.classpath
@@ -7,5 +7,6 @@
<classpathentry kind="lib" path="libs/host-libprotobuf-java-2.3.0-lite.jar"/>
<classpathentry kind="lib" path="libs/liblzf.jar"/>
<classpathentry kind="lib" path="libs/sdklib.jar"/>
+ <classpathentry kind="lib" path="libs/guava-10.0.1.jar"/>
<classpathentry kind="output" path="bin"/>
</classpath>
diff --git a/eclipse/plugins/com.android.ide.eclipse.gldebugger/META-INF/MANIFEST.MF b/eclipse/plugins/com.android.ide.eclipse.gldebugger/META-INF/MANIFEST.MF
index feb16bb..fcaceb7 100644
--- a/eclipse/plugins/com.android.ide.eclipse.gldebugger/META-INF/MANIFEST.MF
+++ b/eclipse/plugins/com.android.ide.eclipse.gldebugger/META-INF/MANIFEST.MF
@@ -15,6 +15,7 @@
libs/liblzf.jar,
libs/sdklib.jar,
libs/ddmlib.jar,
+ libs/guava-10.0.1.jar,
.
Bundle-Vendor: The Android Open Source Project
Export-Package: com.android.ide.eclipse.gldebugger;x-friends:="com.android.ide.eclipse.gldebugger.tests",
diff --git a/eclipse/plugins/com.android.ide.eclipse.gldebugger/build.properties b/eclipse/plugins/com.android.ide.eclipse.gldebugger/build.properties
index 91e1d10..3b4a698 100644
--- a/eclipse/plugins/com.android.ide.eclipse.gldebugger/build.properties
+++ b/eclipse/plugins/com.android.ide.eclipse.gldebugger/build.properties
@@ -7,6 +7,5 @@
lib/host-libprotobuf-java-2.3.0-lite.jar,\
lib/liblzf.jar,\
lib/sdklib.jar,\
- libs/,\
- libs/ddmlib.jar,\
- entries.in
+ entries.in,\
+ libs/
diff --git a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/FileUtils.java b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/FileUtils.java
new file mode 100644
index 0000000..ab06f67
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/FileUtils.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ide.eclipse.gltrace;
+
+import com.google.common.io.Files;
+
+import java.io.File;
+import java.io.IOException;
+
+public class FileUtils {
+ private static final File sCacheDir;
+
+ static {
+ sCacheDir = Files.createTempDir();
+ sCacheDir.deleteOnExit();
+ }
+
+ public static File createTempFile(String prefix, String suffix) {
+ File f;
+ try {
+ f = File.createTempFile(prefix, suffix, sCacheDir);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ f.deleteOnExit();
+ return f;
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/TraceFileParserTask.java b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/TraceFileParserTask.java
index 381d8ce..b3ca918 100644
--- a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/TraceFileParserTask.java
+++ b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/TraceFileParserTask.java
@@ -130,17 +130,12 @@
// counters that maintain some statistics about the trace messages
long minTraceStartTime = Long.MAX_VALUE;
- int maxContextId = -1;
- while ((msg = sReader.getMessageAtOffset(mFile, 0)) != null) {
+ while ((msg = sReader.getMessageAtOffset(mFile, -1)) != null) {
if (minTraceStartTime > msg.getStartTime()) {
minTraceStartTime = msg.getStartTime();
}
- if (maxContextId < msg.getContextId()) {
- maxContextId = msg.getContextId();
- }
-
addMessage(msgCount, filePointer, msg, msg.getStartTime() - minTraceStartTime);
filePointer = mFile.getFilePointer();
@@ -151,7 +146,7 @@
}
}
- if (maxContextId > 0) {
+ if (mGLContextIds.size() > 1) {
// if there are multiple contexts, then the calls may arrive at the
// host out of order. So we perform a sort based on the invocation time.
Collections.sort(mGLCalls, new Comparator<GLCall>() {
diff --git a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/TraceFileReader.java b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/TraceFileReader.java
index e5121bf..ba7bf67 100644
--- a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/TraceFileReader.java
+++ b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/TraceFileReader.java
@@ -43,7 +43,7 @@
int len;
byte[] b;
try {
- if (offset != 0) {
+ if (offset != -1) {
file.seek(offset);
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/editors/DurationMinimap.java b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/editors/DurationMinimap.java
index 0fd7c42..88a8e31 100644
--- a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/editors/DurationMinimap.java
+++ b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/editors/DurationMinimap.java
@@ -196,6 +196,12 @@
private void initializeBackBuffer() {
Rectangle clientArea = getClientArea();
+ if (clientArea.width == 0 || clientArea.height == 0) {
+ mBackBufferImage = null;
+ mBackBufferGC = null;
+ return;
+ }
+
mBackBufferImage = new Image(getDisplay(),
clientArea.width,
clientArea.height);
@@ -232,6 +238,10 @@
initializeBackBuffer();
}
+ if (mBackBufferImage == null) {
+ return;
+ }
+
// draw contents onto the back buffer
drawBackground(mBackBufferGC, mBackBufferImage.getBounds());
drawContextHeaders(mBackBufferGC);
diff --git a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/editors/GLFunctionTraceViewer.java b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/editors/GLFunctionTraceViewer.java
index d69aeda..bbb668f 100644
--- a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/editors/GLFunctionTraceViewer.java
+++ b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/editors/GLFunctionTraceViewer.java
@@ -346,6 +346,7 @@
@Override
public void callSelected(int selectedCallIndex) {
table.select(selectedCallIndex);
+ table.setTopIndex(selectedCallIndex);
}
});
diff --git a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/editors/StateViewPage.java b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/editors/StateViewPage.java
index 4a9ad31..40e0a2a 100644
--- a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/editors/StateViewPage.java
+++ b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/editors/StateViewPage.java
@@ -16,12 +16,17 @@
package com.android.ide.eclipse.gltrace.editors;
+import com.android.ide.eclipse.gldebugger.Activator;
import com.android.ide.eclipse.gltrace.model.GLCall;
import com.android.ide.eclipse.gltrace.model.GLTrace;
import com.android.ide.eclipse.gltrace.state.GLState;
import com.android.ide.eclipse.gltrace.state.IGLProperty;
import com.android.ide.eclipse.gltrace.state.transforms.IStateTransform;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.TreeViewer;
@@ -132,20 +137,41 @@
return;
}
+ final int selectedCallIndex = selectedCall.getIndex();
if (selectedCall.getIndex() != mCurrentStateIndex) {
- final Set<IGLProperty> changedProperties = updateState(mCurrentStateIndex,
- selectedCall.getIndex());
- mCurrentStateIndex = selectedCall.getIndex();
-
- mLabelProvider.setChangedProperties(changedProperties);
- Display.getDefault().syncExec(new Runnable() {
+ // Creation of texture images takes a few seconds on the first run. So run
+ // the update task as an Eclipse job.
+ Job job = new Job("Updating GL State") {
@Override
- public void run() {
- if (!mTreeViewer.getTree().isDisposed()) {
- mTreeViewer.refresh();
+ protected IStatus run(IProgressMonitor monitor) {
+ Set<IGLProperty> changedProperties = null;
+
+ try {
+ changedProperties = updateState(mCurrentStateIndex,
+ selectedCallIndex);
+ } catch (Exception e) {
+ return new Status(Status.ERROR,
+ Activator.PLUGIN_ID,
+ "Unexpected error while updating GL State.",
+ e);
}
+ mCurrentStateIndex = selectedCallIndex;
+
+ mLabelProvider.setChangedProperties(changedProperties);
+ Display.getDefault().syncExec(new Runnable() {
+ @Override
+ public void run() {
+ if (!mTreeViewer.getTree().isDisposed()) {
+ mTreeViewer.refresh();
+ }
+ }
+ });
+
+ return Status.OK_STATUS;
}
- });
+ };
+ job.setPriority(Job.SHORT);
+ job.schedule();
}
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/state/GLState.java b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/state/GLState.java
index 25878e9..d8a3e34 100644
--- a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/state/GLState.java
+++ b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/state/GLState.java
@@ -230,8 +230,9 @@
GLEnum.GL_INVALID_VALUE);
IGLProperty imageType = new GLEnumProperty(GLStateType.TEXTURE_IMAGE_TYPE,
GLEnum.GL_UNSIGNED_BYTE);
+ IGLProperty image = new GLStringProperty(GLStateType.TEXTURE_IMAGE, null);
IGLProperty textureDefaultState = new GLCompositeProperty(GLStateType.PER_TEXTURE_STATE,
- minFilter, magFilter, wrapS, wrapT, format, width, height, imageType);
+ minFilter, magFilter, wrapS, wrapT, format, width, height, imageType, image);
GLSparseArrayProperty textures = new GLSparseArrayProperty(GLStateType.TEXTURES,
textureDefaultState);
textures.add(0);
diff --git a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/state/GLStateType.java b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/state/GLStateType.java
index da49a0c..e2c1edf 100644
--- a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/state/GLStateType.java
+++ b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/state/GLStateType.java
@@ -112,6 +112,7 @@
TEXTURE_WIDTH("Width"),
TEXTURE_HEIGHT("Height"),
TEXTURE_IMAGE_TYPE("Image Type"),
+ TEXTURE_IMAGE("Image"),
FRAMEBUFFER_STATE("Framebuffer State"),
FRAMEBUFFER_BINDING("Framebuffer Binding"),
diff --git a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/state/GLStringProperty.java b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/state/GLStringProperty.java
new file mode 100644
index 0000000..af435af
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/state/GLStringProperty.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ide.eclipse.gltrace.state;
+
+public class GLStringProperty extends GLAbstractAtomicProperty {
+ private final String mDefaultValue;
+ private String mCurrentValue;
+
+ public GLStringProperty(GLStateType type, String defaultValue) {
+ super(type);
+
+ mDefaultValue = defaultValue;
+ }
+
+ @Override
+ public boolean isDefault() {
+ return mDefaultValue.equalsIgnoreCase(mCurrentValue);
+ }
+
+ @Override
+ public void setValue(Object value) {
+ if (value instanceof String) {
+ mCurrentValue = (String) value;
+ } else {
+ throw new IllegalArgumentException("Attempt to set non-string value for " //$NON-NLS-1$
+ + getType());
+ }
+ }
+
+ public void setValue(String value) {
+ mCurrentValue = value;
+ }
+
+ @Override
+ public Object getValue() {
+ return mCurrentValue;
+ }
+
+ @Override
+ public String getStringValue() {
+ return mCurrentValue;
+ }
+
+ @Override
+ public String toString() {
+ return mCurrentValue;
+ };
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/state/transforms/StateTransformFactory.java b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/state/transforms/StateTransformFactory.java
index fb19ed8..0c282c4 100644
--- a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/state/transforms/StateTransformFactory.java
+++ b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/state/transforms/StateTransformFactory.java
@@ -17,17 +17,24 @@
package com.android.ide.eclipse.gltrace.state.transforms;
import com.android.ide.eclipse.gldebugger.GLEnum;
+import com.android.ide.eclipse.gltrace.FileUtils;
import com.android.ide.eclipse.gltrace.GLProtoBuf.GLMessage;
import com.android.ide.eclipse.gltrace.state.GLState;
import com.android.ide.eclipse.gltrace.state.GLStateType;
import com.android.ide.eclipse.gltrace.state.IGLProperty;
+import com.google.common.io.Files;
+import com.google.protobuf.ByteString;
+import java.io.File;
+import java.io.IOException;
import java.util.ArrayList;
-import java.util.Collection;
import java.util.Collections;
import java.util.List;
public class StateTransformFactory {
+ private static final String TEXTURE_DATA_FILE_PREFIX = "tex"; //$NON-NLS-1$
+ private static final String TEXTURE_DATA_FILE_SUFFIX = ".dat"; //$NON-NLS-1$
+
/** Construct a list of transformations to be applied for the provided OpenGL call. */
public static List<IStateTransform> getTransformsFor(GLMessage msg) {
switch (msg.getFunction()) {
@@ -694,7 +701,7 @@
* {@link #transformsForGlTexSubImage2D(GLMessage)}.
*/
private static List<IStateTransform> transformsForGlTexImage(GLMessage msg, int widthArgIndex,
- int heightArgIndex) {
+ int heightArgIndex, int xOffsetIndex, int yOffsetIndex) {
GLEnum target = GLEnum.valueOf(msg.getArgs(0).getIntValue(0));
Integer width = Integer.valueOf(msg.getArgs(widthArgIndex).getIntValue(0));
Integer height = Integer.valueOf(msg.getArgs(heightArgIndex).getIntValue(0));
@@ -722,19 +729,49 @@
getTextureUnitTargetName(target),
GLStateType.TEXTURE_IMAGE_TYPE),
type));
+
+ // if texture data is available, extract and store it in the cache folder
+ File f = null;
+ if (msg.getArgs(8).getIsArray()) {
+ ByteString data = msg.getArgs(8).getRawBytes(0);
+ f = FileUtils.createTempFile(TEXTURE_DATA_FILE_PREFIX, TEXTURE_DATA_FILE_SUFFIX);
+ try {
+ Files.write(data.toByteArray(), f);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ int xOffset = 0;
+ int yOffset = 0;
+
+ if (xOffsetIndex >= 0) {
+ xOffset = msg.getArgs(xOffsetIndex).getIntValue(0);
+ }
+
+ if (yOffsetIndex >= 0) {
+ yOffset = msg.getArgs(yOffsetIndex).getIntValue(0);
+ }
+
+ transforms.add(new TexImageTransform(
+ new TexturePropertyAccessor(msg.getContextId(),
+ getTextureUnitTargetName(target),
+ GLStateType.TEXTURE_IMAGE),
+ f, format, xOffset, yOffset, width, height));
+
return transforms;
}
private static List<IStateTransform> transformsForGlTexImage2D(GLMessage msg) {
// void glTexImage2D(GLenum target, GLint level, GLint internalformat, GLsizei width,
- // GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid * data);
- return transformsForGlTexImage(msg, 3, 4);
+ // GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *data);
+ return transformsForGlTexImage(msg, 3, 4, -1, -1);
}
private static List<IStateTransform> transformsForGlTexSubImage2D(GLMessage msg) {
// void glTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset,
- // GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid * data);
- return transformsForGlTexImage(msg, 4, 5);
+ // GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *data);
+ return transformsForGlTexImage(msg, 4, 5, 2, 3);
}
private static List<IStateTransform> transformsForGlTexParameter(GLMessage msg) {
diff --git a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/state/transforms/TexImageTransform.java b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/state/transforms/TexImageTransform.java
new file mode 100644
index 0000000..0d4ee30
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/state/transforms/TexImageTransform.java
@@ -0,0 +1,257 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ide.eclipse.gltrace.state.transforms;
+
+import com.android.ide.eclipse.gldebugger.GLEnum;
+import com.android.ide.eclipse.gltrace.FileUtils;
+import com.android.ide.eclipse.gltrace.state.GLStringProperty;
+import com.android.ide.eclipse.gltrace.state.IGLProperty;
+import com.google.common.io.Files;
+
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.IOException;
+
+import javax.imageio.ImageIO;
+
+/**
+ * {@link TexImageTransform} transforms the state to reflect the effect of a
+ * glTexImage2D or glTexSubImage2D GL call.
+ */
+public class TexImageTransform implements IStateTransform {
+ private static final String PNG_IMAGE_FORMAT = "PNG";
+ private static final String TEXTURE_FILE_PREFIX = "tex";
+ private static final String TEXTURE_FILE_SUFFIX = ".png";
+
+ private final IGLPropertyAccessor mAccessor;
+ private final File mTextureDataFile;
+
+ private final int mxOffset;
+ private final int myOffset;
+ private final int mWidth;
+ private final int mHeight;
+
+ private String mOldValue;
+ private String mNewValue;
+ private GLEnum mFormat;
+
+ /**
+ * Construct a texture image transformation.
+ * @param accessor accessor to obtain the GL state variable to modify
+ * @param textureData texture data passed in by the call. Could be null.
+ * @param format format of the source texture data
+ * @param xOffset x offset for the source data (used only in glTexSubImage2D)
+ * @param yOffset y offset for the source data (used only in glTexSubImage2D)
+ * @param width width of the texture
+ * @param height height of the texture
+ */
+ public TexImageTransform(IGLPropertyAccessor accessor, File textureData, GLEnum format,
+ int xOffset, int yOffset, int width, int height) {
+ mAccessor = accessor;
+ mTextureDataFile = textureData;
+ mFormat = format;
+
+ mxOffset = xOffset;
+ myOffset = yOffset;
+ mWidth = width;
+ mHeight = height;
+ }
+
+ @Override
+ public void apply(IGLProperty currentState) {
+ assert mOldValue == null : "Transform cannot be applied multiple times"; //$NON-NLS-1$
+
+ IGLProperty property = mAccessor.getProperty(currentState);
+ if (!(property instanceof GLStringProperty)) {
+ return;
+ }
+
+ GLStringProperty prop = (GLStringProperty) property;
+ mOldValue = prop.getStringValue();
+
+ // Applying texture transformations is a heavy weight process. So we perform
+ // it only once and save the result in a temporary file. The property is actually
+ // the path to the file.
+ if (mNewValue == null) {
+ try {
+ if (mOldValue == null) {
+ mNewValue = createTexture(mTextureDataFile, mWidth, mHeight);
+ } else {
+ mNewValue = updateTextureData(mOldValue, mTextureDataFile, mxOffset, myOffset,
+ mWidth, mHeight);
+ }
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ prop.setValue(mNewValue);
+ }
+
+ @Override
+ public void revert(IGLProperty state) {
+ if (mOldValue != null) {
+ IGLProperty property = mAccessor.getProperty(state);
+ property.setValue(mOldValue);
+ mOldValue = null;
+ }
+ }
+
+ @Override
+ public IGLProperty getChangedProperty(IGLProperty state) {
+ return mAccessor.getProperty(state);
+ }
+
+ /**
+ * Creates a texture of provided width and height. If the texture data file is provided,
+ * then the texture is initialized with the contents of that file, otherwise an empty
+ * image is created.
+ * @param textureDataFile path to texture data, could be null.
+ * @param width width of texture
+ * @param height height of texture
+ * @return path to cached texture
+ */
+ private String createTexture(File textureDataFile, int width, int height) throws IOException {
+ File f = FileUtils.createTempFile(TEXTURE_FILE_PREFIX, TEXTURE_FILE_SUFFIX);
+
+ BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_4BYTE_ABGR);
+
+ if (textureDataFile != null) {
+ byte[] initialData = Files.toByteArray(textureDataFile);
+ img.getRaster().setDataElements(0, 0, width, height,
+ formatSourceData(initialData, width, height));
+ }
+
+ ImageIO.write(img, PNG_IMAGE_FORMAT, f);
+
+ return f.getAbsolutePath();
+ }
+
+ /**
+ * Update part of an existing texture.
+ * @param currentImagePath current texture image.
+ * @param textureDataFile new data to update the current texture with
+ * @param xOffset x offset for the update region
+ * @param yOffset y offset for the update region
+ * @param width width of the update region
+ * @param height height of the update region
+ * @return path to the updated texture
+ */
+ private String updateTextureData(String currentImagePath, File textureDataFile,
+ int xOffset, int yOffset, int width, int height) throws IOException {
+ assert currentImagePath != null : "Attempt to update a null texture";
+
+ if (textureDataFile == null) {
+ // Do not perform any updates if we don't have the actual data.
+ return currentImagePath;
+ }
+
+ File f = FileUtils.createTempFile(TEXTURE_FILE_PREFIX, TEXTURE_FILE_SUFFIX);
+ BufferedImage image = null;
+ image = ImageIO.read(new File(currentImagePath));
+
+ byte[] subImageData = Files.toByteArray(textureDataFile);
+ image.getRaster().setDataElements(xOffset, yOffset, width, height,
+ formatSourceData(subImageData, width, height));
+
+ ImageIO.write(image, PNG_IMAGE_FORMAT, f);
+
+ return f.getAbsolutePath();
+ }
+
+ private byte[] formatSourceData(byte[] subImageData, int width, int height) {
+ switch (mFormat) {
+ case GL_RGBA:
+ // no conversions necessary
+ return subImageData;
+ case GL_RGB:
+ return addAlphaChannel(subImageData, width, height);
+ case GL_ALPHA:
+ return addRGBChannels(subImageData, width, height);
+ case GL_LUMINANCE:
+ return createRGBAFromLuminance(subImageData, width, height);
+ case GL_LUMINANCE_ALPHA:
+ return createRGBAFromLuminanceAlpha(subImageData, width, height);
+ default:
+ throw new RuntimeException();
+ }
+ }
+
+ private byte[] addAlphaChannel(byte[] sourceData, int width, int height) {
+ assert sourceData.length == 3 * width * height; // should have R, G & B channels
+
+ byte[] data = new byte[4 * width * height];
+
+ for (int src = 0, dst = 0; src < sourceData.length; src += 3, dst += 4) {
+ data[dst + 0] = sourceData[src + 0]; // copy R byte
+ data[dst + 1] = sourceData[src + 1]; // copy G byte
+ data[dst + 2] = sourceData[src + 2]; // copy B byte
+ data[dst + 3] = 1; // add alpha = 1
+ }
+
+ return data;
+ }
+
+ private byte[] addRGBChannels(byte[] sourceData, int width, int height) {
+ assert sourceData.length == width * height; // should have a single alpha channel
+
+ byte[] data = new byte[4 * width * height];
+
+ for (int src = 0, dst = 0; src < sourceData.length; src++, dst += 4) {
+ data[dst + 0] = data[dst + 1] = data[dst + 2] = 0; // set R = G = B = 0
+ data[dst + 3] = sourceData[src]; // copy over alpha
+ }
+
+ return data;
+ }
+
+ private byte[] createRGBAFromLuminance(byte[] sourceData, int width, int height) {
+ assert sourceData.length == width * height; // should have a single luminance channel
+
+ byte[] data = new byte[4 * width * height];
+
+ for (int src = 0, dst = 0; src < sourceData.length; src++, dst += 4) {
+ int l = sourceData[src] * 3;
+ if (l > 255) { // clamp to 255
+ l = 255;
+ }
+
+ data[dst + 0] = data[dst + 1] = data[dst + 2] = (byte) l; // set R = G = B = L * 3
+ data[dst + 3] = 1; // set alpha = 1
+ }
+
+ return data;
+ }
+
+ private byte[] createRGBAFromLuminanceAlpha(byte[] sourceData, int width, int height) {
+ assert sourceData.length == 2 * width * height; // should have luminance & alpha channels
+
+ byte[] data = new byte[4 * width * height];
+
+ for (int src = 0, dst = 0; src < sourceData.length; src += 2, dst += 4) {
+ int l = sourceData[src] * 3;
+ if (l > 255) { // clamp to 255
+ l = 255;
+ }
+
+ data[dst + 0] = data[dst + 1] = data[dst + 2] = (byte) l; // set R = G = B = L * 3
+ data[dst + 3] = sourceData[src + 1]; // copy over alpha
+ }
+
+ return data;
+ }
+}
diff --git a/eclipse/scripts/create_all_symlinks.sh b/eclipse/scripts/create_all_symlinks.sh
index b23a3b5..1e68571 100755
--- a/eclipse/scripts/create_all_symlinks.sh
+++ b/eclipse/scripts/create_all_symlinks.sh
@@ -162,9 +162,10 @@
GLD_DEST="sdk/eclipse/plugins/com.android.ide.eclipse.gldebugger/libs"
GLD_LIBS="host-libprotobuf-java-2.3.0-lite liblzf sdklib ddmlib"
+ GLD_PREBUILTS="prebuilts/tools/common/guava-tools/guava-10.0.1.jar"
LIBS="$LIBS $GLD_LIBS"
- CP_FILES="$CP_FILES @:$GLD_DEST $GLD_LIBS"
+ CP_FILES="$CP_FILES @:$GLD_DEST $GLD_LIBS $GLD_PREBUILTS"
fi
# Make sure we have lunch sdk-<something>