1 /*
2 * Copyright 2010, 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 #define LOG_TAG "WebCore"
27
28 #include "config.h"
29 #include "android_graphics.h"
30 #include "Document.h"
31 #include "IntRect.h"
32 #include "Node.h"
33 #include "RenderObject.h"
34 #include "RenderSkinMediaButton.h"
35 #include "RenderSlider.h"
36 #include "SkCanvas.h"
37 #include "SkNinePatch.h"
38 #include "SkRect.h"
39 #include <androidfw/AssetManager.h>
40 #include <utils/Debug.h>
41 #include <utils/Log.h>
42 #include <wtf/text/CString.h>
43
44 extern android::AssetManager* globalAssetManager();
45
46 struct PatchData {
47 const char* name;
48 int8_t outset, margin;
49 };
50
51 static const PatchData gFiles[] =
52 {
53 { "scrubber_primary_holo.9.png", 0, 0 }, // SLIDER_TRACK, left of the SLIDER_THUMB
54 { "ic_media_pause.png", 0, 0}, // PAUSE
55 { "ic_media_play.png", 0, 0 }, // PLAY
56 { "ic_media_pause.png", 0, 0 }, // MUTE
57 { "ic_media_rew.png", 0, 0 }, // REWIND
58 { "ic_media_ff.png", 0, 0 }, // FORWARD
59 { "ic_media_fullscreen.png", 0, 0 }, // FULLSCREEN
60 { "spinner_76_outer_holo.png", 0, 0 }, // SPINNER_OUTER
61 { "spinner_76_inner_holo.png", 0, 0 }, // SPINNER_INNER
62 { "ic_media_video_poster.png", 0, 0 }, // VIDEO
63 { "btn_media_player_disabled.9.png", 0, 0 }, // BACKGROUND_SLIDER
64 { "scrubber_track_holo_dark.9.png", 0, 0 }, // SLIDER_TRACK
65 { "scrubber_control_normal_holo.png", 0, 0 } // SLIDER_THUMB
66 };
67
68 static SkBitmap gButton[sizeof(gFiles)/sizeof(gFiles[0])];
69 static bool gDecoded;
70 static bool gDecodingFailed;
71
72 namespace WebCore {
73
Decode()74 void RenderSkinMediaButton::Decode()
75 {
76 String drawableDirectory = RenderSkinAndroid::DrawableDirectory();
77
78 gDecoded = true;
79 gDecodingFailed = false;
80 android::AssetManager* am = globalAssetManager();
81 for (size_t i = 0; i < sizeof(gFiles)/sizeof(gFiles[0]); i++) {
82 String path = drawableDirectory + gFiles[i].name;
83 if (!RenderSkinAndroid::DecodeBitmap(am, path.utf8().data(), &gButton[i])) {
84 gDecodingFailed = true;
85 ALOGD("RenderSkinButton::Init: button assets failed to decode\n\tBrowser buttons will not draw");
86 break;
87 }
88 }
89 }
90
Draw(SkCanvas * canvas,const IntRect & r,int buttonType,bool translucent,RenderObject * o,bool drawBackground)91 void RenderSkinMediaButton::Draw(SkCanvas* canvas, const IntRect& r, int buttonType,
92 bool translucent, RenderObject* o, bool drawBackground)
93 {
94 if (!gDecoded) {
95 Decode();
96 }
97
98 if (!canvas)
99 return;
100
101 // If we failed to decode, do nothing. This way the browser still works,
102 // and webkit will still draw the label and layout space for us.
103 if (gDecodingFailed)
104 return;
105
106 bool drawsNinePatch = false;
107 bool drawsImage = true;
108
109 int ninePatchIndex = 0;
110 int imageIndex = 0;
111
112 SkRect bounds(r);
113 SkScalar imageMargin = 8;
114 SkPaint paint;
115
116 int alpha = 255;
117 if (translucent)
118 alpha = 190;
119
120 SkColor backgroundColor = SkColorSetARGB(alpha, 34, 34, 34);
121 SkColor trackBackgroundColor = SkColorSetARGB(255, 100, 100, 100);
122 paint.setColor(backgroundColor);
123 paint.setFlags(SkPaint::kFilterBitmap_Flag);
124
125 switch (buttonType) {
126 case PAUSE:
127 case PLAY:
128 case MUTE:
129 case REWIND:
130 case FORWARD:
131 case FULLSCREEN:
132 {
133 imageIndex = buttonType + 1;
134 paint.setColor(backgroundColor);
135 break;
136 }
137 case SPINNER_OUTER:
138 case SPINNER_INNER:
139 case VIDEO:
140 {
141 imageIndex = buttonType + 1;
142 break;
143 }
144 case BACKGROUND_SLIDER:
145 {
146 drawsImage = false;
147 break;
148 }
149 case SLIDER_TRACK:
150 {
151 drawsNinePatch = true;
152 drawsImage = false;
153 ninePatchIndex = buttonType + 1;
154 break;
155 }
156 case SLIDER_THUMB:
157 {
158 imageMargin = 0;
159 imageIndex = buttonType + 1;
160 break;
161 }
162 default:
163 return;
164 }
165
166 if (drawBackground) {
167 canvas->drawRect(r, paint);
168 }
169
170 if (drawsNinePatch) {
171 const PatchData& pd = gFiles[ninePatchIndex];
172 int marginValue = pd.margin + pd.outset;
173
174 SkIRect margin;
175 margin.set(marginValue, marginValue, marginValue, marginValue);
176 if (buttonType == SLIDER_TRACK) {
177 // Cut the height in half (with some extra slop determined by trial
178 // and error to get the placement just right.
179 SkScalar quarterHeight = SkScalarHalf(SkScalarHalf(bounds.height()));
180 bounds.fTop += quarterHeight + SkScalarHalf(3);
181 bounds.fBottom += -quarterHeight + SK_ScalarHalf;
182 if (o && o->isSlider()) {
183 RenderSlider* slider = toRenderSlider(o);
184 IntRect thumb = slider->thumbRect();
185 // Inset the track by half the width of the thumb, so the track
186 // does not appear to go beyond the space where the thumb can
187 // be.
188 SkScalar thumbHalfWidth = SkIntToScalar(thumb.width()/2);
189 bounds.fLeft += thumbHalfWidth;
190 bounds.fRight -= thumbHalfWidth;
191 if (thumb.x() > 0) {
192 // The video is past the starting point. Show the area to
193 // left of the thumb as having been played.
194 SkScalar alreadyPlayed = SkIntToScalar(thumb.center().x() + r.x());
195 SkRect playedRect(bounds);
196 playedRect.fRight = alreadyPlayed;
197 SkNinePatch::DrawNine(canvas, playedRect, gButton[0], margin);
198 bounds.fLeft = alreadyPlayed;
199 }
200
201 }
202 }
203 SkNinePatch::DrawNine(canvas, bounds, gButton[ninePatchIndex], margin);
204 }
205
206 if (drawsImage) {
207 SkScalar SIZE = gButton[imageIndex].width();
208 SkScalar width = r.width();
209 SkScalar scale = SkScalarDiv(width - 2*imageMargin, SIZE);
210 int saveScaleCount = canvas->save();
211 canvas->translate(bounds.fLeft + imageMargin, bounds.fTop + imageMargin);
212 canvas->scale(scale, scale);
213 canvas->drawBitmap(gButton[imageIndex], 0, 0, &paint);
214 canvas->restoreToCount(saveScaleCount);
215 }
216 }
217
218 } // WebCore
219