Merge "LayoutLib: Support PorterDuffColorFilter" into lmp-preview-dev

This commit is contained in:
Deepanshu Gupta
2014-06-24 05:37:10 +00:00
committed by Android (Google) Code Review
9 changed files with 320 additions and 152 deletions

View File

@@ -627,8 +627,7 @@ public final class Canvas_Delegate {
// set the color
graphics.setColor(new Color(color, true /*alpha*/));
Composite composite = PorterDuffXfermode_Delegate.getComposite(
PorterDuffXfermode_Delegate.getPorterDuffMode(mode), 0xFF);
Composite composite = PorterDuffXfermode_Delegate.getComposite(mode, 0xFF);
if (composite != null) {
graphics.setComposite(composite);
}

View File

@@ -19,6 +19,8 @@ package android.graphics;
import com.android.layoutlib.bridge.impl.DelegateManager;
import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
import java.awt.Graphics2D;
/**
* Delegate implementing the native methods of android.graphics.ColorFilter
*
@@ -50,9 +52,17 @@ public abstract class ColorFilter_Delegate {
return sManager.getDelegate(nativeShader);
}
public abstract boolean isSupported();
public abstract String getSupportMessage();
public boolean isSupported() {
return false;
}
public void applyFilter(Graphics2D g, int width, int height) {
// This should never be called directly. If supported, the sub class should override this.
assert false;
}
// ---- native methods ----
@LayoutlibDelegate

View File

@@ -42,11 +42,6 @@ public class ColorMatrixColorFilter_Delegate extends ColorFilter_Delegate {
// ---- Public Helper methods ----
@Override
public boolean isSupported() {
return false;
}
@Override
public String getSupportMessage() {
return "ColorMatrix Color Filters are not supported.";

View File

@@ -42,11 +42,6 @@ public class LightingColorFilter_Delegate extends ColorFilter_Delegate {
// ---- Public Helper methods ----
@Override
public boolean isSupported() {
return false;
}
@Override
public String getSupportMessage() {
return "Lighting Color Filters are not supported.";

View File

@@ -827,8 +827,8 @@ public class Paint_Delegate {
delegate.mColorFilter = ColorFilter_Delegate.getDelegate(filter);
// since none of those are supported, display a fidelity warning right away
if (delegate.mColorFilter != null && delegate.mColorFilter.isSupported() == false) {
// Log warning if it's not supported.
if (delegate.mColorFilter != null && !delegate.mColorFilter.isSupported()) {
Bridge.getLog().fidelityWarning(LayoutLog.TAG_COLORFILTER,
delegate.mColorFilter.getSupportMessage(), null, null /*data*/);
}
@@ -873,7 +873,7 @@ public class Paint_Delegate {
delegate.mMaskFilter = MaskFilter_Delegate.getDelegate(maskfilter);
// since none of those are supported, display a fidelity warning right away
if (delegate.mMaskFilter != null && delegate.mMaskFilter.isSupported() == false) {
if (delegate.mMaskFilter != null && !delegate.mMaskFilter.isSupported()) {
Bridge.getLog().fidelityWarning(LayoutLog.TAG_MASKFILTER,
delegate.mMaskFilter.getSupportMessage(), null, null /*data*/);
}
@@ -906,7 +906,7 @@ public class Paint_Delegate {
delegate.mRasterizer = Rasterizer_Delegate.getDelegate(rasterizer);
// since none of those are supported, display a fidelity warning right away
if (delegate.mRasterizer != null && delegate.mRasterizer.isSupported() == false) {
if (delegate.mRasterizer != null && !delegate.mRasterizer.isSupported()) {
Bridge.getLog().fidelityWarning(LayoutLog.TAG_RASTERIZER,
delegate.mRasterizer.getSupportMessage(), null, null /*data*/);
}

View File

@@ -19,6 +19,15 @@ package android.graphics;
import com.android.layoutlib.bridge.impl.DelegateManager;
import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
import android.graphics.PorterDuff.Mode;
import java.awt.AlphaComposite;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import static com.android.layoutlib.bridge.impl.PorterDuffUtility.getAlphaCompositeRule;
import static com.android.layoutlib.bridge.impl.PorterDuffUtility.getPorterDuffMode;
/**
* Delegate implementing the native methods of android.graphics.PorterDuffColorFilter
*
@@ -40,25 +49,80 @@ public class PorterDuffColorFilter_Delegate extends ColorFilter_Delegate {
// ---- delegate data ----
private final int mSrcColor;
private final Mode mMode;
private int mWidth;
private int mHeight;
private BufferedImage mImage;
// ---- Public Helper methods ----
@Override
public boolean isSupported() {
switch (mMode) {
case CLEAR:
case SRC:
case SRC_IN:
case DST_IN:
case SRC_ATOP:
return true;
}
return false;
}
@Override
public String getSupportMessage() {
return "PorterDuff Color Filters are not supported.";
return "PorterDuff Color Filter is not supported for mode: " + mMode.name() + ".";
}
@Override
public void applyFilter(Graphics2D g, int width, int height) {
createFilterImage(width, height);
g.setComposite(getComposite());
g.drawImage(mImage, 0, 0, null);
}
// ---- native methods ----
@LayoutlibDelegate
/*package*/ static long native_CreatePorterDuffFilter(int srcColor, int porterDuffMode) {
PorterDuffColorFilter_Delegate newDelegate = new PorterDuffColorFilter_Delegate();
PorterDuffColorFilter_Delegate newDelegate =
new PorterDuffColorFilter_Delegate(srcColor, porterDuffMode);
return sManager.addNewDelegate(newDelegate);
}
// ---- Private delegate/helper methods ----
private PorterDuffColorFilter_Delegate(int srcColor, int mode) {
mSrcColor = srcColor;
mMode = getPorterDuffMode(mode);
}
private void createFilterImage(int width, int height) {
if (mWidth == width && mHeight == height && mImage != null) {
return;
}
mWidth = width;
mHeight = height;
mImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
Graphics2D graphics = mImage.createGraphics();
try {
graphics.setColor(new java.awt.Color(mSrcColor, true /* hasAlpha */));
graphics.fillRect(0, 0, width, height);
} finally {
graphics.dispose();
}
}
private AlphaComposite getComposite() {
return AlphaComposite.getInstance(getAlphaCompositeRule(mMode),
getAlpha() / 255f);
}
private int getAlpha() {
return mSrcColor >>> 24;
}
}

View File

@@ -21,9 +21,14 @@ import com.android.layoutlib.bridge.Bridge;
import com.android.layoutlib.bridge.impl.DelegateManager;
import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
import android.graphics.PorterDuff.Mode;
import java.awt.AlphaComposite;
import java.awt.Composite;
import static com.android.layoutlib.bridge.impl.PorterDuffUtility.getAlphaCompositeRule;
import static com.android.layoutlib.bridge.impl.PorterDuffUtility.getPorterDuffMode;
/**
* Delegate implementing the native methods of android.graphics.PorterDuffXfermode
*
@@ -43,17 +48,17 @@ public class PorterDuffXfermode_Delegate extends Xfermode_Delegate {
// ---- delegate data ----
private final int mMode;
private final Mode mMode;
// ---- Public Helper methods ----
public PorterDuff.Mode getMode() {
return getPorterDuffMode(mMode);
public Mode getMode() {
return mMode;
}
@Override
public Composite getComposite(int alpha) {
return getComposite(getPorterDuffMode(mMode), alpha);
return getComposite(mMode, alpha);
}
@Override
@@ -67,61 +72,8 @@ public class PorterDuffXfermode_Delegate extends Xfermode_Delegate {
return null;
}
public static PorterDuff.Mode getPorterDuffMode(int mode) {
for (PorterDuff.Mode m : PorterDuff.Mode.values()) {
if (m.nativeInt == mode) {
return m;
}
}
Bridge.getLog().error(LayoutLog.TAG_BROKEN,
String.format("Unknown PorterDuff.Mode: %d", mode), null /*data*/);
assert false;
return PorterDuff.Mode.SRC_OVER;
}
public static Composite getComposite(PorterDuff.Mode mode, int alpha) {
float falpha = alpha != 0xFF ? (float)alpha / 255.f : 1.f;
switch (mode) {
case CLEAR:
return AlphaComposite.getInstance(AlphaComposite.CLEAR, falpha);
case DARKEN:
break;
case DST:
return AlphaComposite.getInstance(AlphaComposite.DST, falpha);
case DST_ATOP:
return AlphaComposite.getInstance(AlphaComposite.DST_ATOP, falpha);
case DST_IN:
return AlphaComposite.getInstance(AlphaComposite.DST_IN, falpha);
case DST_OUT:
return AlphaComposite.getInstance(AlphaComposite.DST_OUT, falpha);
case DST_OVER:
return AlphaComposite.getInstance(AlphaComposite.DST_OVER, falpha);
case LIGHTEN:
break;
case MULTIPLY:
break;
case SCREEN:
break;
case SRC:
return AlphaComposite.getInstance(AlphaComposite.SRC, falpha);
case SRC_ATOP:
return AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, falpha);
case SRC_IN:
return AlphaComposite.getInstance(AlphaComposite.SRC_IN, falpha);
case SRC_OUT:
return AlphaComposite.getInstance(AlphaComposite.SRC_OUT, falpha);
case SRC_OVER:
return AlphaComposite.getInstance(AlphaComposite.SRC_OVER, falpha);
case XOR:
return AlphaComposite.getInstance(AlphaComposite.XOR, falpha);
}
Bridge.getLog().fidelityWarning(LayoutLog.TAG_BROKEN,
String.format("Unsupported PorterDuff Mode: %s", mode.name()),
null, null /*data*/);
return AlphaComposite.getInstance(AlphaComposite.SRC_OVER, falpha);
public static Composite getComposite(int mode, int alpha) {
return getComposite(getPorterDuffMode(mode), alpha);
}
// ---- native methods ----
@@ -135,6 +87,20 @@ public class PorterDuffXfermode_Delegate extends Xfermode_Delegate {
// ---- Private delegate/helper methods ----
private PorterDuffXfermode_Delegate(int mode) {
mMode = mode;
mMode = getPorterDuffMode(mode);
}
private static Composite getComposite(Mode mode, int alpha255) {
float alpha1 = alpha255 != 0xFF ? alpha255 / 255.f : 1.f;
int rule = getAlphaCompositeRule(mode);
if (rule >= 0) {
return AlphaComposite.getInstance(rule, alpha1);
}
Bridge.getLog().fidelityWarning(LayoutLog.TAG_BROKEN,
String.format("Unsupported PorterDuff Mode: %1$s", mode.name()),
null, null /*data*/);
return AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha1);
}
}

