• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2006, The Android Open Source Project
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *  * Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  *  * Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25 
26 #include "config.h"
27 #include "RenderSkinCombo.h"
28 
29 #include "Document.h"
30 #include "Element.h"
31 #include "Node.h"
32 #include "NodeRenderStyle.h"
33 #include "RenderStyle.h"
34 #include "SkCanvas.h"
35 #include "SkNinePatch.h"
36 #include <utils/AssetManager.h>
37 #include <wtf/text/CString.h>
38 
39 extern android::AssetManager* globalAssetManager();
40 
41 namespace WebCore {
42 
43 // Indicates if the entire asset is being drawn, or if the border is being
44 // excluded and just the arrow drawn.
45 enum BorderStyle {
46     FullAsset,
47     NoBorder,
48     BorderStyleCount // Keep at the end.
49 };
50 
51 // There are 2.5 different concepts of a 'border' here, which results
52 // in rather a lot of magic constants.
53 
54 // Firstly, we have the extra padding that webkit needs to know about,
55 // which defines how much bigger this element is made by the
56 // asset. This is actually a bit broader than the actual border on the
57 // asset, to make things look less cramped. The border is the same
58 // width on all sides, except on the right when it's significantly
59 // wider to allow for the arrow.
60 const int RenderSkinCombo::arrowMargin[ResolutionCount] = {
61     22, // Medium resolution
62     34, // High resolution
63     46 // Extra high resolution
64 };
65 const int RenderSkinCombo::padMargin[ResolutionCount] = {
66     2, // Medium resolution
67     5, // High resolution
68     6 // Extra high resolution
69 };
70 
71 namespace {
72 // Then we have the borders used for the 9-patch stretch. The
73 // rectangle at the centre of these borders is entirely below and to
74 // the left of the arrow in the asset. Hence the border widths are the
75 // same for the bottom and left, but are different for the top. The
76 // right hand border width happens to be the same as arrowMargin
77 // defined above.
78 const int stretchMargin[RenderSkinAndroid::ResolutionCount] = { // border width for the bottom and left of the 9-patch
79     3, // Medium resolution
80     5, // High resolution
81     6 // Extra high resolution
82 
83 };
84 const int stretchTop[RenderSkinAndroid::ResolutionCount] = { // border width for the top of the 9-patch
85     15, // Medium resolution
86     23, // High resolution
87     34 // Extra high resolution
88 };
89 
90 // Finally, if the border is defined by the CSS, we only draw the
91 // arrow and not the border. We do this by drawing the relevant subset
92 // of the bitmap, which must now be precisely determined by what's in
93 // the asset with no extra padding to make things look properly
94 // spaced. The border to remove at the top, right and bottom of the
95 // image is the same as stretchMargin above, but we need to know the width
96 // of the arrow.
97 const int arrowWidth[RenderSkinAndroid::ResolutionCount] = {
98     22, // Medium resolution
99     31, // High resolution
100     42 // Extra high resolution
101 };
102 
103 // Store the calculated 9 patch margins for each border style.
104 SkIRect margin[BorderStyleCount];
105 
106 SkBitmap bitmaps[2][BorderStyleCount]; // Collection of assets for a combo box - 2 states (enabled/disabled)
107 bool isDecodingAttempted = false; // True if we've tried to decode the assets
108 bool isDecoded = false;           // True if all assets were decoded
109 
110 } // namespace
111 
Decode()112 void RenderSkinCombo::Decode()
113 {
114     if (isDecodingAttempted)
115         return;
116 
117     isDecodingAttempted = true;
118     isDecoded = false;
119 
120     android::AssetManager* am = globalAssetManager();
121 
122     String drawableDirectory = RenderSkinAndroid::DrawableDirectory();
123     Resolution res = RenderSkinAndroid::DrawableResolution();
124 
125     isDecoded = RenderSkinAndroid::DecodeBitmap(am, (drawableDirectory + "combobox_nohighlight.png").utf8().data(), &bitmaps[kNormal][FullAsset]);
126     isDecoded &= RenderSkinAndroid::DecodeBitmap(am, (drawableDirectory + "combobox_disabled.png").utf8().data(), &bitmaps[kDisabled][FullAsset]);
127 
128     int width = bitmaps[kNormal][FullAsset].width();
129     int height = bitmaps[kNormal][FullAsset].height();
130     SkIRect subset;
131     subset.set(width - arrowWidth[res], 0, width, height);
132     bitmaps[kNormal][FullAsset].extractSubset(&bitmaps[kNormal][NoBorder], subset);
133     bitmaps[kDisabled][FullAsset].extractSubset(&bitmaps[kDisabled][NoBorder], subset);
134 
135     // Calculate 9 patch margins.
136     SkIRect fullAssetMargin;
137     fullAssetMargin.fLeft = stretchMargin[res];
138     fullAssetMargin.fTop = stretchTop[res];
139     fullAssetMargin.fRight = arrowMargin[res] + stretchMargin[res];
140     fullAssetMargin.fBottom = stretchMargin[res];
141 
142     SkIRect noBorderMargin;
143     noBorderMargin.fLeft = 0;
144     noBorderMargin.fTop = stretchTop[res];
145     noBorderMargin.fRight = 0;
146     noBorderMargin.fBottom = stretchMargin[res];
147 
148     margin[FullAsset] = fullAssetMargin;
149     margin[NoBorder] = noBorderMargin;
150 }
151 
Draw(SkCanvas * canvas,Node * element,int x,int y,int width,int height)152 bool RenderSkinCombo::Draw(SkCanvas* canvas, Node* element, int x, int y, int width, int height)
153 {
154     if (!isDecodingAttempted)
155         Decode();
156 
157     if (!isDecoded)
158         return true;
159 
160     State state = (element->isElementNode() && static_cast<Element*>(element)->isEnabledFormControl()) ? kNormal : kDisabled;
161     height = std::max(height, (stretchMargin[RenderSkinAndroid::DrawableResolution()]<<1) + 1);
162 
163     SkRect bounds;
164     BorderStyle drawBorder = FullAsset;
165 
166     bounds.set(SkIntToScalar(x+1), SkIntToScalar(y+1), SkIntToScalar(x + width-1), SkIntToScalar(y + height-1));
167     RenderStyle* style = element->renderStyle();
168     SkPaint paint;
169     paint.setColor(style->visitedDependentColor(CSSPropertyBackgroundColor).rgb());
170     canvas->drawRect(bounds, paint);
171 
172     bounds.set(SkIntToScalar(x), SkIntToScalar(y), SkIntToScalar(x + width), SkIntToScalar(y + height));
173 
174     // If this is an appearance where RenderTheme::paint returns true
175     // without doing anything, this means that
176     // RenderBox::PaintBoxDecorationWithSize will end up painting the
177     // border, so we shouldn't paint a border here.
178     if (style->appearance() == MenulistButtonPart ||
179         style->appearance() == ListboxPart ||
180         style->appearance() == TextFieldPart ||
181         style->appearance() == TextAreaPart) {
182         bounds.fLeft += SkIntToScalar(width - RenderSkinCombo::extraWidth());
183         bounds.fRight -= SkIntToScalar(style->borderRightWidth());
184         bounds.fTop += SkIntToScalar(style->borderTopWidth());
185         bounds.fBottom -= SkIntToScalar(style->borderBottomWidth());
186         drawBorder = NoBorder;
187     }
188     SkNinePatch::DrawNine(canvas, bounds, bitmaps[state][drawBorder], margin[drawBorder]);
189     return false;
190 }
191 
192 }   // namspace WebCore
193