blob: 0949c1f2909814c2a82c27c0d8a9c03eb924e450 [file] [log] [blame]
/*=============================================================================#
# Copyright (c) 2009, 2019 Stephan Wahlbrink and others.
#
# This program and the accompanying materials are made available under the
# terms of the Eclipse Public License 2.0 which is available at
# https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
# which is available at https://www.apache.org/licenses/LICENSE-2.0.
#
# SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
#
# Contributors:
# Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
#=============================================================================*/
package org.eclipse.statet.rj.eclient.graphics;
import java.util.List;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.LineAttributes;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.graphics.Transform;
import org.eclipse.statet.internal.rj.eclient.graphics.CircleElement;
import org.eclipse.statet.internal.rj.eclient.graphics.ClipSetting;
import org.eclipse.statet.internal.rj.eclient.graphics.ColorSetting;
import org.eclipse.statet.internal.rj.eclient.graphics.FillSetting;
import org.eclipse.statet.internal.rj.eclient.graphics.FontSetting;
import org.eclipse.statet.internal.rj.eclient.graphics.GraphicInitialization;
import org.eclipse.statet.internal.rj.eclient.graphics.LineElement;
import org.eclipse.statet.internal.rj.eclient.graphics.LineSetting;
import org.eclipse.statet.internal.rj.eclient.graphics.PathElement;
import org.eclipse.statet.internal.rj.eclient.graphics.PolygonElement;
import org.eclipse.statet.internal.rj.eclient.graphics.PolylineElement;
import org.eclipse.statet.internal.rj.eclient.graphics.RasterElement;
import org.eclipse.statet.internal.rj.eclient.graphics.RectElement;
import org.eclipse.statet.internal.rj.eclient.graphics.TextElement;
import org.eclipse.statet.rj.graphic.core.RGraphicInstruction;
import org.eclipse.statet.rj.graphic.core.RLineSetting;
import org.eclipse.statet.rj.server.client.RClientGraphic;
public class DefaultGCRenderer {
private static int swtLineJoin2Cap(final int join) {
return (join == SWT.JOIN_ROUND) ? SWT.CAP_ROUND : SWT.CAP_FLAT;
}
private double scale= 1.0f;
private final LineAttributes lineAttributes= new LineAttributes(1.0f);
private Color lineColor;
private int lineAlpha;
private Color fillColor;
private int fillAlpha;
private final double[] fontProperties= new double[1];
public void clear(final double scale) {
this.scale= scale;
this.lineColor= null;
this.lineAlpha= 0xff;
this.fillColor= null;
this.fillAlpha= 0xff;
this.lineAttributes.style= SWT.LINE_SOLID;
this.lineAttributes.width= (float) scale;
this.lineAttributes.cap= SWT.CAP_ROUND;
this.lineAttributes.join= SWT.JOIN_ROUND;
this.lineAttributes.miterLimit= (float) (10.0 * scale);
}
public void paint(final GC gc, final List<? extends ERGraphicInstruction> instructions) {
final Transform defaultTransform= null;
final Transform tempTransform= new Transform(gc.getDevice());
final Rectangle tempRect= new Rectangle(0, 0, 0, 0);
final double scale= this.scale;
int currentAlpha= -1;
int currentInterpolation= -1;
int currentFillRule= -1;
Color lineColor= this.lineColor;
int lineAlpha= this.lineAlpha;
Color fillColor= this.fillColor;
int fillAlpha= this.fillAlpha;
try {
final Rectangle bounds= gc.getClipping();
gc.setAdvanced(true);
gc.setAntialias(SWT.ON);
gc.setTextAntialias(SWT.ON);
gc.setLineAttributes(this.lineAttributes);
gc.setTransform(defaultTransform);
gc.setAlpha(currentAlpha);
if (this.lineColor != null) {
gc.setForeground(this.lineColor);
}
if (this.fillColor != null) {
gc.setBackground(this.fillColor);
}
for (final ERGraphicInstruction instr : instructions) {
switch (instr.getInstructionType()) {
case RGraphicInstruction.INIT: {
final GraphicInitialization init= (GraphicInitialization) instr;
gc.setBackground(init.swtCanvasColor);
gc.setAlpha(currentAlpha= 0xff);
tempRect.x= 0;
tempRect.y= 0;
tempRect.width= (int) (init.width * scale + 0.5);
tempRect.height= (int) (init.height * scale + 0.5);
tempRect.intersect(bounds);
gc.setClipping(tempRect);
gc.fillRectangle(tempRect);
// gc.setBackground(fillColor= gc.getDevice().getSystemColor(SWT.COLOR_WHITE));
// gc.setForeground(lineColor= gc.getDevice().getSystemColor(SWT.COLOR_BLACK));
continue;
}
case RGraphicInstruction.SET_CLIP: {
final ClipSetting setting= (ClipSetting) instr;
tempRect.x= (int) (setting.x0 * scale + 1.5);
tempRect.y= (int) (setting.y0 * scale + 1.5);
tempRect.width= (int) (setting.x1 * scale + 0.5) - tempRect.x;
tempRect.height= (int) (setting.y1 * scale + 0.5) - tempRect.y;
tempRect.intersect(bounds);
gc.setClipping(tempRect);
continue;
}
case RGraphicInstruction.SET_COLOR: {
final ColorSetting setting= (ColorSetting) instr;
lineAlpha= setting.getAlpha();
gc.setForeground(lineColor= setting.swtColor);
continue;
}
case RGraphicInstruction.SET_FILL: {
final FillSetting setting= (FillSetting) instr;
fillAlpha= setting.getAlpha();
gc.setBackground(fillColor= setting.swtColor);
continue;
}
case RGraphicInstruction.SET_LINE: {
final LineSetting setting= (LineSetting) instr;
this.lineAttributes.cap= setting.swtCap();
this.lineAttributes.join= setting.swtJoin();
this.lineAttributes.miterLimit= (float) (setting.joinMiterLimit * scale);
switch (setting.type) {
case RLineSetting.TYPE_SOLID:
this.lineAttributes.style= SWT.LINE_SOLID;
this.lineAttributes.width= (float) (setting.width * scale);
gc.setLineAttributes(this.lineAttributes);
continue;
case RLineSetting.TYPE_BLANK:
this.lineAttributes.style= SWT.LINE_SOLID;
this.lineAttributes.width= 0.0f;
gc.setLineAttributes(this.lineAttributes);
continue;
// case 0x44:
// this.tempLineAttributes.style= SWT.LINE_DASH;
// this.tempLineAttributes.width= (float) (setting.width * scale);
// gc.setLineAttributes(this.tempLineAttributes);
// continue;
// case 0x13:
// this.tempLineAttributes.style= SWT.LINE_DOT;
// this.tempLineAttributes.width= (float) (setting.width * scale);
// gc.setLineAttributes(this.tempLineAttributes);
// continue;
// case 0x1343:
// this.tempLineAttributes.style= SWT.LINE_DASHDOT;
// this.tempLineAttributes.width= (float) (setting.width * scale);
// gc.setLineAttributes(this.tempLineAttributes);
// continue;
default:
this.lineAttributes.style= SWT.LINE_SOLID;
this.lineAttributes.width= (float) (setting.width * scale);
gc.setLineAttributes(this.lineAttributes);
gc.setLineDash(setting.swtDashes());
continue;
}
}
case RGraphicInstruction.SET_FONT: {
final FontSetting setting= (FontSetting) instr;
gc.setFont(setting.swtFont);
this.fontProperties[0]= setting.swtProperties[0];
continue;
}
case RGraphicInstruction.DRAW_LINE: {
final LineElement element= (LineElement) instr;
if (lineAlpha != currentAlpha) {
gc.setAlpha(currentAlpha= lineAlpha);
}
gc.drawLine(
(int) (element.x0 * scale + 0.5),
(int) (element.y0 * scale + 0.5),
(int) (element.x1 * scale + 0.5),
(int) (element.y1 * scale + 0.5) );
continue;
}
case RGraphicInstruction.DRAW_RECTANGLE: {
final RectElement element= (RectElement) instr;
// small dots: priority on size / center
// default: priority on edges
final int ix0, iy0;
final int iw, ih;
if ((element.x1 - element.x0) < 5.1111 && (element.y1 - element.y0) < 5.1111) {
iw= (int) ((element.x1 - element.x0) * scale + 0.5);
if (lineAlpha == 0) {
ix0= (int) (((element.x0 + element.x1) * scale - iw) / 2.0 + 0.5);
// System.out.println("(a==0) ix0= " + ix0 + " (" + (float) (((element.x0 + element.x1) * scale - iw) / 2.0) + ")"
// + ", x0= " + (float) (element.x0 * scale) + ", iw= " + iw + " (" + (float) ((element.x1 - element.x0) * scale) + ")"
// + ", c= " + (float) ((element.x0 + element.x1) * scale / 2.0));
}
else {
ix0= (int) (((element.x0 + element.x1) * scale - (iw + 1)) / 2.0 + 0.5);
// System.out.println("(a!=0) ix0= " + ix0 + " (" + (float) (((element.x0 + element.x1) * scale - (iw + 1)) / 2.0) + ")"
// + ", x0= " + (float) (element.x0 * scale) + ", iw= " + iw + "+1 (" + (float) ((element.x1 - element.x0) * scale) + ")"
// + ", c= " + (float) ((element.x0 + element.x1) * scale / 2.0));
}
ih= (int) ((element.y1 - element.y0) * scale + 0.5);
if (lineAlpha == 0) {
iy0= (int) (((element.y0 + element.y1) * scale - ih) / 2.0 + 0.5);
}
else {
iy0= (int) (((element.y0 + element.y1) * scale - (ih + 1)) / 2.0 + 0.5);
}
}
else {
ix0= (int) (element.x0 * scale + 0.5);
if (lineAlpha == 0) {
iw= (int) (element.x1 * scale + 0.5) - ix0 + 1;
}
else {
iw= (int) (element.x1 * scale + 0.5) - ix0;
}
iy0= (int) (element.y0 * scale + 0.5);
if (lineAlpha == 0) {
ih= (int) (element.y1 * scale + 0.5) - iy0 + 1;
}
else {
ih= (int) (element.y1 * scale + 0.5) - iy0;
}
}
if (iw == 0 || ih == 0) {
if (lineAlpha == 0) {
continue;
}
if (lineAlpha != currentAlpha) {
gc.setAlpha(currentAlpha= lineAlpha);
}
gc.setLineCap(swtLineJoin2Cap(this.lineAttributes.join));
gc.drawLine(ix0, iy0, ix0 + iw, iy0 + ih);
gc.setLineCap(this.lineAttributes.cap);
continue;
}
if (fillAlpha != 0) {
if (lineAlpha == 0) {
if (fillAlpha != currentAlpha) {
gc.setAlpha(currentAlpha= fillAlpha);
}
gc.fillRectangle(ix0, iy0, iw, ih);
continue;
}
if (iw > 1 && ih > 1) {
if (fillAlpha != currentAlpha) {
gc.setAlpha(currentAlpha= fillAlpha);
}
gc.fillRectangle(ix0 + 1, iy0 + 1, iw - 1, ih - 1);
}
}
if (lineAlpha != 0) {
if (lineAlpha != currentAlpha) {
gc.setAlpha(currentAlpha= lineAlpha);
}
gc.drawRectangle(ix0, iy0, iw, ih);
}
continue;
}
case RGraphicInstruction.DRAW_POLYLINE: {
final PolylineElement element= (PolylineElement) instr;
final int[] icoord;
{ final int n= element.x.length;
icoord= new int[n * 2];
for (int i= 0, j= 0; j < n; j++) {
icoord[i++]= (int) (element.x[j] * scale + 0.5);
icoord[i++]= (int) (element.y[j] * scale + 0.5);
}
}
if (lineAlpha != currentAlpha) {
gc.setAlpha(currentAlpha= lineAlpha);
}
gc.drawPolyline(icoord);
continue;
}
case RGraphicInstruction.DRAW_POLYGON: {
final PolygonElement element= (PolygonElement) instr;
final int[] icoord;
{ final int n= element.x.length;
icoord= new int[n * 2];
for (int i= 0, j= 0; j < n; j++) {
icoord[i++]= (int) (element.x[j] * scale + 0.5);
icoord[i++]= (int) (element.y[j] * scale + 0.5);
}
}
if (fillAlpha != 0) {
if (fillAlpha != currentAlpha) {
gc.setAlpha(currentAlpha= fillAlpha);
}
if (SWT.FILL_EVEN_ODD != currentFillRule) {
gc.setFillRule(currentFillRule= SWT.FILL_EVEN_ODD);
}
gc.fillPolygon(icoord);
}
if (lineAlpha != 0) {
if (lineAlpha != currentAlpha) {
gc.setAlpha(currentAlpha= lineAlpha);
}
gc.drawPolygon(icoord);
}
continue;
}
case RGraphicInstruction.DRAW_PATH: {
final PathElement element= (PathElement) instr;
{ final int fillRule= ((element.mode & RClientGraphic.MASK_FILL_RULE) == RClientGraphic.FILL_WIND_NON_ZERO) ?
SWT.FILL_WINDING : SWT.FILL_EVEN_ODD;
if (fillRule != currentFillRule) {
gc.setFillRule(currentFillRule= fillRule);
}
}
if (fillAlpha != 0) {
if (fillAlpha != currentAlpha) {
gc.setAlpha(currentAlpha= fillAlpha);
}
gc.fillPath(element.swtPath);
}
if (lineAlpha != 0) {
if (lineAlpha != currentAlpha) {
gc.setAlpha(currentAlpha= lineAlpha);
}
gc.drawPath(element.swtPath);
}
continue;
}
case RGraphicInstruction.DRAW_CIRCLE: {
final CircleElement element= (CircleElement) instr;
final int id= (int) (element.r * 2.0 + 0.5);
tempTransform.setElements(1, 0, 0, 1,
(float) (element.x - id / 2.0),
(float) (element.y - id / 2.0) );
gc.setTransform(tempTransform);
if (fillAlpha != 0) {
if (fillAlpha != currentAlpha) {
gc.setAlpha(currentAlpha= fillAlpha);
}
gc.fillOval(1, 1, id-1, id-1);
}
if (lineAlpha != 0) {
if (lineAlpha != currentAlpha) {
gc.setAlpha(currentAlpha= lineAlpha);
}
gc.drawOval(0, 0, id, id);
}
gc.setTransform(defaultTransform);
continue;
}
case RGraphicInstruction.DRAW_TEXT: {
final TextElement element= (TextElement) instr;
final double hShift;
if (element.horizontalAdjust != 0.0) {
hShift= element.horizontalAdjust * element.swtStrWidth;
}
else {
hShift= 0.0;
}
if (element.rotateDegree != 0.0) {
tempTransform.setElements(1, 0, 0, 1,
(float) (element.x * scale),
(float) (element.y * scale) );
tempTransform.rotate((float) -element.rotateDegree);
tempTransform.translate(
(float) Math.floor(1.1111 - hShift),
(float) Math.floor(0.0511 - this.fontProperties[0]) );
gc.setTransform(tempTransform);
if (lineAlpha != currentAlpha) {
gc.setAlpha(currentAlpha= lineAlpha);
}
gc.drawString(element.text, 0, 0, true);
gc.setTransform(defaultTransform);
continue;
}
else {
if (lineAlpha != currentAlpha) {
gc.setAlpha(currentAlpha= lineAlpha);
}
gc.drawString(element.text,
(int) Math.floor(1.1111 + element.x - hShift),
(int) Math.floor(0.6111 + element.y - this.fontProperties[0]),
true );
continue;
}
}
case RGraphicInstruction.DRAW_RASTER: {
final RasterElement element= (RasterElement) instr;
if (0xff != currentAlpha) {
gc.setAlpha(currentAlpha= 0xff);
}
{ final int interpolation= (element.interpolate) ? SWT.LOW : SWT.NONE;
if (interpolation != currentInterpolation) {
gc.setInterpolation(currentInterpolation= interpolation);
}
}
final int ix, iy;
final int ih, iw;
if (element.width >= 0) {
ix= (int) Math.floor(element.x * scale + 0.5);
iw= (int) (element.width * scale + 0.5);
}
else {
ix= (int) Math.floor((element.x + element.height) * scale + 0.5);
iw= (int) (-element.width * scale + 0.5);
}
if (element.height >= 0) {
iy= (int) Math.floor(element.y * scale + 0.5);
ih= (int) (element.height * scale + 0.5);
}
else {
iy= (int) Math.floor((element.y + element.height) * scale + 0.5);
ih= (int) (-element.height * scale + 0.5);
}
if (element.rotateDegree != 0.0) {
tempTransform.setElements(1, 0, 0, 1,
(float) (element.x * scale),
(float) (element.y * scale) );
tempTransform.rotate(-(float) element.rotateDegree);
if (element.width < 0 || element.height < 0) {
tempTransform.translate(
(element.width < 0) ? -iw : 0,
(element.height < 0) ? -ih : 0 );
}
gc.setTransform(tempTransform);
gc.drawImage(element.swtImage, 0, 0, element.imgWidth, element.imgHeight,
0, 0, iw, ih );
gc.setTransform(defaultTransform);
continue;
}
else {
gc.drawImage(element.swtImage, 0, 0, element.imgWidth, element.imgHeight,
ix, iy, iw, ih );
continue;
}
}
}
}
this.lineColor= lineColor;
this.lineAlpha= lineAlpha;
this.fillColor= fillColor;
this.fillAlpha= fillAlpha;
}
finally {
tempTransform.dispose();
}
}
}