/*
Реализация спецификаций CLDC версии 1.1 (JSR-139), MIDP версии 2.1 (JSR-118)
и других спецификаций для функционирования компактных приложений на языке
Java (мидлетов) в среде программного обеспечения Малик Эмулятор.
Copyright © 2016, 2019 Малик Разработчик
Это свободная программа: вы можете перераспространять ее и/или изменять
ее на условиях Меньшей Стандартной общественной лицензии GNU в том виде,
в каком она была опубликована Фондом свободного программного обеспечения;
либо версии 3 лицензии, либо (по вашему выбору) любой более поздней версии.
Эта программа распространяется в надежде, что она будет полезной,
но БЕЗО ВСЯКИХ ГАРАНТИЙ; даже без неявной гарантии ТОВАРНОГО ВИДА
или ПРИГОДНОСТИ ДЛЯ ОПРЕДЕЛЕННЫХ ЦЕЛЕЙ. Подробнее см. в Меньшей Стандартной
общественной лицензии GNU.
Вы должны были получить копию Меньшей Стандартной общественной лицензии GNU
вместе с этой программой. Если это не так, см.
<https://www.gnu.org/licenses/>.
*/
package malik.emulator.media.graphics;
import malik.emulator.media.text.*;
public class RasterCanvas extends Object
{
public static final int TRANSFORM_NONE = 0;
public static final int TRANSFORM_ROTATE_90 = 1;
public static final int TRANSFORM_ROTATE_180 = 2;
public static final int TRANSFORM_ROTATE_270 = 3;
public static final int TRANSFORM_MIRROR = 4;
public static final int TRANSFORM_MIRROR_ROTATE_90 = 5;
public static final int TRANSFORM_MIRROR_ROTATE_180 = 6;
public static final int TRANSFORM_MIRROR_ROTATE_270 = 7;
private static final RasterCanvas SCREEN_CANVAS;
static
{
int s;
int w;
int h;
GraphicBufferDescriptor d;
MalikSystem.syscall(
((long) (d = new GraphicBufferDescriptor()).getDescriptorAddress()) << 32, 0x0022);
SCREEN_CANVAS = new RasterCanvas(new GraphicBuffer(Array.createSystemIntArray(d.base,
(w = (s = d.sizes) & 0xffff) + ((h = s >>> 16) - 1) * (s = d.scan)), 0, s, w, h),
true);
}
public static int getElementOfGUISizes(int type, int subtype, int state)
{
return (int) MalikSystem.syscall((long) ((type & 0xff) | ((subtype & 0xff) << 8) |
((state & 0xff) << 16)), 0x0024);
}
public static int getSystemColor(int index)
{
return (int) MalikSystem.syscall((long) index, 0x0026);
}
public static RasterCanvas getScreenCanvas()
{
return SCREEN_CANVAS;
}
private boolean opaque;
private int width;
private int height;
private Object lock;
private GraphicBuffer buffer;
private GraphicBufferWithAlphaDescriptor alphaDescriptor;
private GUIElementDescriptor elementDescriptor;
private StringDrawDescriptor stringDescriptor;
private StretchDrawDescriptor stretchDescriptor;
public RasterCanvas(GraphicBuffer buffer)
{
this(buffer, false);
}
public RasterCanvas(GraphicBuffer buffer, boolean opaque)
{
if(buffer == null)
{
throw new NullPointerException("RasterCanvas: " +
"параметр buffer равен нулевой ссылке.");
}
this.opaque = opaque;
this.width = buffer.getWidth();
this.height = buffer.getHeight();
this.lock = new Object();
this.buffer = buffer;
}
public void drawElementOfGUI(int clipLeft, int clipTop, int clipWidth, int clipHeight,
int type, int subtype, int state, int left, int top, int width, int height)
{
int cleft;
int ctop;
int cwidth;
int cheight;
int w;
int h;
GUIElementDescriptor d;
if(width <= 0 || height <= 0)
{
throw new IllegalArgumentException("RasterCanvas.drawElementOfGUI: " +
"размеры могут быть только положительными.");
}
if((cwidth = Math.min(w = this.width, clipLeft + clipWidth) -
(cleft = Math.max(0, clipLeft))) > w || cwidth <= 0 ||
(cheight = Math.min(h = this.height, clipTop + clipHeight) -
(ctop = Math.max(0, clipTop))) > h || cheight <= 0)
{
return;
}
synchronized(lock)
{
(d = getElementDescriptor()).setAlpha(!opaque);
d.assign(buffer, cleft, ctop, cwidth, cheight);
d.assignElement(type, subtype, state, left - cleft, top - ctop, width, height);
MalikSystem.syscall((long) d.getDescriptorAddress(), 0x0025);
}
}
public void drawCharacter(int clipLeft, int clipTop, int clipWidth, int clipHeight,
int charCode, int x, int y, int colorARGB, RasterFont font,
boolean underline, boolean strikeout, boolean alpha)
{
int cleft;
int ctop;
int cwidth;
int cheight;
int w;
int h;
StringDrawDescriptor d;
if(font == null)
{
throw new NullPointerException("RasterCanvas.drawCharacter: " +
"параметр font равен нулевой ссылке.");
}
if((cwidth = Math.min(w = this.width, clipLeft + clipWidth) -
(cleft = Math.max(0, clipLeft))) > w || cwidth <= 0 ||
(cheight = Math.min(h = this.height, clipTop + clipHeight) -
(ctop = Math.max(0, clipTop))) > h || cheight <= 0)
{
return;
}
synchronized(lock)
{
(d = getStringDescriptor()).setAlpha(!opaque);
d.assign(buffer, cleft, ctop, cwidth, cheight);
d.assignStringUTF32(MalikSystem.getLocalVariableAddress(charCode), 1,
x - cleft, y - ctop, colorARGB, font.getHandle(), underline, strikeout, alpha);
MalikSystem.syscall((long) d.getDescriptorAddress(), 0x002d);
}
}
public void drawString(int clipLeft, int clipTop, int clipWidth, int clipHeight,
int[] src, int offset, int length, int x, int y, int colorARGB, RasterFont font,
boolean underline, boolean strikeout, boolean alpha)
{
int cleft;
int ctop;
int cwidth;
int cheight;
int lim;
int len;
int w;
int h;
StringDrawDescriptor d;
if(src == null)
{
throw new NullPointerException("RasterCanvas.drawString: " +
"параметр src равен нулевой ссылке.");
}
if((lim = offset + length) > (len = src.length) ||
lim < offset || offset > len || offset < 0)
{
throw new IndexOutOfBoundsException("RasterCanvas.drawString: " +
"индекс выходит из диапазона.");
}
if(font == null)
{
throw new NullPointerException("RasterCanvas.drawString: " +
"параметр font равен нулевой ссылке.");
}
if(length == 0 ||
(cwidth = Math.min(w = this.width, clipLeft + clipWidth) -
(cleft = Math.max(0, clipLeft))) > w || cwidth <= 0 ||
(cheight = Math.min(h = this.height, clipTop + clipHeight) -
(ctop = Math.max(0, clipTop))) > h || cheight <= 0)
{
return;
}
synchronized(lock)
{
(d = getStringDescriptor()).setAlpha(!opaque);
d.assign(buffer, cleft, ctop, cwidth, cheight);
d.assignStringUTF32(Array.getFirstElementAddress(src) + (offset << 2), length,
x - cleft, y - ctop, colorARGB, font.getHandle(), underline, strikeout, alpha);
MalikSystem.syscall((long) d.getDescriptorAddress(), 0x002d);
}
}
public void drawString(int clipLeft, int clipTop, int clipWidth, int clipHeight,
char[] src, int offset, int length, int x, int y, int colorARGB, RasterFont font,
boolean underline, boolean strikeout, boolean alpha)
{
int cleft;
int ctop;
int cwidth;
int cheight;
int lim;
int len;
int w;
int h;
StringDrawDescriptor d;
if(src == null)
{
throw new NullPointerException("RasterCanvas.drawString: " +
"параметр src равен нулевой ссылке.");
}
if((lim = offset + length) > (len = src.length) ||
lim < offset || offset > len || offset < 0)
{
throw new IndexOutOfBoundsException("RasterCanvas.drawString: " +
"индекс выходит из диапазона.");
}
if(font == null)
{
throw new NullPointerException("RasterCanvas.drawString: " +
"параметр font равен нулевой ссылке.");
}
if(length == 0 ||
(cwidth = Math.min(w = this.width, clipLeft + clipWidth) -
(cleft = Math.max(0, clipLeft))) > w || cwidth <= 0 ||
(cheight = Math.min(h = this.height, clipTop + clipHeight) -
(ctop = Math.max(0, clipTop))) > h || cheight <= 0)
{
return;
}
synchronized(lock)
{
(d = getStringDescriptor()).setAlpha(!opaque);
d.assign(buffer, cleft, ctop, cwidth, cheight);
d.assignStringUTF16(Array.getFirstElementAddress(src) + (offset << 1), length,
x - cleft, y - ctop, colorARGB, font.getHandle(), underline, strikeout, alpha);
MalikSystem.syscall((long) d.getDescriptorAddress(), 0x002d);
}
}
public void drawString(int clipLeft, int clipTop, int clipWidth, int clipHeight,
String src, int offset, int length, int x, int y, int colorARGB, RasterFont font,
boolean underline, boolean strikeout, boolean alpha)
{
int cleft;
int ctop;
int cwidth;
int cheight;
int lim;
int len;
int w;
int h;
StringDrawDescriptor d;
if(src == null)
{
throw new NullPointerException("RasterCanvas.drawString: " +
"параметр src равен нулевой ссылке.");
}
if((lim = offset + length) > (len = src.length()) ||
lim < offset || offset > len || offset < 0)
{
throw new IndexOutOfBoundsException("RasterCanvas.drawString: " +
"индекс выходит из диапазона.");
}
if(font == null)
{
throw new NullPointerException("RasterCanvas.drawString: " +
"параметр font равен нулевой ссылке.");
}
if(length == 0 ||
(cwidth = Math.min(w = this.width, clipLeft + clipWidth) -
(cleft = Math.max(0, clipLeft))) > w || cwidth <= 0 ||
(cheight = Math.min(h = this.height, clipTop + clipHeight) -
(ctop = Math.max(0, clipTop))) > h || cheight <= 0)
{
return;
}
synchronized(lock)
{
(d = getStringDescriptor()).setAlpha(!opaque);
d.assign(buffer, cleft, ctop, cwidth, cheight);
d.assignStringUTF16(Array.getFirstElementAddress(src) + (offset << 1), length,
x - cleft, y - ctop, colorARGB, font.getHandle(), underline, strikeout, alpha);
MalikSystem.syscall((long) d.getDescriptorAddress(), 0x002d);
}
}
public void drawPixelsArea(int clipLeft, int clipTop, int clipWidth, int clipHeight,
int[] src, int offset, int scanlength, int width, int height, int transform,
int dstLeft, int dstTop, int dstWidth, int dstHeight, boolean alpha)
{
int cleft;
int ctop;
int cwidth;
int cheight;
int lim;
int len;
int w;
int h;
int ofs;
int length;
StretchDrawDescriptor d;
GraphicBufferWithAlphaDescriptor a;
if(src == null)
{
throw new NullPointerException("RasterCanvas.drawPixelsArea: " +
"параметр src равен нулевой ссылке.");
}
if(width <= 0 || height <= 0 || dstWidth <= 0 || dstHeight <= 0)
{
throw new IllegalArgumentException("RasterCanvas.drawPixelsArea: " +
"все размеры могут быть только положительными.");
}
if(scanlength >= 0)
{
ofs = 0;
length = width + (height - 1) * scanlength;
} else
{
ofs = (-scanlength) * (height - 1);
length = width + ofs;
}
offset -= ofs;
if((lim = offset + length) > (len = src.length) ||
lim < offset || offset > len || offset < 0)
{
throw new IndexOutOfBoundsException("RasterCanvas.drawPixelsArea: " +
"индекс выходит из диапазона.");
}
offset += ofs;
if((cwidth = Math.min(w = this.width, clipLeft + clipWidth) -
(cleft = Math.max(0, clipLeft))) > w || cwidth <= 0 ||
(cheight = Math.min(h = this.height, clipTop + clipHeight) -
(ctop = Math.max(0, clipTop))) > h || cheight <= 0)
{
return;
}
synchronized(lock)
{
(a = getAlphaDescriptor()).setAlpha(alpha);
a.assign(src, offset, scanlength, width, height);
(d = getStretchDescriptor()).setAlpha(!opaque);
d.assign(buffer, cleft, ctop, cwidth, cheight);
d.assignStretch(dstLeft - cleft, dstTop - ctop, dstWidth, dstHeight, a, transform);
MalikSystem.syscall((long) d.getDescriptorAddress(), 0x0027);
}
}
public void drawPixelsArea(int clipLeft, int clipTop, int clipWidth, int clipHeight,
GraphicBuffer src, int left, int top, int width, int height, int transform,
int dstLeft, int dstTop, int dstWidth, int dstHeight, boolean alpha)
{
int cleft;
int ctop;
int cwidth;
int cheight;
int lim;
int len;
int w;
int h;
StretchDrawDescriptor d;
GraphicBufferWithAlphaDescriptor a;
if(src == null)
{
throw new NullPointerException("RasterCanvas.drawPixelsArea: " +
"параметр src равен нулевой ссылке.");
}
if(width <= 0 || height <= 0 || dstWidth <= 0 || dstHeight <= 0)
{
throw new IllegalArgumentException("RasterCanvas.drawPixelsArea: " +
"все размеры могут быть только положительными.");
}
if((lim = left + width) > (len = src.getWidth()) ||
lim < left || left > len || left < 0 ||
(lim = top + height) > (len = src.getHeight()) ||
lim < top || top > len || top < 0)
{
throw new IllegalArgumentException("RasterCanvas.drawPixelsArea: " +
"заданный регион выходит за пределы графического буфера.");
}
if((cwidth = Math.min(w = this.width, clipLeft + clipWidth) -
(cleft = Math.max(0, clipLeft))) > w || cwidth <= 0 ||
(cheight = Math.min(h = this.height, clipTop + clipHeight) -
(ctop = Math.max(0, clipTop))) > h || cheight <= 0)
{
return;
}
synchronized(lock)
{
(a = getAlphaDescriptor()).setAlpha(alpha);
a.assign(src, left, top, width, height);
(d = getStretchDescriptor()).setAlpha(!opaque);
d.assign(buffer, cleft, ctop, cwidth, cheight);
d.assignStretch(dstLeft - cleft, dstTop - ctop, dstWidth, dstHeight, a, transform);
MalikSystem.syscall((long) d.getDescriptorAddress(), 0x0027);
}
}
public void drawHorzLine(int clipLeft, int clipTop, int clipWidth, int clipHeight,
int x, int y, int length, int colorARGB, boolean alpha)
{
int cleft;
int ctop;
int cwidth;
int cheight;
int w;
int h;
StretchDrawDescriptor d;
GraphicBufferWithAlphaDescriptor a;
if(length < 0)
{
throw new IllegalArgumentException("RasterCanvas.drawHorzLine: " +
"длина линии не может быть отрицательной.");
}
if(length == 0 ||
(cwidth = Math.min(w = this.width, clipLeft + clipWidth) -
(cleft = Math.max(0, clipLeft))) > w || cwidth <= 0 ||
(cheight = Math.min(h = this.height, clipTop + clipHeight) -
(ctop = Math.max(0, clipTop))) > h || cheight <= 0)
{
return;
}
synchronized(lock)
{
(a = getAlphaDescriptor()).setAlpha(alpha);
a.assign(MalikSystem.getLocalVariableAddress(colorARGB), 1, 1, 1);
(d = getStretchDescriptor()).setAlpha(!opaque);
d.assign(buffer, cleft, ctop, cwidth, cheight);
d.assignStretch(x - cleft, y - ctop, length, 1, a, TRANSFORM_NONE);
MalikSystem.syscall((long) d.getDescriptorAddress(), 0x0027);
}
}
public void drawVertLine(int clipLeft, int clipTop, int clipWidth, int clipHeight,
int x, int y, int length, int colorARGB, boolean alpha)
{
int cleft;
int ctop;
int cwidth;
int cheight;
int w;
int h;
StretchDrawDescriptor d;
GraphicBufferWithAlphaDescriptor a;
if(length < 0)
{
throw new IllegalArgumentException("RasterCanvas.drawVertLine: " +
"длина линии не может быть отрицательной.");
}
if(length == 0 ||
(cwidth = Math.min(w = this.width, clipLeft + clipWidth) -
(cleft = Math.max(0, clipLeft))) > w || cwidth <= 0 ||
(cheight = Math.min(h = this.height, clipTop + clipHeight) -
(ctop = Math.max(0, clipTop))) > h || cheight <= 0)
{
return;
}
synchronized(lock)
{
(a = getAlphaDescriptor()).setAlpha(alpha);
a.assign(MalikSystem.getLocalVariableAddress(colorARGB), 1, 1, 1);
(d = getStretchDescriptor()).setAlpha(!opaque);
d.assign(buffer, cleft, ctop, cwidth, cheight);
d.assignStretch(x - cleft, y - ctop, 1, length, a, TRANSFORM_NONE);
MalikSystem.syscall((long) d.getDescriptorAddress(), 0x0027);
}
}
public void fillRect(int clipLeft, int clipTop, int clipWidth, int clipHeight,
int left, int top, int width, int height, int colorARGB, boolean alpha)
{
int cleft;
int ctop;
int cwidth;
int cheight;
int w;
int h;
StretchDrawDescriptor d;
GraphicBufferWithAlphaDescriptor a;
if(width <= 0 || height <= 0)
{
throw new IllegalArgumentException("RasterCanvas.fillRect: " +
"размеры могут быть только положительными.");
}
if((cwidth = Math.min(w = this.width, clipLeft + clipWidth) -
(cleft = Math.max(0, clipLeft))) > w || cwidth <= 0 ||
(cheight = Math.min(h = this.height, clipTop + clipHeight) -
(ctop = Math.max(0, clipTop))) > h || cheight <= 0)
{
return;
}
synchronized(lock)
{
(a = getAlphaDescriptor()).setAlpha(alpha);
a.assign(MalikSystem.getLocalVariableAddress(colorARGB), 1, 1, 1);
(d = getStretchDescriptor()).setAlpha(!opaque);
d.assign(buffer, cleft, ctop, cwidth, cheight);
d.assignStretch(left - cleft, top - ctop, width, height, a, TRANSFORM_NONE);
MalikSystem.syscall((long) d.getDescriptorAddress(), 0x0027);
}
}
public RasterCanvas subcanvas(int left, int top, int width, int height)
{
int s;
int w;
int h;
int lim;
GraphicBuffer g;
if(width <= 0 || height <= 0)
{
throw new IllegalArgumentException("RasterCanvas.subcanvas: " +
"размеры могут быть только положительными.");
}
if((lim = left + width) > (w = this.width) ||
lim < left || left > w || left < 0 ||
(lim = top + height) > (h = this.height) ||
lim < top || top > h || top < 0)
{
throw new IllegalArgumentException("RasterCanvas.subcanvas: " +
"заданная прямоугольная область выходит за пределы растровой канвы.");
}
return left == 0 && top == 0 && width == w && height == h ? this :
new RasterCanvas(new GraphicBuffer((g = buffer).getPixels(),
g.getOffset() + left + top * (s = g.getScanlength()), s, width, height), opaque);
}
public final void copyArea(int clipLeft, int clipTop, int clipWidth, int clipHeight,
int left, int top, int width, int height, int dstLeft, int dstTop)
{
int cleft;
int ctop;
int cwidth;
int cheight;
int lim;
int w;
int h;
GraphicBuffer g;
StretchDrawDescriptor d;
GraphicBufferWithAlphaDescriptor a;
if(opaque)
{
throw new IllegalStateException("RasterCanvas.copyArea: " +
"эта растровая канва представляет экран устройства, " +
"поэтому её пикселы читать нельзя.");
}
if(width <= 0 || height <= 0)
{
throw new IllegalArgumentException("RasterCanvas.copyArea: " +
"размеры могут быть только положительными.");
}
if((lim = left + width) > (w = this.width) ||
lim < left || left > w || left < 0 ||
(lim = top + height) > (h = this.height) ||
lim < top || top > h || top < 0)
{
throw new IllegalArgumentException("RasterCanvas.copyArea: " +
"заданная прямоугольная область выходит за пределы растровой канвы.");
}
if((cwidth = Math.min(w, clipLeft + clipWidth) -
(cleft = Math.max(0, clipLeft))) > w || cwidth <= 0 ||
(cheight = Math.min(h, clipTop + clipHeight) -
(ctop = Math.max(0, clipTop))) > h || cheight <= 0)
{
return;
}
synchronized(lock)
{
(a = getAlphaDescriptor()).setAlpha(true);
a.assign(g = buffer, left, top, width, height);
(d = getStretchDescriptor()).setAlpha(true);
d.assign(g, cleft, ctop, cwidth, cheight);
d.assignStretch(dstLeft - cleft, dstTop - ctop, width, height, a, TRANSFORM_NONE);
MalikSystem.syscall((long) d.getDescriptorAddress(), 0x0027);
}
}
public final void getPixels(int[] dst, int offset, int scanlength,
int left, int top, int width, int height)
{
int i;
int lim;
int len;
int length;
int srcScan;
int srcLine;
int dstLine;
int[] src;
GraphicBuffer g;
if(opaque)
{
throw new IllegalStateException("RasterCanvas.getPixels: " +
"эта растровая канва представляет экран устройства, " +
"поэтому её пикселы читать нельзя.");
}
if(dst == null)
{
throw new NullPointerException("RasterCanvas.getPixels: " +
"параметр dst равен нулевой ссылке.");
}
if(width <= 0 || height <= 0)
{
throw new IllegalArgumentException("RasterCanvas.getPixels: " +
"размеры могут быть только положительными.");
}
if((lim = left + width) > (len = this.width) ||
lim < left || left > len || left < 0 ||
(lim = top + height) > (len = this.height) ||
lim < top || top > len || top < 0)
{
throw new IllegalArgumentException("RasterCanvas.getPixels: " +
"заданная прямоугольная область выходит за пределы растровой канвы.");
}
if(Math.abs(scanlength) < width)
{
throw new IllegalArgumentException("RasterCanvas.getPixels: " +
"длина линии сканирования не может быть меньше ширины.");
}
if(scanlength >= 0)
{
i = 0;
length = width + (height - 1) * scanlength;
} else
{
i = (-scanlength) * (height - 1);
length = width + i;
}
offset -= i;
if((lim = offset + length) > (len = dst.length) ||
lim < offset || offset > len || offset < 0)
{
throw new ArrayIndexOutOfBoundsException("RasterCanvas.getPixels: " +
"индекс выходит из диапазона.");
}
offset += i;
src = (g = buffer).getPixels();
srcLine = g.getOffset() + left + top * (srcScan = g.getScanlength());
dstLine = offset;
for(i = 0; i < height; srcLine += srcScan, dstLine += scanlength, i++)
{
Array.copy(src, srcLine, dst, dstLine, width);
}
}
public final void updateScreen()
{
if(SCREEN_CANVAS != this)
{
throw new IllegalStateException("RasterCanvas.updateScreen: " +
"эта операция доступна только для растровой канвы, " +
"которая представляет экран устройства.");
}
MalikSystem.syscall(0L, 0x0020);
}
public final boolean isScreenCanvas()
{
return SCREEN_CANVAS == this;
}
public final boolean isOpaque()
{
return opaque;
}
public final int getWidth()
{
return width;
}
public final int getHeight()
{
return height;
}
public final GraphicBuffer getBuffer()
{
return buffer;
}
private GraphicBufferWithAlphaDescriptor getAlphaDescriptor()
{
GraphicBufferWithAlphaDescriptor result;
if((result = alphaDescriptor) == null)
{
result = alphaDescriptor = new GraphicBufferWithAlphaDescriptor();
}
return result;
}
private GUIElementDescriptor getElementDescriptor()
{
GUIElementDescriptor result;
if((result = elementDescriptor) == null)
{
result = elementDescriptor = new GUIElementDescriptor();
}
return result;
}
private StringDrawDescriptor getStringDescriptor()
{
StringDrawDescriptor result;
if((result = stringDescriptor) == null)
{
result = stringDescriptor = new StringDrawDescriptor();
}
return result;
}
private StretchDrawDescriptor getStretchDescriptor()
{
StretchDrawDescriptor result;
if((result = stretchDescriptor) == null)
{
result = stretchDescriptor = new StretchDrawDescriptor();
}
return result;
}
}