diff options
author | Bob Badour <bbadour@google.com> | 2020-05-06 14:17:12 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2020-05-06 14:17:12 +0000 |
commit | fc7cda06f54946e3a03ea008c1ba086d90aeef84 (patch) | |
tree | fd845444b59dfc72656b7781596e0b1a0662c4c7 /eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GCWrapper.java | |
parent | 1aa104fb55f1b87494e4e2bc38e23416f486adfd (diff) | |
parent | 512ba8f80c0a2422f9e58a1401cb142db190a56c (diff) | |
download | sdk-fc7cda06f54946e3a03ea008c1ba086d90aeef84.tar.gz |
Merge "Revert "Remove unused project.""
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GCWrapper.java')
-rw-r--r-- | eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GCWrapper.java | 645 |
1 files changed, 645 insertions, 0 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GCWrapper.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GCWrapper.java new file mode 100644 index 000000000..354517e76 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GCWrapper.java @@ -0,0 +1,645 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php + * + * 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.adt.internal.editors.layout.gle2; + +import com.android.annotations.NonNull; +import com.android.ide.common.api.DrawingStyle; +import com.android.ide.common.api.IColor; +import com.android.ide.common.api.IGraphics; +import com.android.ide.common.api.IViewRule; +import com.android.ide.common.api.Point; +import com.android.ide.common.api.Rect; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.SWTException; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.FontMetrics; +import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.graphics.RGB; + +import java.util.EnumMap; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Wraps an SWT {@link GC} into an {@link IGraphics} interface so that {@link IViewRule} objects + * can directly draw on the canvas. + * <p/> + * The actual wrapped GC object is only non-null during the context of a paint operation. + */ +public class GCWrapper implements IGraphics { + + /** + * The actual SWT {@link GC} being wrapped. This can change during the lifetime of the + * object. It is generally set to something during an onPaint method and then changed + * to null when not in the context of a paint. + */ + private GC mGc; + + /** + * Current style being used for drawing. + */ + private SwtDrawingStyle mCurrentStyle = SwtDrawingStyle.INVALID; + + /** + * Implementation of IColor wrapping an SWT color. + */ + private static class ColorWrapper implements IColor { + private final Color mColor; + + public ColorWrapper(Color color) { + mColor = color; + } + + public Color getColor() { + return mColor; + } + } + + /** A map of registered colors. All these colors must be disposed at the end. */ + private final HashMap<Integer, ColorWrapper> mColorMap = new HashMap<Integer, ColorWrapper>(); + + /** + * A map of the {@link SwtDrawingStyle} stroke colors that we have actually + * used (to be disposed) + */ + private final Map<DrawingStyle, Color> mStyleStrokeMap = new EnumMap<DrawingStyle, Color>( + DrawingStyle.class); + + /** + * A map of the {@link SwtDrawingStyle} fill colors that we have actually + * used (to be disposed) + */ + private final Map<DrawingStyle, Color> mStyleFillMap = new EnumMap<DrawingStyle, Color>( + DrawingStyle.class); + + /** The cached pixel height of the default current font. */ + private int mFontHeight = 0; + + /** The scaling of the canvas in X. */ + private final CanvasTransform mHScale; + /** The scaling of the canvas in Y. */ + private final CanvasTransform mVScale; + + public GCWrapper(CanvasTransform hScale, CanvasTransform vScale) { + mHScale = hScale; + mVScale = vScale; + mGc = null; + } + + void setGC(GC gc) { + mGc = gc; + } + + private GC getGc() { + return mGc; + } + + void checkGC() { + if (mGc == null) { + throw new RuntimeException("IGraphics used without a valid context."); + } + } + + void dispose() { + for (ColorWrapper c : mColorMap.values()) { + c.getColor().dispose(); + } + mColorMap.clear(); + + for (Color c : mStyleStrokeMap.values()) { + c.dispose(); + } + mStyleStrokeMap.clear(); + + for (Color c : mStyleFillMap.values()) { + c.dispose(); + } + mStyleFillMap.clear(); + } + + //------------- + + @Override + public @NonNull IColor registerColor(int rgb) { + checkGC(); + + Integer key = Integer.valueOf(rgb); + ColorWrapper c = mColorMap.get(key); + if (c == null) { + c = new ColorWrapper(new Color(getGc().getDevice(), + (rgb >> 16) & 0xFF, + (rgb >> 8) & 0xFF, + (rgb >> 0) & 0xFF)); + mColorMap.put(key, c); + } + + return c; + } + + /** Returns the (cached) pixel height of the current font. */ + @Override + public int getFontHeight() { + if (mFontHeight < 1) { + checkGC(); + FontMetrics fm = getGc().getFontMetrics(); + mFontHeight = fm.getHeight(); + } + return mFontHeight; + } + + @Override + public @NonNull IColor getForeground() { + Color c = getGc().getForeground(); + return new ColorWrapper(c); + } + + @Override + public @NonNull IColor getBackground() { + Color c = getGc().getBackground(); + return new ColorWrapper(c); + } + + @Override + public int getAlpha() { + return getGc().getAlpha(); + } + + @Override + public void setForeground(@NonNull IColor color) { + checkGC(); + getGc().setForeground(((ColorWrapper) color).getColor()); + } + + @Override + public void setBackground(@NonNull IColor color) { + checkGC(); + getGc().setBackground(((ColorWrapper) color).getColor()); + } + + @Override + public void setAlpha(int alpha) { + checkGC(); + try { + getGc().setAlpha(alpha); + } catch (SWTException e) { + // This means that we cannot set the alpha on this platform; this is + // an acceptable no-op. + } + } + + @Override + public void setLineStyle(@NonNull LineStyle style) { + int swtStyle = 0; + switch (style) { + case LINE_SOLID: + swtStyle = SWT.LINE_SOLID; + break; + case LINE_DASH: + swtStyle = SWT.LINE_DASH; + break; + case LINE_DOT: + swtStyle = SWT.LINE_DOT; + break; + case LINE_DASHDOT: + swtStyle = SWT.LINE_DASHDOT; + break; + case LINE_DASHDOTDOT: + swtStyle = SWT.LINE_DASHDOTDOT; + break; + default: + assert false : style; + break; + } + + if (swtStyle != 0) { + checkGC(); + getGc().setLineStyle(swtStyle); + } + } + + @Override + public void setLineWidth(int width) { + checkGC(); + if (width > 0) { + getGc().setLineWidth(width); + } + } + + // lines + + @Override + public void drawLine(int x1, int y1, int x2, int y2) { + checkGC(); + useStrokeAlpha(); + x1 = mHScale.translate(x1); + y1 = mVScale.translate(y1); + x2 = mHScale.translate(x2); + y2 = mVScale.translate(y2); + getGc().drawLine(x1, y1, x2, y2); + } + + @Override + public void drawLine(@NonNull Point p1, @NonNull Point p2) { + drawLine(p1.x, p1.y, p2.x, p2.y); + } + + // rectangles + + @Override + public void drawRect(int x1, int y1, int x2, int y2) { + checkGC(); + useStrokeAlpha(); + int x = mHScale.translate(x1); + int y = mVScale.translate(y1); + int w = mHScale.scale(x2 - x1); + int h = mVScale.scale(y2 - y1); + getGc().drawRectangle(x, y, w, h); + } + + @Override + public void drawRect(@NonNull Point p1, @NonNull Point p2) { + drawRect(p1.x, p1.y, p2.x, p2.y); + } + + @Override + public void drawRect(@NonNull Rect r) { + checkGC(); + useStrokeAlpha(); + int x = mHScale.translate(r.x); + int y = mVScale.translate(r.y); + int w = mHScale.scale(r.w); + int h = mVScale.scale(r.h); + getGc().drawRectangle(x, y, w, h); + } + + @Override + public void fillRect(int x1, int y1, int x2, int y2) { + checkGC(); + useFillAlpha(); + int x = mHScale.translate(x1); + int y = mVScale.translate(y1); + int w = mHScale.scale(x2 - x1); + int h = mVScale.scale(y2 - y1); + getGc().fillRectangle(x, y, w, h); + } + + @Override + public void fillRect(@NonNull Point p1, @NonNull Point p2) { + fillRect(p1.x, p1.y, p2.x, p2.y); + } + + @Override + public void fillRect(@NonNull Rect r) { + checkGC(); + useFillAlpha(); + int x = mHScale.translate(r.x); + int y = mVScale.translate(r.y); + int w = mHScale.scale(r.w); + int h = mVScale.scale(r.h); + getGc().fillRectangle(x, y, w, h); + } + + // circles (actually ovals) + + public void drawOval(int x1, int y1, int x2, int y2) { + checkGC(); + useStrokeAlpha(); + int x = mHScale.translate(x1); + int y = mVScale.translate(y1); + int w = mHScale.scale(x2 - x1); + int h = mVScale.scale(y2 - y1); + getGc().drawOval(x, y, w, h); + } + + public void drawOval(Point p1, Point p2) { + drawOval(p1.x, p1.y, p2.x, p2.y); + } + + public void drawOval(Rect r) { + checkGC(); + useStrokeAlpha(); + int x = mHScale.translate(r.x); + int y = mVScale.translate(r.y); + int w = mHScale.scale(r.w); + int h = mVScale.scale(r.h); + getGc().drawOval(x, y, w, h); + } + + public void fillOval(int x1, int y1, int x2, int y2) { + checkGC(); + useFillAlpha(); + int x = mHScale.translate(x1); + int y = mVScale.translate(y1); + int w = mHScale.scale(x2 - x1); + int h = mVScale.scale(y2 - y1); + getGc().fillOval(x, y, w, h); + } + + public void fillOval(Point p1, Point p2) { + fillOval(p1.x, p1.y, p2.x, p2.y); + } + + public void fillOval(Rect r) { + checkGC(); + useFillAlpha(); + int x = mHScale.translate(r.x); + int y = mVScale.translate(r.y); + int w = mHScale.scale(r.w); + int h = mVScale.scale(r.h); + getGc().fillOval(x, y, w, h); + } + + + // strings + + @Override + public void drawString(@NonNull String string, int x, int y) { + checkGC(); + useStrokeAlpha(); + x = mHScale.translate(x); + y = mVScale.translate(y); + // Background fill of text is not useful because it does not + // use the alpha; we instead supply a separate method (drawBoxedStrings) which + // first paints a semi-transparent mask for the text to sit on + // top of (this ensures that the text is readable regardless of + // colors of the pixels below the text) + getGc().drawString(string, x, y, true /*isTransparent*/); + } + + @Override + public void drawBoxedStrings(int x, int y, @NonNull List<?> strings) { + checkGC(); + + x = mHScale.translate(x); + y = mVScale.translate(y); + + // Compute bounds of the box by adding up the sum of the text heights + // and the max of the text widths + int width = 0; + int height = 0; + int lineHeight = getGc().getFontMetrics().getHeight(); + for (Object s : strings) { + org.eclipse.swt.graphics.Point extent = getGc().stringExtent(s.toString()); + height += extent.y; + width = Math.max(width, extent.x); + } + + // Paint a box below the text + int padding = 2; + useFillAlpha(); + getGc().fillRectangle(x - padding, y - padding, width + 2 * padding, height + 2 * padding); + + // Finally draw strings on top + useStrokeAlpha(); + int lineY = y; + for (Object s : strings) { + getGc().drawString(s.toString(), x, lineY, true /* isTransparent */); + lineY += lineHeight; + } + } + + @Override + public void drawString(@NonNull String string, @NonNull Point topLeft) { + drawString(string, topLeft.x, topLeft.y); + } + + // Styles + + @Override + public void useStyle(@NonNull DrawingStyle style) { + checkGC(); + + // Look up the specific SWT style which defines the actual + // colors and attributes to be used for the logical drawing style. + SwtDrawingStyle swtStyle = SwtDrawingStyle.of(style); + RGB stroke = swtStyle.getStrokeColor(); + if (stroke != null) { + Color color = getStrokeColor(style, stroke); + mGc.setForeground(color); + } + RGB fill = swtStyle.getFillColor(); + if (fill != null) { + Color color = getFillColor(style, fill); + mGc.setBackground(color); + } + mGc.setLineWidth(swtStyle.getLineWidth()); + mGc.setLineStyle(swtStyle.getLineStyle()); + if (swtStyle.getLineStyle() == SWT.LINE_CUSTOM) { + mGc.setLineDash(new int[] { + 8, 4 + }); + } + mCurrentStyle = swtStyle; + } + + /** Uses the stroke alpha for subsequent drawing operations. */ + private void useStrokeAlpha() { + mGc.setAlpha(mCurrentStyle.getStrokeAlpha()); + } + + /** Uses the fill alpha for subsequent drawing operations. */ + private void useFillAlpha() { + mGc.setAlpha(mCurrentStyle.getFillAlpha()); + } + + /** + * Get the SWT stroke color (foreground/border) to use for the given style, + * using the provided color description if we haven't seen this color yet. + * The color will also be placed in the {@link #mStyleStrokeMap} such that + * it can be disposed of at cleanup time. + * + * @param style The drawing style for which we want a color + * @param defaultColorDesc The RGB values to initialize the color to if we + * haven't seen this color before + * @return The color object + */ + private Color getStrokeColor(DrawingStyle style, RGB defaultColorDesc) { + return getStyleColor(style, defaultColorDesc, mStyleStrokeMap); + } + + /** + * Get the SWT fill (background/interior) color to use for the given style, + * using the provided color description if we haven't seen this color yet. + * The color will also be placed in the {@link #mStyleStrokeMap} such that + * it can be disposed of at cleanup time. + * + * @param style The drawing style for which we want a color + * @param defaultColorDesc The RGB values to initialize the color to if we + * haven't seen this color before + * @return The color object + */ + private Color getFillColor(DrawingStyle style, RGB defaultColorDesc) { + return getStyleColor(style, defaultColorDesc, mStyleFillMap); + } + + /** + * Get the SWT color to use for the given style, using the provided color + * description if we haven't seen this color yet. The color will also be + * placed in the map referenced by the map parameter such that it can be + * disposed of at cleanup time. + * + * @param style The drawing style for which we want a color + * @param defaultColorDesc The RGB values to initialize the color to if we + * haven't seen this color before + * @param map The color map to use + * @return The color object + */ + private Color getStyleColor(DrawingStyle style, RGB defaultColorDesc, + Map<DrawingStyle, Color> map) { + Color color = map.get(style); + if (color == null) { + color = new Color(getGc().getDevice(), defaultColorDesc); + map.put(style, color); + } + + return color; + } + + // dots + + @Override + public void drawPoint(int x, int y) { + checkGC(); + useStrokeAlpha(); + x = mHScale.translate(x); + y = mVScale.translate(y); + + getGc().drawPoint(x, y); + } + + // arrows + + private static final int MIN_LENGTH = 10; + + + @Override + public void drawArrow(int x1, int y1, int x2, int y2, int size) { + int arrowWidth = size; + int arrowHeight = size; + + checkGC(); + useStrokeAlpha(); + x1 = mHScale.translate(x1); + y1 = mVScale.translate(y1); + x2 = mHScale.translate(x2); + y2 = mVScale.translate(y2); + GC graphics = getGc(); + + // Make size adjustments to ensure that the arrow has enough width to be visible + if (x1 == x2 && Math.abs(y1 - y2) < MIN_LENGTH) { + int delta = (MIN_LENGTH - Math.abs(y1 - y2)) / 2; + if (y1 < y2) { + y1 -= delta; + y2 += delta; + } else { + y1 += delta; + y2-= delta; + } + + } else if (y1 == y2 && Math.abs(x1 - x2) < MIN_LENGTH) { + int delta = (MIN_LENGTH - Math.abs(x1 - x2)) / 2; + if (x1 < x2) { + x1 -= delta; + x2 += delta; + } else { + x1 += delta; + x2-= delta; + } + } + + graphics.drawLine(x1, y1, x2, y2); + + // Arrowhead: + + if (x1 == x2) { + // Vertical + if (y2 > y1) { + graphics.drawLine(x2 - arrowWidth, y2 - arrowHeight, x2, y2); + graphics.drawLine(x2 + arrowWidth, y2 - arrowHeight, x2, y2); + } else { + graphics.drawLine(x2 - arrowWidth, y2 + arrowHeight, x2, y2); + graphics.drawLine(x2 + arrowWidth, y2 + arrowHeight, x2, y2); + } + } else if (y1 == y2) { + // Horizontal + if (x2 > x1) { + graphics.drawLine(x2 - arrowHeight, y2 - arrowWidth, x2, y2); + graphics.drawLine(x2 - arrowHeight, y2 + arrowWidth, x2, y2); + } else { + graphics.drawLine(x2 + arrowHeight, y2 - arrowWidth, x2, y2); + graphics.drawLine(x2 + arrowHeight, y2 + arrowWidth, x2, y2); + } + } else { + // Compute angle: + int dy = y2 - y1; + int dx = x2 - x1; + double angle = Math.atan2(dy, dx); + double lineLength = Math.sqrt(dy * dy + dx * dx); + + // Imagine a line of the same length as the arrow, but with angle 0. + // Its two arrow lines are at (-arrowWidth, -arrowHeight) relative + // to the endpoint (x1 + lineLength, y1) stretching up to (x2,y2). + // We compute the positions of (ax,ay) for the point above and + // below this line and paint the lines to it: + double ax = x1 + lineLength - arrowHeight; + double ay = y1 - arrowWidth; + int rx = (int) (Math.cos(angle) * (ax-x1) - Math.sin(angle) * (ay-y1) + x1); + int ry = (int) (Math.sin(angle) * (ax-x1) + Math.cos(angle) * (ay-y1) + y1); + graphics.drawLine(x2, y2, rx, ry); + + ay = y1 + arrowWidth; + rx = (int) (Math.cos(angle) * (ax-x1) - Math.sin(angle) * (ay-y1) + x1); + ry = (int) (Math.sin(angle) * (ax-x1) + Math.cos(angle) * (ay-y1) + y1); + graphics.drawLine(x2, y2, rx, ry); + } + + /* TODO: Experiment with filled arrow heads? + if (x1 == x2) { + // Vertical + if (y2 > y1) { + for (int i = 0; i < arrowWidth; i++) { + graphics.drawLine(x2 - arrowWidth + i, y2 - arrowWidth + i, + x2 + arrowWidth - i, y2 - arrowWidth + i); + } + } else { + for (int i = 0; i < arrowWidth; i++) { + graphics.drawLine(x2 - arrowWidth + i, y2 + arrowWidth - i, + x2 + arrowWidth - i, y2 + arrowWidth - i); + } + } + } else if (y1 == y2) { + // Horizontal + if (x2 > x1) { + for (int i = 0; i < arrowHeight; i++) { + graphics.drawLine(x2 - arrowHeight + i, y2 - arrowHeight + i, x2 + - arrowHeight + i, y2 + arrowHeight - i); + } + } else { + for (int i = 0; i < arrowHeight; i++) { + graphics.drawLine(x2 + arrowHeight - i, y2 - arrowHeight + i, x2 + + arrowHeight - i, y2 + arrowHeight - i); + } + } + } else { + // Arbitrary angle -- need to use trig + // TODO: Implement this + } + */ + } +} |