The code "Region.Op.REPLACE", at two places in AndroidDisplayGraphics.java throws illegalArgumentException in android 9 and later versions due to policy change.
Here is the complete java code of AndroidDisplayGraphics.java:
/**
* MicroEmulator
* Copyright (C) 2008 Bartek Teodorczyk <[email protected]>
*
* It is licensed under the following two licenses as alternatives:
* 1. GNU Lesser General Public License (the "LGPL") version 2.1 or any newer version
* 2. Apache License (the "AL") Version 2.0
*
* You may not use this file except in compliance with at least one of
* the above two licenses.
*
* You may obtain a copy of the LGPL at
* http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt
*
* You may obtain a copy of the AL 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 LGPL or the AL for the specific language governing permissions and
* limitations.
*
* @version $Id: AndroidDisplayGraphics.java 2522 2011-11-28 13:44:59Z [email protected] $
*/
package org.microemu.android.device;
import javax.microedition.lcdui.Font;
import javax.microedition.lcdui.Image;
import javax.microedition.lcdui.game.Sprite;
import org.microemu.log.Logger;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.DashPathEffect;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Region;
public class AndroidDisplayGraphics extends javax.microedition.lcdui.Graphics {
public Paint strokePaint = new Paint();
public Paint fillPaint = new Paint();
public AndroidFont androidFont;
private static final DashPathEffect dashPathEffect = new DashPathEffect(new float[] { 5, 5 }, 0);
private static final Matrix identityMatrix = new Matrix();
private Canvas canvas;
private GraphicsDelegate delegate;
private Rect clip;
private Font font;
private int strokeStyle = SOLID;
private Rect tmpRect = new Rect();
private Rect tmpRectSecond = new Rect();
private Matrix tmpMatrix = new Matrix();
public AndroidDisplayGraphics() {
this.delegate = null;
strokePaint.setAntiAlias(true);
strokePaint.setStyle(Paint.Style.STROKE);
fillPaint.setAntiAlias(true);
fillPaint.setStyle(Paint.Style.FILL);
}
public AndroidDisplayGraphics(Bitmap bitmap) {
this.canvas = new Canvas(bitmap);
this.canvas.clipRect(0, 0, bitmap.getWidth(), bitmap.getHeight());
this.delegate = null;
strokePaint.setAntiAlias(true);
strokePaint.setStyle(Paint.Style.STROKE);
fillPaint.setAntiAlias(true);
fillPaint.setStyle(Paint.Style.FILL);
reset(this.canvas);
}
public void reset(Canvas canvas) {
this.canvas = canvas;
Rect tmp = this.canvas.getClipBounds();
this.canvas.setMatrix(identityMatrix);
// setMatrix changes the clipping too
this.canvas.clipRect(tmp, Region.Op.REPLACE);
clip = this.canvas.getClipBounds();
setFont(Font.getDefaultFont());
}
public Canvas getCanvas() {
return canvas;
}
public void setDelegate(GraphicsDelegate delegate) {
this.delegate = delegate;
}
public void clipRect(int x, int y, int width, int height) {
canvas.clipRect(x, y, x + width, y + height);
clip = canvas.getClipBounds();
if (delegate != null) {
delegate.setClip(clip.left, clip.top, clip.right - clip.left, clip.bottom - clip.top);
}
}
public void drawArc(int x, int y, int width, int height, int startAngle, int arcAngle) {
RectF rect = new RectF(x, y, x + width, y + height);
canvas.drawArc(rect, -startAngle, -arcAngle, false, strokePaint);
}
public void drawImage(Image img, int x, int y, int anchor) {
if (delegate != null) {
delegate.drawImage(img, x, y, anchor);
} else {
int newx = x;
int newy = y;
if (anchor == 0) {
anchor = javax.microedition.lcdui.Graphics.TOP | javax.microedition.lcdui.Graphics.LEFT;
}
if ((anchor & javax.microedition.lcdui.Graphics.RIGHT) != 0) {
newx -= img.getWidth();
} else if ((anchor & javax.microedition.lcdui.Graphics.HCENTER) != 0) {
newx -= img.getWidth() / 2;
}
if ((anchor & javax.microedition.lcdui.Graphics.BOTTOM) != 0) {
newy -= img.getHeight();
} else if ((anchor & javax.microedition.lcdui.Graphics.VCENTER) != 0) {
newy -= img.getHeight() / 2;
}
if (img.isMutable()) {
canvas.drawBitmap(((AndroidMutableImage) img).getBitmap(), newx, newy, strokePaint);
} else {
canvas.drawBitmap(((AndroidImmutableImage) img).getBitmap(), newx, newy, strokePaint);
}
}
}
public void drawLine(int x1, int y1, int x2, int y2) {
if (delegate != null) {
delegate.drawLine(x1, y1, x2, y2);
} else {
if (x1 == x2) {
canvas.drawLine(x1, y1, x2, y2 + 1, strokePaint);
} else if (y1 == y2) {
canvas.drawLine(x1, y1, x2 + 1, y2, strokePaint);
} else {
canvas.drawLine(x1, y1, x2 + 1, y2 + 1, strokePaint);
}
}
}
public void drawRect(int x, int y, int width, int height) {
if (delegate != null) {
delegate.drawRect(x, y, width, height);
} else {
canvas.drawRect(x, y, x + width, y + height, strokePaint);
}
}
public void drawRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) {
canvas.drawRoundRect(new RectF(x, y, x + width, y + height), (float) arcWidth, (float) arcHeight, strokePaint);
}
public void drawString(String str, int x, int y, int anchor) {
drawSubstring(str, 0, str.length(), x, y, anchor);
}
public void drawSubstring(String str, int offset, int len, int x, int y, int anchor) {
int newx = x;
int newy = y;
if (anchor == 0) {
anchor = javax.microedition.lcdui.Graphics.TOP | javax.microedition.lcdui.Graphics.LEFT;
}
if ((anchor & javax.microedition.lcdui.Graphics.TOP) != 0) {
newy -= androidFont.metrics.ascent;
} else if ((anchor & javax.microedition.lcdui.Graphics.BOTTOM) != 0) {
newy -= androidFont.metrics.descent;
}
if ((anchor & javax.microedition.lcdui.Graphics.HCENTER) != 0) {
newx -= androidFont.paint.measureText(str) / 2;
} else if ((anchor & javax.microedition.lcdui.Graphics.RIGHT) != 0) {
newx -= androidFont.paint.measureText(str);
}
androidFont.paint.setColor(strokePaint.getColor());
if (delegate != null) {
delegate.drawSubstringDelegate(str, offset, len, newx, newy, anchor);
} else {
canvas.drawText(str, offset, len + offset, newx, newy, androidFont.paint);
}
}
public void fillArc(int x, int y, int width, int height, int startAngle, int arcAngle) {
RectF rect = new RectF(x, y, x + width, y + height);
canvas.drawArc(rect, -startAngle, -arcAngle, true, fillPaint);
}
public void fillRect(int x, int y, int width, int height) {
if (delegate != null) {
delegate.fillRect(x, y, width, height);
} else {
canvas.drawRect(x, y, x + width, y + height, fillPaint);
}
}
public void fillRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) {
canvas.drawRoundRect(new RectF(x, y, x + width, y + height), (float) arcWidth, (float) arcHeight, fillPaint);
}
public int getClipHeight() {
return clip.bottom - clip.top;
}
public int getClipWidth() {
return clip.right - clip.left;
}
public int getClipX() {
return clip.left;
}
public int getClipY() {
return clip.top;
}
public int getColor() {
return strokePaint.getColor();
}
public Font getFont() {
return font;
}
public int getStrokeStyle() {
return strokeStyle;
}
public void setClip(int x, int y, int width, int height) {
if (x == clip.left && x + width == clip.right && y == clip.top && y + height == clip.bottom) {
return;
}
if (delegate != null) {
delegate.setClip(x, y, width, height);
}
clip.left = x;
clip.top = y;
clip.right = x + width;
clip.bottom = y + height;
canvas.clipRect(clip, Region.Op.REPLACE);
}
public void setColor(int RGB) {
strokePaint.setColor(0xff000000 | RGB);
fillPaint.setColor(0xff000000 | RGB);
}
public void setFont(Font font) {
this.font = font;
androidFont = AndroidFontManager.getFont(font);
}
public void setStrokeStyle(int style) {
if (style != SOLID && style != DOTTED) {
throw new IllegalArgumentException();
}
this.strokeStyle = style;
if (style == SOLID) {
strokePaint.setPathEffect(null);
fillPaint.setPathEffect(null);
} else { // DOTTED
strokePaint.setPathEffect(dashPathEffect);
fillPaint.setPathEffect(dashPathEffect);
}
}
public void translate(int x, int y) {
if (delegate != null) {
delegate.translate(x, y);
} else {
canvas.translate(x, y);
}
super.translate(x, y);
clip.left -= x;
clip.right -= x;
clip.top -= y;
clip.bottom -= y;
}
public void drawRegion(Image src, int x_src, int y_src, int width,
int height, int transform, int x_dst, int y_dst, int anchor) {
// may throw NullPointerException, this is ok
if (x_src + width > src.getWidth() || y_src + height > src.getHeight() || width < 0 || height < 0 || x_src < 0
|| y_src < 0)
throw new IllegalArgumentException("Area out of Image");
// this cannot be done on the same image we are drawing
// check this if the implementation of getGraphics change so
// as to return different Graphic Objects on each call to
// getGraphics
if (src.isMutable() && src.getGraphics() == this)
throw new IllegalArgumentException("Image is source and target");
Bitmap img;
if (src.isMutable()) {
img = ((AndroidMutableImage) src).getBitmap();
} else {
img = ((AndroidImmutableImage) src).getBitmap();
}
tmpMatrix.reset();
int dW = width, dH = height;
switch (transform) {
case Sprite.TRANS_NONE: {
break;
}
case Sprite.TRANS_ROT90: {
tmpMatrix.preRotate(90);
img = Bitmap.createBitmap(img, x_src, y_src, width, height, tmpMatrix, true);
dW = height;
dH = width;
break;
}
case Sprite.TRANS_ROT180: {
tmpMatrix.preRotate(180);
img = Bitmap.createBitmap(img, x_src, y_src, width, height, tmpMatrix, true);
break;
}
case Sprite.TRANS_ROT270: {
tmpMatrix.preRotate(270);
img = Bitmap.createBitmap(img, x_src, y_src, width, height, tmpMatrix, true);
dW = height;
dH = width;
break;
}
case Sprite.TRANS_MIRROR: {
tmpMatrix.preScale(-1, 1);
img = Bitmap.createBitmap(img, x_src, y_src, width, height, tmpMatrix, true);
break;
}
case Sprite.TRANS_MIRROR_ROT90: {
tmpMatrix.preScale(-1, 1);
tmpMatrix.preRotate(-90);
img = Bitmap.createBitmap(img, x_src, y_src, width, height, tmpMatrix, true);
dW = height;
dH = width;
break;
}
case Sprite.TRANS_MIRROR_ROT180: {
tmpMatrix.preScale(-1, 1);
tmpMatrix.preRotate(-180);
img = Bitmap.createBitmap(img, x_src, y_src, width, height, tmpMatrix, true);
break;
}
case Sprite.TRANS_MIRROR_ROT270: {
tmpMatrix.preScale(-1, 1);
tmpMatrix.preRotate(-270);
img = Bitmap.createBitmap(img, x_src, y_src, width, height, tmpMatrix, true);
dW = height;
dH = width;
break;
}
default:
throw new IllegalArgumentException("Bad transform");
}
// process anchor and correct x and y _dest
// vertical
boolean badAnchor = false;
if (anchor == 0) {
anchor = TOP | LEFT;
}
if ((anchor & 0x7f) != anchor || (anchor & BASELINE) != 0)
badAnchor = true;
if ((anchor & TOP) != 0) {
if ((anchor & (VCENTER | BOTTOM)) != 0)
badAnchor = true;
} else if ((anchor & BOTTOM) != 0) {
if ((anchor & VCENTER) != 0)
badAnchor = true;
else {
y_dst -= dH - 1;
}
} else if ((anchor & VCENTER) != 0) {
y_dst -= (dH - 1) >>> 1;
} else {
// no vertical anchor
badAnchor = true;
}
// horizontal
if ((anchor & LEFT) != 0) {
if ((anchor & (HCENTER | RIGHT)) != 0)
badAnchor = true;
} else if ((anchor & RIGHT) != 0) {
if ((anchor & HCENTER) != 0)
badAnchor = true;
else {
x_dst -= dW - 1;
}
} else if ((anchor & HCENTER) != 0) {
x_dst -= (dW - 1) >>> 1;
} else {
// no horizontal anchor
badAnchor = true;
}
if (badAnchor) {
throw new IllegalArgumentException("Bad Anchor");
}
tmpRect.left = x_src;
tmpRect.top = y_src;
tmpRect.right = x_src + width;
tmpRect.bottom = y_src + height;
tmpRectSecond.left = x_dst;
tmpRectSecond.top = y_dst;
tmpRectSecond.right = x_dst + width;
tmpRectSecond.bottom = y_dst + height;
if (delegate != null) {
delegate.drawRegionDelegate(img, tmpRect, tmpRectSecond);
} else {
canvas.drawBitmap(img, tmpRect, tmpRectSecond, strokePaint);
}
}
public void drawRGB(int[] rgbData, int offset, int scanlength, int x,
int y, int width, int height, boolean processAlpha) {
if (rgbData == null)
throw new NullPointerException();
if (width == 0 || height == 0) {
return;
}
int l = rgbData.length;
if (width < 0 || height < 0 || offset < 0 || offset >= l || (scanlength < 0 && scanlength * (height - 1) < 0)
|| (scanlength >= 0 && scanlength * (height - 1) + width - 1 >= l)) {
throw new ArrayIndexOutOfBoundsException();
}
// MIDP allows almost any value of scanlength, drawBitmap is more strict with the stride
if (scanlength == 0) {
scanlength = width;
}
int rows = rgbData.length / scanlength;
if (rows < height) {
height = rows;
}
canvas.drawBitmap(rgbData, offset, scanlength, x, y, width, height, processAlpha, strokePaint);
}
public void fillTriangle(int x1, int y1, int x2, int y2, int x3, int y3) {
if (delegate != null) {
delegate.fillTriangle(x1, y1, x2, y2, x3, y3);
} else {
Path path = new Path();
path.moveTo(x1, y1);
path.lineTo(x2, y2);
path.lineTo(x3, y3);
path.lineTo(x1, y1);
canvas.drawPath(path, fillPaint);
}
}
public void copyArea(int x_src, int y_src, int width, int height,
int x_dest, int y_dest, int anchor) {
Logger.debug("copyArea");
}
public int getDisplayColor(int color) {
Logger.debug("getDisplayColor");
return -1;
}
}
logcat shows as follows:
10-10 19:26:13.970 D/AndroidRuntime(20484): Shutting down VM
10-10 19:26:13.971 E/AndroidRuntime(20484): FATAL EXCEPTION: main
10-10 19:26:13.971 E/AndroidRuntime(20484): Process: org.microemu.android.ketabche.QuranSearch, PID: 20484
10-10 19:26:13.971 E/AndroidRuntime(20484): java.lang.IllegalArgumentException: Invalid Region.Op - only INTERSECT and DIFFERENCE are allowed
10-10 19:26:13.971 E/AndroidRuntime(20484): at android.graphics.Canvas.checkValidClipOp(Canvas.java:779)
10-10 19:26:13.971 E/AndroidRuntime(20484): at android.graphics.Canvas.clipRect(Canvas.java:826)
10-10 19:26:13.971 E/AndroidRuntime(20484): at org.microemu.android.device.AndroidDisplayGraphics.reset(AndroidDisplayGraphics.java:101)
10-10 19:26:13.971 E/AndroidRuntime(20484): at org.microemu.android.device.ui.AndroidCanvasUI$CanvasView.onDraw(AndroidCanvasUI.java:225)
10-10 19:26:13.971 E/AndroidRuntime(20484): at android.view.View.draw(View.java:20207)
10-10 19:26:13.971 E/AndroidRuntime(20484): at android.view.View.updateDisplayListIfDirty(View.java:19082)
10-10 19:26:13.971 E/AndroidRuntime(20484): at android.view.View.draw(View.java:19935)
10-10 19:26:13.971 E/AndroidRuntime(20484): at android.view.ViewGroup.drawChild(ViewGroup.java:4333)
10-10 19:26:13.971 E/AndroidRuntime(20484): at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4112)
10-10 19:26:13.971 E/AndroidRuntime(20484): at android.view.View.updateDisplayListIfDirty(View.java:19073)
10-10 19:26:13.971 E/AndroidRuntime(20484): at android.view.View.draw(View.java:19935)
10-10 19:26:13.971 E/AndroidRuntime(20484): at android.view.ViewGroup.drawChild(ViewGroup.java:4333)
10-10 19:26:13.971 E/AndroidRuntime(20484): at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4112)
10-10 19:26:13.971 E/AndroidRuntime(20484): at android.view.View.updateDisplayListIfDirty(View.java:19073)
10-10 19:26:13.971 E/AndroidRuntime(20484): at android.view.View.draw(View.java:19935)
10-10 19:26:13.971 E/AndroidRuntime(20484): at android.view.ViewGroup.drawChild(ViewGroup.java:4333)
10-10 19:26:13.971 E/AndroidRuntime(20484): at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4112)
10-10 19:26:13.971 E/AndroidRuntime(20484): at android.view.View.draw(View.java:20210)
10-10 19:26:13.971 E/AndroidRuntime(20484): at com.android.internal.policy.DecorView.draw(DecorView.java:780)
10-10 19:26:13.971 E/AndroidRuntime(20484): at android.view.View.updateDisplayListIfDirty(View.java:19082)
10-10 19:26:13.971 E/AndroidRuntime(20484): at android.view.ThreadedRenderer.updateViewTreeDisplayList(ThreadedRenderer.java:686)
10-10 19:26:13.971 E/AndroidRuntime(20484): at android.view.ThreadedRenderer.updateRootDisplayList(ThreadedRenderer.java:692)
10-10 19:26:13.971 E/AndroidRuntime(20484): at android.view.ThreadedRenderer.draw(ThreadedRenderer.java:801)
10-10 19:26:13.971 E/AndroidRuntime(20484): at android.view.ViewRootImpl.draw(ViewRootImpl.java:3318)
10-10 19:26:13.971 E/AndroidRuntime(20484): at android.view.ViewRootImpl.performDraw(ViewRootImpl.java:3122)
10-10 19:26:13.971 E/AndroidRuntime(20484): at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2481)
10-10 19:26:13.971 E/AndroidRuntime(20484): at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1463)
10-10 19:26:13.971 E/AndroidRuntime(20484): at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:7190)
10-10 19:26:13.971 E/AndroidRuntime(20484): at android.view.Choreographer$CallbackRecord.run(Choreographer.java:949)
10-10 19:26:13.971 E/AndroidRuntime(20484): at android.view.Choreographer.doCallbacks(Choreographer.java:761)
10-10 19:26:13.971 E/AndroidRuntime(20484): at android.view.Choreographer.doFrame(Choreographer.java:696)
10-10 19:26:13.971 E/AndroidRuntime(20484): at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:935)
10-10 19:26:13.971 E/AndroidRuntime(20484): at android.os.Handler.handleCallback(Handler.java:873)
10-10 19:26:13.971 E/AndroidRuntime(20484): at android.os.Handler.dispatchMessage(Handler.java:99)
10-10 19:26:13.971 E/AndroidRuntime(20484): at android.os.Looper.loop(Looper.java:193)
10-10 19:26:13.971 E/AndroidRuntime(20484): at android.app.ActivityThread.main(ActivityThread.java:6718)
10-10 19:26:13.971 E/AndroidRuntime(20484): at java.lang.reflect.Method.invoke(Native Method)
10-10 19:26:13.971 E/AndroidRuntime(20484): at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
10-10 19:26:13.971 E/AndroidRuntime(20484): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
10-10 19:26:13.973 W/s.nexuslaunche( 6826): resources.arsc in APK '/data/app/com.UCMobile.intl-ZgwYkgY4BszTYcMmkf6BuQ==/base.apk' is compressed.
10-10 19:26:13.975 W/ActivityManager( 1206): Force finishing activity org.microemu.android.ketabche.QuranSearch/org.microemu.android.MicroEmulator
10-10 19:26:13.979 D/ ( 800): ipc_resp_thread: opcode: 0x2, payload_size: 4, resp_buf size
all experts are requested to help fixing this issue and thanks in advance