1 /*
2  * Copyright (C) 2013 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.ide.eclipse.adt.internal.editors.draw9patch.ui;
18 
19 import com.android.ide.eclipse.adt.AdtPlugin;
20 import com.android.ide.eclipse.adt.internal.editors.draw9patch.graphics.GraphicsUtilities;
21 import com.android.ide.eclipse.adt.internal.editors.draw9patch.graphics.NinePatchedImage;
22 import com.android.ide.eclipse.adt.internal.editors.draw9patch.graphics.NinePatchedImage.Projection;
23 
24 import org.eclipse.swt.SWT;
25 import org.eclipse.swt.events.PaintEvent;
26 import org.eclipse.swt.events.PaintListener;
27 import org.eclipse.swt.graphics.GC;
28 import org.eclipse.swt.graphics.Image;
29 import org.eclipse.swt.graphics.ImageData;
30 import org.eclipse.swt.graphics.PaletteData;
31 import org.eclipse.swt.graphics.Point;
32 import org.eclipse.swt.graphics.RGB;
33 import org.eclipse.swt.graphics.Rectangle;
34 import org.eclipse.swt.layout.FillLayout;
35 import org.eclipse.swt.widgets.Canvas;
36 import org.eclipse.swt.widgets.Composite;
37 
38 /**
39  * Preview 9-patched image pane.
40  */
41 public class StretchesViewer extends Composite {
42     private static final boolean DEBUG = false;
43 
44     private static final int PADDING_COLOR = 0x0000CC;
45 
46     private static final int PADDING_COLOR_ALPHA = 100;
47 
48     private static final PaletteData PADDING_PALLET
49             = new PaletteData(new RGB[] {new RGB(0x00, 0x00, 0xCC)});
50 
51     private static final String CHECKER_PNG_PATH = "/icons/checker.png";
52 
53     private Image mBackgroundLayer = null;
54 
55     private final StretchView mHorizontal;
56     private final StretchView mVertical;
57 
58     private final StretchView mBoth;
59 
60     private NinePatchedImage mNinePatchedImage = null;
61 
62     private ImageData mContentAreaImageData = null;
63 
64     private boolean mIsContentAreaShown = false;
65 
66     private Image mContentAreaImage = null;
67 
68     private int mScale = 2;
69 
StretchesViewer(Composite parent, int style)70     public StretchesViewer(Composite parent, int style) {
71         super(parent, style);
72 
73         mBackgroundLayer = AdtPlugin.getImageDescriptor(CHECKER_PNG_PATH).createImage();
74 
75         setLayout(new FillLayout(SWT.VERTICAL));
76 
77         mHorizontal = new StretchView(this, SWT.NULL);
78         mVertical = new StretchView(this, SWT.NULL);
79         mBoth = new StretchView(this, SWT.NULL);
80     }
81 
82     /**
83      * Set show/not show content area.
84      * @param If show, true
85      */
setShowContentArea(boolean show)86     public void setShowContentArea(boolean show) {
87         mIsContentAreaShown = show;
88         redraw();
89     }
90 
equalSize(ImageData image1, ImageData image2)91     private static final boolean equalSize(ImageData image1, ImageData image2) {
92         return (image1.width == image2.width && image1.height == image2.height);
93     }
94 
95     /**
96      * Update preview image.
97      */
updatePreview(NinePatchedImage image)98     public void updatePreview(NinePatchedImage image) {
99         mNinePatchedImage = image;
100         ImageData base = mNinePatchedImage.getImageData();
101 
102         if (mContentAreaImageData == null
103                 || (mContentAreaImageData != null && !equalSize(base, mContentAreaImageData))) {
104             mContentAreaImageData = new ImageData(
105                     base.width,
106                     base.height,
107                     1,
108                     PADDING_PALLET);
109         } else {
110             GraphicsUtilities.clearImageData(mContentAreaImageData);
111         }
112 
113         mHorizontal.setImage(mNinePatchedImage);
114         mVertical.setImage(mNinePatchedImage);
115         mBoth.setImage(mNinePatchedImage);
116 
117         mContentAreaImage = buildContentAreaPreview();
118 
119         setScale(mScale);
120     }
121 
buildContentAreaPreview()122     private Image buildContentAreaPreview() {
123         if (mContentAreaImage != null) {
124             mContentAreaImage.dispose();
125         }
126 
127         Rectangle rect = mNinePatchedImage.getContentArea();
128 
129         int yLen = rect.y + rect.height;
130         for (int y = rect.y; y < yLen; y++) {
131             int xLen = rect.x + rect.width;
132             for (int x = rect.x; x < xLen; x++) {
133                 mContentAreaImageData.setPixel(x, y, PADDING_COLOR);
134                 mContentAreaImageData.setAlpha(x, y, PADDING_COLOR_ALPHA);
135             }
136         }
137         return new Image(AdtPlugin.getDisplay(), mContentAreaImageData);
138     }
139 
setScale(int scale)140     public void setScale(int scale) {
141         if (DEBUG) {
142             System.out.println("scale = " + scale);
143         }
144 
145         mScale = scale;
146         int imageWidth = mNinePatchedImage.getWidth();
147         int imageHeight = mNinePatchedImage.getHeight();
148 
149         mHorizontal.setSize(imageWidth * scale, imageHeight);
150         mVertical.setSize(imageWidth, imageHeight * scale);
151         mBoth.setSize(imageWidth * scale, imageHeight * scale);
152 
153         redraw();
154     }
155 
156     @Override
dispose()157     public void dispose() {
158         mBackgroundLayer.dispose();
159         super.dispose();
160     }
161 
162     private class StretchView extends Canvas implements PaintListener {
163 
164         private final Point mSize = new Point(0, 0);
165         private final Rectangle mPadding = new Rectangle(0, 0, 0, 0);
166         private Projection[][] mProjection = null;
167 
StretchView(Composite parent, int style)168         public StretchView(Composite parent, int style) {
169             super(parent, style);
170             addPaintListener(this);
171         }
172 
setImage(NinePatchedImage image)173         private void setImage(NinePatchedImage image) {
174             setSize(image.getWidth(), image.getHeight());
175         }
176 
177         @Override
setSize(int width, int heigh)178         public void setSize(int width, int heigh) {
179             mSize.x = width;
180             mSize.y = heigh;
181             mProjection = mNinePatchedImage.getProjections(mSize.x, mSize.y, mProjection);
182         }
183 
calcPaddings(int width, int height)184         private synchronized void calcPaddings(int width, int height) {
185             Point canvasSize = getSize();
186 
187             mPadding.x = (canvasSize.x - width) / 2;
188             mPadding.y = (canvasSize.y - height) / 2;
189 
190             mPadding.width = width;
191             mPadding.height = height;
192         }
193 
194         @Override
paintControl(PaintEvent pe)195         public void paintControl(PaintEvent pe) {
196             if (mNinePatchedImage == null || mProjection == null) {
197                 return;
198             }
199 
200             Point size = getSize();
201 
202             // relative scaling
203             float ratio = 1.0f;
204             float wRatio = ((float) size.x / mSize.x);
205             ratio = Math.min(wRatio, ratio);
206             float hRatio = ((float) size.y / mSize.y);
207             ratio = Math.min(hRatio, ratio);
208 
209             int width = Math.round(mSize.x * ratio);
210             int height = Math.round(mSize.y * ratio);
211 
212             calcPaddings(width, height);
213 
214             Rectangle dest = new Rectangle(0, 0, 0, 0);
215 
216             GC gc = pe.gc;
217 
218             int backgroundLayerWidth = mBackgroundLayer.getImageData().width;
219             int backgroundLayerHeight = mBackgroundLayer.getImageData().height;
220 
221             int yCount = size.y / backgroundLayerHeight
222                     + ((size.y % backgroundLayerHeight) > 0 ? 1 : 0);
223             int xCount = size.x / backgroundLayerWidth
224                     + ((size.x % backgroundLayerWidth) > 0 ? 1 : 0);
225 
226             // draw background layer
227             for (int y = 0; y < yCount; y++) {
228                 for (int x = 0; x < xCount; x++) {
229                     gc.drawImage(mBackgroundLayer,
230                             x * backgroundLayerWidth,
231                             y * backgroundLayerHeight);
232                 }
233             }
234 
235             // draw the border line
236             gc.setAlpha(0x88);
237             gc.drawRectangle(0, 0, size.x, size.y);
238             gc.setAlpha(0xFF);
239 
240             int yLen = mProjection.length;
241             int xLen = mProjection[0].length;
242             for (int yPos = 0; yPos < yLen; yPos++) {
243                 for (int xPos = 0; xPos < xLen; xPos++) {
244                     Projection p = mProjection[yPos][xPos];
245 
246                     // consider the scale
247                     dest.x = (int) Math.ceil(p.dest.x * ratio);
248                     dest.y = (int) Math.ceil(p.dest.y * ratio);
249                     dest.width = (int) Math.ceil(p.dest.width * ratio);
250                     dest.height = (int) Math.ceil(p.dest.height * ratio);
251 
252                     gc.drawImage(mNinePatchedImage.getImage(), p.src.x, p.src.y,
253                             p.src.width, p.src.height,
254                             (mPadding.x + dest.x), (mPadding.y + dest.y),
255                             dest.width, dest.height);
256 
257                     if (mIsContentAreaShown) {
258                         gc.drawImage(mContentAreaImage, p.src.x, p.src.y,
259                                 p.src.width, p.src.height,
260                                 (mPadding.x + dest.x), (mPadding.y + dest.y),
261                                 dest.width, dest.height);
262                     }
263                 }
264             }
265         }
266     }
267 }
268