View File

@@ -21,6 +21,7 @@ import com.android.layoutlib.bridge.Bridge;
import android.graphics.Bitmap_Delegate;
import android.graphics.Canvas;
import android.graphics.ColorFilter_Delegate;
import android.graphics.Paint;
import android.graphics.Paint_Delegate;
import android.graphics.Rect;
@@ -48,8 +49,8 @@ import java.util.ArrayList;
* This is based on top of {@link Graphics2D} but can operate independently if none are available
* yet when setting transforms and clip information.
* <p>
* This allows for drawing through {@link #draw(Drawable, Paint_Delegate)} and
* {@link #draw(Drawable, Paint_Delegate)}
* This allows for drawing through {@link #draw(Drawable, Paint_Delegate, boolean, boolean)} and
* {@link #draw(Drawable)}
*
* Handling of layers (created with {@link Canvas#saveLayer(RectF, Paint, int)}) is handled through
* a list of Graphics2D for each layers. The class actually maintains a list of {@link Layer}
@@ -203,7 +204,7 @@ public class GcSnapshot {
* called before the snapshot can be used to draw. Transform and clip operations are permitted
* before.
*
* @param image the image to associate to the snapshot or null.
* @param bitmap the image to associate to the snapshot or null.
* @return the root snapshot
*/
public static GcSnapshot createDefaultSnapshot(Bitmap_Delegate bitmap) {
@@ -557,7 +558,6 @@ public class GcSnapshot {
* Executes the Drawable's draw method, with a null paint delegate.
* <p/>
* Note that the method can be called several times if there are more than one active layer.
* @param drawable
*/
public void draw(Drawable drawable) {
draw(drawable, null, false /*compositeOnly*/, false /*forceSrcMode*/);
@@ -567,20 +567,19 @@ public class GcSnapshot {
* Executes the Drawable's draw method.
* <p/>
* Note that the method can be called several times if there are more than one active layer.
* @param drawable
* @param paint
* @param compositeOnly whether the paint is used for composite only. This is typically
* the case for bitmaps.
* @param forceSrcMode if true, this overrides the composite to be SRC
*/
public void draw(Drawable drawable, Paint_Delegate paint, boolean compositeOnly,
boolean forceSrcMode) {
int forceMode = forceSrcMode ? AlphaComposite.SRC : 0;
// the current snapshot may not have a mLocalLayer (ie it was created on save() instead
// of saveLayer(), but that doesn't mean there's no layer.
// mLayers however saves all the information we need (flags).
if (mLayers.size() == 1) {
// no layer, only base layer. easy case.
drawInLayer(mLayers.get(0), drawable, paint, compositeOnly, forceSrcMode);
drawInLayer(mLayers.get(0), drawable, paint, compositeOnly, forceMode);
} else {
// draw in all the layers until the layer save flags tells us to stop (ie drawing
// in that layer is limited to the layer itself.
@@ -590,7 +589,7 @@ public class GcSnapshot {
do {
Layer layer = mLayers.get(i);
drawInLayer(layer, drawable, paint, compositeOnly, forceSrcMode);
drawInLayer(layer, drawable, paint, compositeOnly, forceMode);
// then go to previous layer, only if there are any left, and its flags
// doesn't restrict drawing to the layer itself.
@@ -601,20 +600,61 @@ public class GcSnapshot {
}
private void drawInLayer(Layer layer, Drawable drawable, Paint_Delegate paint,
boolean compositeOnly, boolean forceSrcMode) {
boolean compositeOnly, int forceMode) {
Graphics2D originalGraphics = layer.getGraphics();
// get a Graphics2D object configured with the drawing parameters.
Graphics2D configuredGraphics2D =
paint != null ?
createCustomGraphics(originalGraphics, paint, compositeOnly, forceSrcMode) :
(Graphics2D) originalGraphics.create();
if (paint == null) {
drawOnGraphics((Graphics2D) originalGraphics.create(), drawable,
null /*paint*/, layer);
} else {
ColorFilter_Delegate filter = paint.getColorFilter();
if (filter == null || !filter.isSupported()) {
// get a Graphics2D object configured with the drawing parameters.
Graphics2D configuredGraphics = createCustomGraphics(originalGraphics, paint,
compositeOnly, forceMode);
drawOnGraphics(configuredGraphics, drawable, paint, layer);
return;
}
int width = layer.getImage().getWidth();
int height = layer.getImage().getHeight();
// Create a temporary image to which the color filter will be applied.
BufferedImage image = new BufferedImage(width, height,
BufferedImage.TYPE_INT_ARGB);
Graphics2D imageBaseGraphics = (Graphics2D) image.getGraphics();
// Configure the Graphics2D object with drawing parameters and shader.
Graphics2D imageGraphics = createCustomGraphics(
imageBaseGraphics, paint, compositeOnly,
AlphaComposite.SRC_OVER);
// get a Graphics2D object configured with the drawing parameters, but no shader.
Graphics2D configuredGraphics = createCustomGraphics(originalGraphics, paint,
true /*compositeOnly*/, forceMode);
try {
// The main draw operation.
drawable.draw(imageGraphics, paint);
// Apply the color filter.
filter.applyFilter(imageGraphics, width, height);
// Draw the tinted image on the main layer.
configuredGraphics.drawImage(image, 0, 0, null);
layer.change();
} finally {
// dispose Graphics2D objects
imageGraphics.dispose();
imageBaseGraphics.dispose();
configuredGraphics.dispose();
}
}
}
private void drawOnGraphics(Graphics2D g, Drawable drawable, Paint_Delegate paint,
Layer layer) {
try {
drawable.draw(configuredGraphics2D, paint);
drawable.draw(g, paint);
layer.change();
} finally {
// dispose Graphics2D object
configuredGraphics2D.dispose();
g.dispose();
}
}
@@ -685,7 +725,7 @@ public class GcSnapshot {
// now draw put the content of the local layer onto the layer,
// using the paint information
Graphics2D g = createCustomGraphics(baseGfx, mLocalLayerPaint,
true /*alphaOnly*/, false /*forceSrcMode*/);
true /*alphaOnly*/, 0 /*forceMode*/);
g.drawImage(mLocalLayer.getImage(),
mLayerBounds.left, mLayerBounds.top, mLayerBounds.right, mLayerBounds.bottom,
@@ -701,7 +741,7 @@ public class GcSnapshot {
* <p/>The object must be disposed ({@link Graphics2D#dispose()}) after being used.
*/
private Graphics2D createCustomGraphics(Graphics2D original, Paint_Delegate paint,
boolean compositeOnly, boolean forceSrcMode) {
boolean compositeOnly, int forceMode) {
// make new one graphics
Graphics2D g = (Graphics2D) original.create();
@@ -714,70 +754,73 @@ public class GcSnapshot {
RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
}
// set the shader first, as it'll replace the color if it can be used it.
boolean customShader = false;
// get the shader first, as it'll replace the color if it can be used it.
if (compositeOnly == false) {
Shader_Delegate shaderDelegate = paint.getShader();
if (shaderDelegate != null) {
if (shaderDelegate.isSupported()) {
java.awt.Paint shaderPaint = shaderDelegate.getJavaPaint();
assert shaderPaint != null;
if (shaderPaint != null) {
g.setPaint(shaderPaint);
customShader = true;
}
} else {
Bridge.getLog().fidelityWarning(LayoutLog.TAG_SHADER,
shaderDelegate.getSupportMessage(),
null /*throwable*/, null /*data*/);
}
}
// if no shader, use the paint color
if (customShader == false) {
g.setColor(new Color(paint.getColor(), true /*hasAlpha*/));
}
if (!compositeOnly) {
customShader = setShader(g, paint);
// set the stroke
g.setStroke(paint.getJavaStroke());
}
// set the composite.
setComposite(g, paint, compositeOnly || customShader, forceMode);
// the alpha for the composite. Always opaque if the normal paint color is used since
// it contains the alpha
int alpha = (compositeOnly || customShader) ? paint.getAlpha() : 0xFF;
return g;
}
if (forceSrcMode) {
g.setComposite(AlphaComposite.getInstance(
AlphaComposite.SRC, (float) alpha / 255.f));
} else {
boolean customXfermode = false;
Xfermode_Delegate xfermodeDelegate = paint.getXfermode();
if (xfermodeDelegate != null) {
if (xfermodeDelegate.isSupported()) {
Composite composite = xfermodeDelegate.getComposite(alpha);
assert composite != null;
if (composite != null) {
g.setComposite(composite);
customXfermode = true;
}
} else {
Bridge.getLog().fidelityWarning(LayoutLog.TAG_XFERMODE,
xfermodeDelegate.getSupportMessage(),
null /*throwable*/, null /*data*/);
private boolean setShader(Graphics2D g, Paint_Delegate paint) {
Shader_Delegate shaderDelegate = paint.getShader();
if (shaderDelegate != null) {
if (shaderDelegate.isSupported()) {
java.awt.Paint shaderPaint = shaderDelegate.getJavaPaint();
assert shaderPaint != null;
if (shaderPaint != null) {
g.setPaint(shaderPaint);
return true;
}
}
// if there was no custom xfermode, but we have alpha (due to a shader and a non
// opaque alpha channel in the paint color), then we create an AlphaComposite anyway
// that will handle the alpha.
if (customXfermode == false && alpha != 0xFF) {
g.setComposite(AlphaComposite.getInstance(
AlphaComposite.SRC_OVER, (float) alpha / 255.f));
} else {
Bridge.getLog().fidelityWarning(LayoutLog.TAG_SHADER,
shaderDelegate.getSupportMessage(),
null /*throwable*/, null /*data*/);
}
}
return g;
// if no shader, use the paint color
g.setColor(new Color(paint.getColor(), true /*hasAlpha*/));
return false;
}
private void setComposite(Graphics2D g, Paint_Delegate paint, boolean usePaintAlpha,
int forceMode) {
// the alpha for the composite. Always opaque if the normal paint color is used since
// it contains the alpha
int alpha = usePaintAlpha ? paint.getAlpha() : 0xFF;
if (forceMode != 0) {
g.setComposite(AlphaComposite.getInstance(forceMode, (float) alpha / 255.f));
return;
}
Xfermode_Delegate xfermodeDelegate = paint.getXfermode();
if (xfermodeDelegate != null) {
if (xfermodeDelegate.isSupported()) {
Composite composite = xfermodeDelegate.getComposite(alpha);
assert composite != null;
if (composite != null) {
g.setComposite(composite);
return;
}
} else {
Bridge.getLog().fidelityWarning(LayoutLog.TAG_XFERMODE,
xfermodeDelegate.getSupportMessage(),
null /*throwable*/, null /*data*/);
}
}
// if there was no custom xfermode, but we have alpha (due to a shader and a non
// opaque alpha channel in the paint color), then we create an AlphaComposite anyway
// that will handle the alpha.
if (alpha != 0xFF) {
g.setComposite(AlphaComposite.getInstance(
AlphaComposite.SRC_OVER, (float) alpha / 255.f));
}
}
private void mapRect(AffineTransform matrix, RectF dst, RectF src) {

View File

@@ -0,0 +1,96 @@
/*
* Copyright (C) 2014 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.layoutlib.bridge.impl;
import com.android.ide.common.rendering.api.LayoutLog;
import com.android.layoutlib.bridge.Bridge;
import android.graphics.PorterDuff.Mode;
import android.graphics.PorterDuffColorFilter_Delegate;
import android.graphics.PorterDuffXfermode_Delegate;
import java.awt.AlphaComposite;
/**
* Provides various utility methods for {@link PorterDuffColorFilter_Delegate} and {@link
* PorterDuffXfermode_Delegate}.
*/
public final class PorterDuffUtility {
// Make the class non-instantiable.
private PorterDuffUtility() {
}
/**
* Convert the porterDuffMode from the framework to its corresponding enum. This defaults to
* {@link Mode#SRC_OVER} for invalid modes.
*/
public static Mode getPorterDuffMode(int porterDuffMode) {
Mode[] values = Mode.values();
if (porterDuffMode >= 0 && porterDuffMode < values.length) {
return values[porterDuffMode];
}
Bridge.getLog().error(LayoutLog.TAG_BROKEN,
String.format("Unknown PorterDuff.Mode: %1$d", porterDuffMode), null /*data*/);
assert false;
return Mode.SRC_OVER;
}
/**
* A utility method to convert the porterDuffMode to an int to be used as a rule for {@link
* AlphaComposite}. If {@code AlphaComposite} doesn't support the mode, -1 is returned.
*/
public static int getAlphaCompositeRule(Mode porterDuffMode) {
switch (porterDuffMode) {
case CLEAR:
return AlphaComposite.CLEAR;
case DARKEN:
break;
case DST:
return AlphaComposite.DST;
case DST_ATOP:
return AlphaComposite.DST_ATOP;
case DST_IN:
return AlphaComposite.DST_IN;
case DST_OUT:
return AlphaComposite.DST_OUT;
case DST_OVER:
return AlphaComposite.DST_OVER;
case LIGHTEN:
break;
case MULTIPLY:
break;
case SCREEN:
break;
case SRC:
return AlphaComposite.SRC;
case SRC_ATOP:
return AlphaComposite.SRC_ATOP;
case SRC_IN:
return AlphaComposite.SRC_IN;
case SRC_OUT:
return AlphaComposite.SRC_OUT;
case SRC_OVER:
return AlphaComposite.SRC_OVER;
case XOR:
return AlphaComposite.XOR;
}
// This is an unsupported mode.
return -1;
}
}