/* * Copyright 2006, The Android Open Source Project * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" #include "RenderSkinCombo.h" #include "Document.h" #include "Element.h" #include "Node.h" #include "NodeRenderStyle.h" #include "RenderStyle.h" #include "SkCanvas.h" #include "SkNinePatch.h" #include #include extern android::AssetManager* globalAssetManager(); namespace WebCore { // Indicates if the entire asset is being drawn, or if the border is being // excluded and just the arrow drawn. enum BorderStyle { FullAsset, NoBorder, BorderStyleCount // Keep at the end. }; // There are 2.5 different concepts of a 'border' here, which results // in rather a lot of magic constants. // Firstly, we have the extra padding that webkit needs to know about, // which defines how much bigger this element is made by the // asset. This is actually a bit broader than the actual border on the // asset, to make things look less cramped. The border is the same // width on all sides, except on the right when it's significantly // wider to allow for the arrow. const int RenderSkinCombo::arrowMargin[ResolutionCount] = { 16, // Medium resolution 25, // High resolution 34 // Extra high resolution }; const int RenderSkinCombo::padMargin[ResolutionCount] = { 1, // Medium resolution 1, // High resolution 1 // Extra high resolution }; namespace { // Then we have the borders used for the 9-patch stretch. The // rectangle at the centre of these borders is entirely below and to // the left of the arrow in the asset. Hence the border widths are the // same for the bottom and left, but are different for the top. The // right hand border width happens to be the same as arrowMargin // defined above. const int stretchMargin[RenderSkinAndroid::ResolutionCount] = { // border width for the bottom and left of the 9-patch 2, // Medium resolution 2, // High resolution 3 // Extra high resolution }; const int stretchTop[RenderSkinAndroid::ResolutionCount] = { // border width for the top of the 9-patch 16, // Medium resolution 23, // High resolution 32 // Extra high resolution }; // Finally, if the border is defined by the CSS, we only draw the // arrow and not the border. We do this by drawing the relevant subset // of the bitmap, which must now be precisely determined by what's in // the asset with no extra padding to make things look properly // spaced. The border to remove at the top, right and bottom of the // image is the same as stretchMargin above, but we need to know the width // of the arrow. const int arrowWidth[RenderSkinAndroid::ResolutionCount] = { 18, // Medium resolution 27, // High resolution 36 // Extra high resolution }; // scale factors for various resolutions const float scaleFactor[RenderSkinAndroid::ResolutionCount] = { 1.0f, // medium res 1.5f, // high res 2.0f // extra high res }; // Store the calculated 9 patch margins for each border style. SkIRect margin[BorderStyleCount]; SkBitmap bitmaps[2][BorderStyleCount]; // Collection of assets for a combo box - 2 states (enabled/disabled) bool isDecodingAttempted = false; // True if we've tried to decode the assets bool isDecoded = false; // True if all assets were decoded } // namespace int RenderSkinCombo::minHeight() { return SkScalarRound(stretchTop[RenderSkinAndroid::DrawableResolution()] / scaleFactor[RenderSkinAndroid::DrawableResolution()]); } void RenderSkinCombo::Decode() { if (isDecodingAttempted) return; isDecodingAttempted = true; isDecoded = false; android::AssetManager* am = globalAssetManager(); String drawableDirectory = RenderSkinAndroid::DrawableDirectory(); Resolution res = RenderSkinAndroid::DrawableResolution(); isDecoded = RenderSkinAndroid::DecodeBitmap(am, (drawableDirectory + "combobox_nohighlight.png").utf8().data(), &bitmaps[kNormal][FullAsset]); isDecoded &= RenderSkinAndroid::DecodeBitmap(am, (drawableDirectory + "combobox_disabled.png").utf8().data(), &bitmaps[kDisabled][FullAsset]); int width = bitmaps[kNormal][FullAsset].width(); int height = bitmaps[kNormal][FullAsset].height(); SkIRect subset; subset.set(width - arrowWidth[res], 0, width, height); bitmaps[kNormal][FullAsset].extractSubset(&bitmaps[kNormal][NoBorder], subset); bitmaps[kDisabled][FullAsset].extractSubset(&bitmaps[kDisabled][NoBorder], subset); // Calculate 9 patch margins. SkIRect fullAssetMargin; fullAssetMargin.fLeft = stretchMargin[res]; fullAssetMargin.fTop = stretchMargin[res]; fullAssetMargin.fRight = arrowMargin[res] + stretchMargin[res]; fullAssetMargin.fBottom = stretchTop[res]; SkIRect noBorderMargin; noBorderMargin.fLeft = 0; noBorderMargin.fTop = stretchTop[res]; noBorderMargin.fRight = 0; noBorderMargin.fBottom = stretchMargin[res]; margin[FullAsset] = fullAssetMargin; margin[NoBorder] = noBorderMargin; } bool RenderSkinCombo::Draw(SkCanvas* canvas, Node* element, int x, int y, int width, int height) { if (!isDecodingAttempted) Decode(); if (!isDecoded) return true; int resolution = RenderSkinAndroid::DrawableResolution(); State state = (element->isElementNode() && static_cast(element)->isEnabledFormControl()) ? kNormal : kDisabled; height = std::max(height, (stretchMargin[resolution] * 2)); SkRect bounds; BorderStyle drawBorder = FullAsset; bounds.set(SkIntToScalar(x+1), SkIntToScalar(y+1), SkIntToScalar(x + width-1), SkIntToScalar(y + height-1)); RenderStyle* style = element->renderStyle(); SkPaint paint; paint.setColor(style->visitedDependentColor(CSSPropertyBackgroundColor).rgb()); canvas->drawRect(bounds, paint); bounds.set(SkIntToScalar(x), SkIntToScalar(y), SkIntToScalar(x + width), SkIntToScalar(y + height)); // If this is an appearance where RenderTheme::paint returns true // without doing anything, this means that // RenderBox::PaintBoxDecorationWithSize will end up painting the // border, so we shouldn't paint a border here. if (style->appearance() == MenulistButtonPart || style->appearance() == ListboxPart || style->appearance() == TextFieldPart || style->appearance() == TextAreaPart) { bounds.fLeft += SkIntToScalar(width - RenderSkinCombo::extraWidth()); bounds.fRight -= SkIntToScalar(style->borderRightWidth()); bounds.fTop += SkIntToScalar(style->borderTopWidth()); bounds.fBottom -= SkIntToScalar(style->borderBottomWidth()); drawBorder = NoBorder; } float scale = scaleFactor[resolution]; bounds.fLeft = bounds.fLeft * scale; bounds.fRight = bounds.fRight * scale; bounds.fTop = bounds.fTop * scale; bounds.fBottom = bounds.fBottom * scale; int count = canvas->save(); canvas->scale(1.0f / scale, 1.0f / scale); SkNinePatch::DrawNine(canvas, bounds, bitmaps[state][drawBorder], margin[drawBorder]); canvas->restoreToCount(count); return false; } } // namspace WebCore