diff options
Diffstat (limited to 'WebCore/platform/graphics/android')
20 files changed, 3585 insertions, 0 deletions
diff --git a/WebCore/platform/graphics/android/AffineTransformAndroid.cpp b/WebCore/platform/graphics/android/AffineTransformAndroid.cpp new file mode 100644 index 0000000..d00aa40 --- /dev/null +++ b/WebCore/platform/graphics/android/AffineTransformAndroid.cpp @@ -0,0 +1,168 @@ +/* +** +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0 +** +** 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. +*/ + +#include "config.h" +#include "AffineTransform.h" + +#include "FloatRect.h" +#include "IntRect.h" + +#include "android_graphics.h" + +namespace WebCore { + +static const double deg2rad = 0.017453292519943295769; // pi/180 + +AffineTransform::AffineTransform() +{ + m_transform.reset(); +} + +AffineTransform::AffineTransform(double a, double b, double c, double d, double tx, double ty) +{ + m_transform.reset(); + + m_transform.set(SkMatrix::kMScaleX, SkDoubleToScalar(a)); + m_transform.set(SkMatrix::kMSkewX, SkDoubleToScalar(b)); + m_transform.set(SkMatrix::kMTransX, SkDoubleToScalar(tx)); + + m_transform.set(SkMatrix::kMScaleY, SkDoubleToScalar(d)); + m_transform.set(SkMatrix::kMSkewX, SkDoubleToScalar(c)); + m_transform.set(SkMatrix::kMTransX, SkDoubleToScalar(ty)); +} + +void AffineTransform::setMatrix(double a, double b, double c, double d, double tx, double ty) +{ + m_transform.set(SkMatrix::kMScaleX, SkDoubleToScalar(a)); + m_transform.set(SkMatrix::kMSkewX, SkDoubleToScalar(b)); + m_transform.set(SkMatrix::kMTransX, SkDoubleToScalar(tx)); + + m_transform.set(SkMatrix::kMScaleY, SkDoubleToScalar(d)); + m_transform.set(SkMatrix::kMSkewX, SkDoubleToScalar(c)); + m_transform.set(SkMatrix::kMTransX, SkDoubleToScalar(ty)); +} + +void AffineTransform::map(double x, double y, double *x2, double *y2) const +{ + SkPoint src, dst; + src.set(SkDoubleToScalar(x), SkDoubleToScalar(y)); + m_transform.mapPoints(&dst, &src, 1); + + *x2 = SkScalarToDouble(dst.fX); + *y2 = SkScalarToDouble(dst.fY); +} + +IntRect AffineTransform::mapRect(const IntRect &rect) const +{ + SkRect src, dst; + SkIRect ir; + + android_setrect(&src, rect); + m_transform.mapRect(&dst, src); + dst.round(&ir); + + return IntRect(ir.fLeft, ir.fTop, ir.width(), ir.height()); +} + +FloatRect AffineTransform::mapRect(const FloatRect &rect) const +{ + SkRect src, dst; + SkIRect ir; + + android_setrect(&src, rect); + m_transform.mapRect(&dst, src); + dst.round(&ir); + + return IntRect(ir.fLeft, ir.fTop, ir.width(), ir.height()); +} + +bool AffineTransform::isIdentity() const +{ + return m_transform.isIdentity(); +} + +void AffineTransform::reset() +{ + m_transform.reset(); +} + +AffineTransform &AffineTransform::scale(double sx, double sy) +{ + m_transform.preScale(SkDoubleToScalar(sx), SkDoubleToScalar(sy)); + return *this; +} + +AffineTransform &AffineTransform::rotate(double d) +{ + m_transform.preRotate(SkDoubleToScalar(d)); + return *this; +} + +AffineTransform &AffineTransform::translate(double tx, double ty) +{ + m_transform.preTranslate(SkDoubleToScalar(tx), SkDoubleToScalar(ty)); + return *this; +} + +AffineTransform &AffineTransform::shear(double sx, double sy) +{ + m_transform.preSkew(SkDoubleToScalar(sx), SkDoubleToScalar(sy)); + return *this; +} + +double AffineTransform::det() const +{ + return SkScalarToDouble(m_transform[SkMatrix::kMScaleX]) * SkScalarToDouble(m_transform[SkMatrix::kMScaleY]) - + SkScalarToDouble(m_transform[SkMatrix::kMSkewX]) * SkScalarToDouble(m_transform[SkMatrix::kMSkewY]); +} + +AffineTransform AffineTransform::inverse() const +{ + AffineTransform inverse; + + m_transform.invert(&inverse.m_transform); + + return inverse; +} + +AffineTransform::operator SkMatrix() const +{ + return m_transform; +} + +bool AffineTransform::operator==(const AffineTransform &m2) const +{ + return m_transform == m2.m_transform; +} + +AffineTransform &AffineTransform::operator*= (const AffineTransform &m2) +{ + // is this the correct order??? + m_transform.setConcat(m_transform, m2.m_transform); + return *this; +} + +AffineTransform AffineTransform::operator* (const AffineTransform &m2) +{ + AffineTransform cat; + + // is this the correct order??? + cat.m_transform.setConcat(m_transform, m2.m_transform); + return cat; +} + +} diff --git a/WebCore/platform/graphics/android/FontAndroid.cpp b/WebCore/platform/graphics/android/FontAndroid.cpp new file mode 100644 index 0000000..340d1c1 --- /dev/null +++ b/WebCore/platform/graphics/android/FontAndroid.cpp @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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 "Font.h" + +#include "FontData.h" +#include "FontFallbackList.h" +#include "GraphicsContext.h" +#include "GlyphBuffer.h" +#include "PlatformGraphicsContext.h" +#include "IntRect.h" + +#include "SkCanvas.h" +#include "SkPaint.h" +#include "SkTemplates.h" +#include "SkTypeface.h" +#include "SkUtils.h" + +namespace WebCore { + +void Font::drawGlyphs(GraphicsContext* gc, const SimpleFontData* font, + const GlyphBuffer& glyphBuffer, int from, int numGlyphs, + const FloatPoint& point) const { + SkCanvas* canvas = gc->platformContext()->mCanvas; + SkPaint paint; + + font->platformData().setupPaint(&paint); + paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); + paint.setColor(gc->fillColor().rgb()); + + SkASSERT(sizeof(GlyphBufferGlyph) == sizeof(uint16_t)); // compile-time assert + + const GlyphBufferGlyph* glyphs = glyphBuffer.glyphs(from); + SkScalar x = SkFloatToScalar(point.x()); + SkScalar y = SkFloatToScalar(point.y()); + + if (glyphBuffer.hasAdjustedWidths()) { + const GlyphBufferAdvance* adv = glyphBuffer.advances(from); + SkAutoSTMalloc<32, SkPoint> storage(numGlyphs); + SkPoint* pos = storage.get(); + + for (int i = 0; i < numGlyphs; i++) { + pos[i].set(x, y); + x += SkFloatToScalar(adv[i].width()); + y += SkFloatToScalar(adv[i].height()); + } + canvas->drawPosText(glyphs, numGlyphs << 1, pos, paint); + } else { + canvas->drawText(glyphs, numGlyphs << 1, x, y, paint); + } +} + +FloatRect Font::selectionRectForComplexText(const TextRun& run, const IntPoint& point, int h, int, int) const +{ + SkPaint paint; + SkScalar width, left; + SkPaint::FontMetrics metrics; + + primaryFont()->platformData().setupPaint(&paint); + + width = paint.measureText(run.characters(), run.length() << 1); + SkScalar spacing = paint.getFontMetrics(&metrics); + + return FloatRect(point.x(), + point.y() - floorf(SkScalarToFloat(-metrics.fAscent)), + roundf(SkScalarToFloat(width)), + roundf(SkScalarToFloat(spacing))); +} + +void Font::drawComplexText(GraphicsContext* gc, TextRun const& run, FloatPoint const& point, int, int) const +{ + SkCanvas* canvas = gc->platformContext()->mCanvas; + SkPaint paint; + + primaryFont()->platformData().setupPaint(&paint); + paint.setColor(gc->fillColor().rgb()); + +#if 0 + int n = run.to() - run.from(); +printf("------------- complex draw %d chars", n); + for (int i = 0; i < n; i++) + printf(" %04X", run.data(run.from())[i]); + printf("\n"); +#endif + + canvas->drawText(run.characters(), run.length() << 1, + SkFloatToScalar(point.x()), SkFloatToScalar(point.y()), + paint); +} + +float Font::floatWidthForComplexText(const TextRun& run) const +{ + SkPaint paint; + + primaryFont()->platformData().setupPaint(&paint); + +//printf("--------- complext measure %d chars\n", run.to() - run.from()); + + SkScalar width = paint.measureText(run.characters(), run.length() << 1); + return SkScalarToFloat(width); +} + +int Font::offsetForPositionForComplexText(const TextRun& run, int x, bool includePartialGlyphs) const +{ + SkPaint paint; + int count = run.length(); + SkAutoSTMalloc<64, SkScalar> storage(count); + SkScalar* widths = storage.get(); + + primaryFont()->platformData().setupPaint(&paint); + + count = paint.getTextWidths(run.characters(), count << 1, widths); + + if (count > 0) + { + SkScalar pos = 0; + for (int i = 0; i < count; i++) + { + if (x < SkScalarRound(pos + SkScalarHalf(widths[i]))) + return i; + pos += widths[i]; + } + } + return count; +} + +} diff --git a/WebCore/platform/graphics/android/FontCacheAndroid.cpp b/WebCore/platform/graphics/android/FontCacheAndroid.cpp new file mode 100644 index 0000000..903159e --- /dev/null +++ b/WebCore/platform/graphics/android/FontCacheAndroid.cpp @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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 "FontCache.h" +#include "FontPlatformData.h" +#include "Font.h" + +#include "SkPaint.h" +#include "SkTypeface.h" +#include "SkUtils.h" + +namespace WebCore { + +void FontCache::platformInit() +{ +} + +const SimpleFontData* FontCache::getFontDataForCharacters(const Font& font, const UChar* characters, int length) +{ + return font.primaryFont(); // do I need to make a copy (i.e. does the caller delete what I return? + +#if 0 + // IMLangFontLink::MapFont Method does what we want. + IMLangFontLink2* langFontLink = getFontLinkInterface(); + if (!langFontLink) + return 0; + + FontData* fontData = 0; + HDC hdc = GetDC(0); + DWORD fontCodePages; + langFontLink->GetFontCodePages(hdc, font.primaryFont()->m_font.hfont(), &fontCodePages); + + DWORD actualCodePages; + long cchActual; + langFontLink->GetStrCodePages(characters, length, fontCodePages, &actualCodePages, &cchActual); + if (cchActual) { + HFONT result; + if (langFontLink->MapFont(hdc, actualCodePages, characters[0], &result) == S_OK) { + fontData = new FontData(FontPlatformData(result, font.fontDescription().computedPixelSize())); + fontData->setIsMLangFont(); + } + } + + ReleaseDC(0, hdc); + return fontData; +#endif +} + +FontPlatformData* FontCache::getSimilarFontPlatformData(const Font& font) +{ + return 0; +} + +FontPlatformData* FontCache::getLastResortFallbackFont(const FontDescription& font) +{ + static AtomicString str("sans-serif"); + return getCachedFontPlatformData(font, str); +} + +static char* AtomicStringToUTF8String(const AtomicString& utf16) +{ + SkASSERT(sizeof(uint16_t) == sizeof(utf16.characters()[0])); + const uint16_t* uni = (uint16_t*)utf16.characters(); + + size_t bytes = SkUTF16_ToUTF8(uni, utf16.length(), NULL); + char* utf8 = (char*)sk_malloc_throw(bytes + 1); + + (void)SkUTF16_ToUTF8(uni, utf16.length(), utf8); + utf8[bytes] = 0; + return utf8; +} + +bool FontCache::fontExists(const FontDescription& fontDescription, const AtomicString& family) +{ + ASSERT(0); // FIXME HACK unimplemented + return false; +} + +FontPlatformData* FontCache::createFontPlatformData(const FontDescription& fontDescription, const AtomicString& family) +{ + char* storage = 0; + const char* name = 0; + + if (family.length() == 0) { + static const struct { + FontDescription::GenericFamilyType mType; + const char* mName; + } gNames[] = { + { FontDescription::SerifFamily, "serif" }, + { FontDescription::SansSerifFamily, "sans-serif" }, + { FontDescription::MonospaceFamily, "monospace" }, + { FontDescription::CursiveFamily, "cursive" }, + { FontDescription::FantasyFamily, "fantasy" } + }; + + FontDescription::GenericFamilyType type = fontDescription.genericFamily(); + for (unsigned i = 0; i < SK_ARRAY_COUNT(gNames); i++) + { + if (type == gNames[i].mType) + { + name = gNames[i].mName; + break; + } + } + // if we fall out of the loop, its ok for name to still be 0 + } + else { // convert the name to utf8 + storage = AtomicStringToUTF8String(family); + name = storage; + } + + int style = SkTypeface::kNormal; + if (fontDescription.weight() >= cBoldWeight) + style |= SkTypeface::kBold; + if (fontDescription.italic()) + style |= SkTypeface::kItalic; + + SkTypeface* tf = SkTypeface::Create(name, (SkTypeface::Style)style); + + FontPlatformData* result = new FontPlatformData(tf, + fontDescription.computedSize(), + (style & SkTypeface::kBold) && !tf->isBold(), + (style & SkTypeface::kItalic) && !tf->isItalic()); + tf->unref(); + sk_free(storage); + return result; +} + +} + diff --git a/WebCore/platform/graphics/android/FontCustomPlatformData.cpp b/WebCore/platform/graphics/android/FontCustomPlatformData.cpp new file mode 100644 index 0000000..eea5e36 --- /dev/null +++ b/WebCore/platform/graphics/android/FontCustomPlatformData.cpp @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +#include "config.h" +#include "FontCustomPlatformData.h" + +#include "SkTypeface.h" +#include "SkStream.h" +#include "SharedBuffer.h" +#include "FontPlatformData.h" + +namespace WebCore { + +FontCustomPlatformData::FontCustomPlatformData(SkTypeface* face) +{ + face->safeRef(); + m_typeface = face; +} + +FontCustomPlatformData::~FontCustomPlatformData() +{ + m_typeface->safeUnref(); + // the unref is enough to release the font data... +} + +FontPlatformData FontCustomPlatformData::fontPlatformData(int size, bool bold, bool italic) +{ + // turn bold/italic into fakeBold/fakeItalic + if (m_typeface != NULL) { + if (m_typeface->isBold() == bold) + bold = false; + if (m_typeface->isItalic() == italic) + italic = false; + } + return FontPlatformData(m_typeface, size, bold, italic); +} + +FontCustomPlatformData* createFontCustomPlatformData(SharedBuffer* buffer) +{ + // pass true until we know how we can share the data, and not have to + // make a copy of it. + SkStream* stream = new SkMemoryStream(buffer->data(), buffer->size(), true); + SkTypeface* face = SkTypeface::CreateFromStream(stream); + if (NULL == face) { + SkDebugf("--------- SkTypeface::CreateFromBuffer failed %d\n", + buffer->size()); + return NULL; + } + + SkAutoUnref aur(face); + + return new FontCustomPlatformData(face); +} + +} diff --git a/WebCore/platform/graphics/android/FontCustomPlatformData.h b/WebCore/platform/graphics/android/FontCustomPlatformData.h new file mode 100644 index 0000000..f072b6f --- /dev/null +++ b/WebCore/platform/graphics/android/FontCustomPlatformData.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +#ifndef FontCustomPlatformData_h_ +#define FontCustomPlatformData_h_ + +#include <wtf/Noncopyable.h> + +class SkTypeface; + +namespace WebCore { + + class SharedBuffer; + class FontPlatformData; + + class FontCustomPlatformData : Noncopyable { + public: + FontCustomPlatformData(SkTypeface* face); + ~FontCustomPlatformData(); + + SkTypeface* typeface() const { return m_typeface; } + + FontPlatformData fontPlatformData(int size, bool bold, bool italic); + + private: + SkTypeface* m_typeface; + }; + + FontCustomPlatformData* createFontCustomPlatformData(SharedBuffer* buffer); + +} // namespace WebCore + +#endif // FontCustomPlatformData_h_ + diff --git a/WebCore/platform/graphics/android/FontDataAndroid.cpp b/WebCore/platform/graphics/android/FontDataAndroid.cpp new file mode 100644 index 0000000..a90f536 --- /dev/null +++ b/WebCore/platform/graphics/android/FontDataAndroid.cpp @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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 "Font.h" +#include "FontCache.h" +#include "SimpleFontData.h" +#include "FloatRect.h" +#include "FontDescription.h" + +#include "SkPaint.h" +#include "SkTypeface.h" +#include "SkTime.h" + +namespace WebCore { + +void SimpleFontData::platformInit() +{ + SkPaint paint; + SkPaint::FontMetrics metrics; + + m_font.setupPaint(&paint); + (void)paint.getFontMetrics(&metrics); + + // use ceil instead of round to favor descent, given a lot of accidental + // clipping of descenders (e.g. 14pt 'g') in textedit fields + int d = SkScalarCeil(metrics.fDescent); + int s = SkScalarRound(metrics.fDescent - metrics.fAscent); + int a = s - d; + + m_ascent = a; + m_descent = d; + m_xHeight = SkScalarToFloat(-metrics.fAscent) * 0.56f; // hack I stole from the window's port + m_lineSpacing = a + d; + m_lineGap = SkScalarRound(metrics.fLeading); +} + +void SimpleFontData::platformDestroy() +{ + delete m_smallCapsFontData; +} + +SimpleFontData* SimpleFontData::smallCapsFontData(const FontDescription& fontDescription) const +{ + if (!m_smallCapsFontData) { + m_smallCapsFontData = new SimpleFontData(FontPlatformData(m_font, fontDescription.computedSize() * 0.7f)); + } + return m_smallCapsFontData; +} + +#define kMaxBufferCount 64 + +bool SimpleFontData::containsCharacters(const UChar* characters, int length) const +{ + SkPaint paint; + uint16_t glyphs[kMaxBufferCount]; + + m_font.setupPaint(&paint); + paint.setTextEncoding(SkPaint::kUTF16_TextEncoding); + + while (length > 0) { + int n = SkMin32(length, SK_ARRAY_COUNT(glyphs)); + + int count = paint.textToGlyphs(characters, n << 1, glyphs); + for (int i = 0; i < count; i++) { + if (0 == glyphs[i]) { + return false; // missing glyph + } + } + + characters += n; + length -= n; + } + return true; +} + +void SimpleFontData::determinePitch() +{ + m_treatAsFixedPitch = false; +} + +float SimpleFontData::platformWidthForGlyph(Glyph glyph) const +{ + SkASSERT(sizeof(glyph) == 2); // compile-time assert + + SkPaint paint; + + m_font.setupPaint(&paint); + + paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); + SkScalar width = paint.measureText(&glyph, 2); + + return SkScalarToFloat(width); +} + +} diff --git a/WebCore/platform/graphics/android/FontPlatformData.h b/WebCore/platform/graphics/android/FontPlatformData.h new file mode 100644 index 0000000..d6933d9 --- /dev/null +++ b/WebCore/platform/graphics/android/FontPlatformData.h @@ -0,0 +1,104 @@ +/* + * This file is part of the internal font implementation. It should not be included by anyone other than + * FontMac.cpp, FontWin.cpp and Font.cpp. + * + * Copyright (C) 2006 Apple Computer, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#ifndef FontPlatformData_H +#define FontPlatformData_H + +class SkPaint; +class SkTypeface; + +namespace WebCore { + +class FontPlatformData +{ +public: + static FontPlatformData Deleted() + { + return FontPlatformData(NULL, -1, false, false); + } + + FontPlatformData(); + FontPlatformData(const FontPlatformData&); + FontPlatformData(SkTypeface*, float textSize, bool fakeBold, bool fakeItalic); + FontPlatformData(const FontPlatformData& src, float textSize); + ~FontPlatformData(); + + FontPlatformData& operator=(const FontPlatformData&); + bool operator==(const FontPlatformData& a) const; + + void setupPaint(SkPaint*) const; + unsigned hash() const; + +private: + SkTypeface* mTypeface; + float mTextSize; + bool mFakeBold; + bool mFakeItalic; +}; + +#if 0 // windows port +class FontPlatformData +{ +public: + class Deleted {}; + + // Used for deleted values in the font cache's hash tables. + FontPlatformData(Deleted) + : m_font((HFONT)-1), m_fontFace(0), m_scaledFont(0), m_size(0) + {} + + FontPlatformData() + : m_font(0), m_fontFace(0), m_scaledFont(0), m_size(0) + {} + + FontPlatformData(HFONT, int size); + ~FontPlatformData(); + + HFONT hfont() const { return m_font; } + cairo_font_face_t* fontFace() const { return m_fontFace; } + cairo_scaled_font_t* scaledFont() const { return m_scaledFont; } + + int size() const { return m_size; } + + unsigned hash() const + { + return StringImpl::computeHash((UChar*)(&m_font), sizeof(HFONT) / sizeof(UChar)); + } + + bool operator==(const FontPlatformData& other) const + { + return m_font == other.m_font && m_fontFace == other.m_fontFace && + m_scaledFont == other.m_scaledFont && m_size == other.m_size; + } + +private: + HFONT m_font; + cairo_font_face_t* m_fontFace; + cairo_scaled_font_t* m_scaledFont; + int m_size; +}; +#endif + +} + +#endif diff --git a/WebCore/platform/graphics/android/FontPlatformDataAndroid.cpp b/WebCore/platform/graphics/android/FontPlatformDataAndroid.cpp new file mode 100644 index 0000000..4b4186a --- /dev/null +++ b/WebCore/platform/graphics/android/FontPlatformDataAndroid.cpp @@ -0,0 +1,151 @@ +/* + * This file is part of the internal font implementation. It should not be included by anyone other than + * FontMac.cpp, FontWin.cpp and Font.cpp. + * + * Copyright (C) 2006 Apple Computer, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "config.h" +#include "FontPlatformData.h" + +#include "SkPaint.h" +#include "SkTypeface.h" + +//#define TRACE_FONTPLATFORMDATA_LIFE +//#define COUNT_FONTPLATFORMDATA_LIFE + +#ifdef COUNT_FONTPLATFORMDATA_LIFE +static int gCount; +static int gMaxCount; + +static void inc_count() +{ + if (++gCount > gMaxCount) + { + gMaxCount = gCount; + SkDebugf("---------- FontPlatformData %d\n", gMaxCount); + } +} + +static void dec_count() { --gCount; } +#else + #define inc_count() + #define dec_count() +#endif + +#ifdef TRACE_FONTPLATFORMDATA_LIFE + #define trace(num) SkDebugf("FontPlatformData%d %p %g %d %d\n", num, mTypeface, mTextSize, mFakeBold, mFakeItalic) +#else + #define trace(num) +#endif + +namespace WebCore { + +FontPlatformData::FontPlatformData() + : mTypeface(NULL), mTextSize(0), mFakeBold(false), mFakeItalic(false) +{ + inc_count(); + trace(1); +} + +FontPlatformData::FontPlatformData(const FontPlatformData& src) +{ + src.mTypeface->safeRef(); + mTypeface = src.mTypeface; + + mTextSize = src.mTextSize; + mFakeBold = src.mFakeBold; + mFakeItalic = src.mFakeItalic; + + inc_count(); + trace(2); +} + +FontPlatformData::FontPlatformData(SkTypeface* tf, float textSize, bool fakeBold, bool fakeItalic) + : mTypeface(tf), mTextSize(textSize), mFakeBold(fakeBold), mFakeItalic(fakeItalic) +{ + mTypeface->safeRef(); + + inc_count(); + trace(3); +} + +FontPlatformData::FontPlatformData(const FontPlatformData& src, float textSize) + : mTypeface(src.mTypeface), mTextSize(textSize), mFakeBold(src.mFakeBold), mFakeItalic(src.mFakeItalic) +{ + mTypeface->safeRef(); + + inc_count(); + trace(4); +} + +FontPlatformData::~FontPlatformData() +{ + dec_count(); +#ifdef TRACE_FONTPLATFORMDATA_LIFE + SkDebugf("----------- ~FontPlatformData\n"); +#endif + + mTypeface->safeUnref(); +} + +FontPlatformData& FontPlatformData::operator=(const FontPlatformData& src) +{ + SkRefCnt_SafeAssign(mTypeface, src.mTypeface); + + mTextSize = src.mTextSize; + mFakeBold = src.mFakeBold; + mFakeItalic = src.mFakeItalic; + + return *this; +} + +void FontPlatformData::setupPaint(SkPaint* paint) const +{ + float ts = mTextSize; + if (!(ts > 0)) + ts = 12; + + paint->setAntiAlias(true); + paint->setSubpixelText(true); + paint->setTextSize(SkFloatToScalar(ts)); + paint->setTypeface(mTypeface); + paint->setFakeBoldText(mFakeBold); + paint->setTextSkewX(mFakeItalic ? -SK_Scalar1/4 : 0); + paint->setTextEncoding(SkPaint::kUTF16_TextEncoding); +} + +bool FontPlatformData::operator==(const FontPlatformData& a) const +{ + return SkTypeface::Equal(mTypeface, a.mTypeface) && + mTextSize == a.mTextSize && + mFakeBold == a.mFakeBold && + mFakeItalic == a.mFakeItalic; +} + +unsigned FontPlatformData::hash() const +{ + unsigned h = SkTypeface::UniqueID(mTypeface); + h ^= 0x01010101 * (((int)mFakeBold << 1) | (int)mFakeItalic); + h ^= *(uint32_t*)&mTextSize; + return h; +} + +} + diff --git a/WebCore/platform/graphics/android/GlyphMapAndroid.cpp b/WebCore/platform/graphics/android/GlyphMapAndroid.cpp new file mode 100644 index 0000000..9b77348 --- /dev/null +++ b/WebCore/platform/graphics/android/GlyphMapAndroid.cpp @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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 "GlyphPageTreeNode.h" +#include "SimpleFontData.h" + +#include "SkTemplates.h" +#include "SkPaint.h" +#include "SkUtils.h" + +namespace WebCore { + +#define NO_BREAK_SPACE_UNICHAR 0xA0 + +bool GlyphPage::fill(unsigned offset, unsigned length, UChar* buffer, unsigned bufferLength, const SimpleFontData* fontData) +{ + if (SkUTF16_IsHighSurrogate(buffer[bufferLength-1])) { + SkDebugf("%s last char is high-surrogate", __FUNCTION__); + return false; + } + + SkPaint paint; + fontData->platformData().setupPaint(&paint); + paint.setTextEncoding(SkPaint::kUTF16_TextEncoding); + + SkAutoSTMalloc <GlyphPage::size, uint16_t> glyphStorage(length); + uint16_t* glyphs = glyphStorage.get(); + unsigned count = paint.textToGlyphs(buffer, bufferLength << 1, glyphs); + if (count != length) { + SkDebugf("%s count != length\n", __FUNCTION__); + return false; + } + + unsigned allGlyphs = 0; // track if any of the glyphIDs are non-zero + for (unsigned i = 0; i < length; i++) { + setGlyphDataForIndex(offset + i, glyphs[i], fontData); + allGlyphs |= glyphs[i]; + } + return allGlyphs != 0; +} + +} + diff --git a/WebCore/platform/graphics/android/GraphicsContextAndroid.cpp b/WebCore/platform/graphics/android/GraphicsContextAndroid.cpp new file mode 100644 index 0000000..23ac51d --- /dev/null +++ b/WebCore/platform/graphics/android/GraphicsContextAndroid.cpp @@ -0,0 +1,1114 @@ +/* + * + * Copyright 2006, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +#include "config.h" +#include "GraphicsContext.h" +#include "NotImplemented.h" +#include "Path.h" + +#include "SkBlurDrawLooper.h" +#include "SkCanvas.h" +#include "SkColorPriv.h" +#include "SkDashPathEffect.h" +#include "SkDevice.h" +#include "SkPaint.h" +#include "SkPorterDuff.h" +#include "WebCoreViewBridge.h" +#include "PlatformGraphicsContext.h" +#include "AffineTransform.h" + +#include "android_graphics.h" +#include "SkGradientShader.h" +#include "SkBitmapRef.h" +#include "SkString.h" + +using namespace std; + +#define GC2Canvas(ctx) (ctx)->m_data->mPgc->mCanvas + +namespace WebCore { + +static int RoundToInt(float x) +{ + return (int)roundf(x); +} + +/* TODO / questions + + mAlpha: how does this interact with the alpha in Color? multiply them together? + mPorterDuffMode: do I always respect this? If so, then + the rgb() & 0xFF000000 check will abort drawing too often + Is Color premultiplied or not? If it is, then I can't blindly pass it to paint.setColor() +*/ + +class GraphicsContextPlatformPrivate { +public: + GraphicsContext* mCG; // back-ptr to our parent + PlatformGraphicsContext* mPgc; + + struct State { + float mMiterLimit; + float mAlpha; + SkDrawLooper* mLooper; + SkPaint::Cap mLineCap; + SkPaint::Join mLineJoin; + SkPorterDuff::Mode mPorterDuffMode; + int mDashRatio; //ratio of the length of a dash to its width + + State() + { + mMiterLimit = 4; + mAlpha = 1; + mLooper = NULL; + mLineCap = SkPaint::kDefault_Cap; + mLineJoin = SkPaint::kDefault_Join; + mPorterDuffMode = SkPorterDuff::kSrcOver_Mode; + mDashRatio = 3; + } + + State(const State& other) + { + other.mLooper->safeRef(); + memcpy(this, &other, sizeof(State)); + } + + ~State() + { + mLooper->safeUnref(); + } + + SkDrawLooper* setDrawLooper(SkDrawLooper* dl) + { + SkRefCnt_SafeAssign(mLooper, dl); + return dl; + } + + SkColor applyAlpha(SkColor c) const + { + int s = RoundToInt(mAlpha * 256); + if (s >= 256) + return c; + if (s < 0) + return 0; + + int a = SkAlphaMul(SkColorGetA(c), s); + return (c & 0x00FFFFFF) | (a << 24); + } + }; + + SkDeque mStateStack; + State* mState; + + GraphicsContextPlatformPrivate(GraphicsContext* cg, PlatformGraphicsContext* pgc) + : mCG(cg) + , mPgc(pgc), mStateStack(sizeof(State)) + + { + State* state = (State*)mStateStack.push_back(); + new (state) State(); + mState = state; + } + + ~GraphicsContextPlatformPrivate() + { + if (mPgc && mPgc->deleteUs()) + delete mPgc; + + // we force restores so we don't leak any subobjects owned by our + // stack of State records. + while (mStateStack.count() > 0) + this->restore(); + } + + void save() + { + State* newState = (State*)mStateStack.push_back(); + new (newState) State(*mState); + mState = newState; + } + + void restore() + { + mState->~State(); + mStateStack.pop_back(); + mState = (State*)mStateStack.back(); + } + + void setup_paint_common(SkPaint* paint) const + { +#ifdef SK_DEBUGx + { + SkPaint defaultPaint; + + SkASSERT(*paint == defaultPaint); + } +#endif + + paint->setAntiAlias(true); + paint->setDither(true); + paint->setPorterDuffXfermode(mState->mPorterDuffMode); + paint->setLooper(mState->mLooper); + } + + void setup_paint_fill(SkPaint* paint) const + { + this->setup_paint_common(paint); + + paint->setColor(mState->applyAlpha(mCG->fillColor().rgb())); + } + + /* sets up the paint for stroking. Returns true if the style is really + just a dash of squares (the size of the paint's stroke-width. + */ + bool setup_paint_stroke(SkPaint* paint, SkRect* rect) { + this->setup_paint_common(paint); + + float width = mCG->strokeThickness(); + + //this allows dashing and dotting to work properly for hairline strokes + if (0 == width) { + width = 1; + } + + paint->setColor(mState->applyAlpha(mCG->strokeColor().rgb())); + paint->setStyle(SkPaint::kStroke_Style); + paint->setStrokeWidth(SkFloatToScalar(width)); + paint->setStrokeCap(mState->mLineCap); + paint->setStrokeJoin(mState->mLineJoin); + paint->setStrokeMiter(SkFloatToScalar(mState->mMiterLimit)); + + if (rect != NULL && (RoundToInt(width) & 1)) { + rect->inset(-SK_ScalarHalf, -SK_ScalarHalf); + } + + switch (mCG->strokeStyle()) { + case NoStroke: + case SolidStroke: + width = 0; + break; + case DashedStroke: + width = mState->mDashRatio * width; + break; + /* no break */ + case DottedStroke: + break; + } + + if (width > 0) { + SkScalar intervals[] = { width, width }; + SkPathEffect* pe = new SkDashPathEffect(intervals, 2, 0); + paint->setPathEffect(pe)->unref(); + // return true if we're basically a dotted dash of squares + return RoundToInt(width) == RoundToInt(paint->getStrokeWidth()); + } + return false; + } + +private: + // not supported yet + State& operator=(const State&); +}; + +//////////////////////////////////////////////////////////////////////////////////////////////// + +GraphicsContext* GraphicsContext::createOffscreenContext(int width, int height) +{ + PlatformGraphicsContext* pgc = new PlatformGraphicsContext(); + + SkBitmap bitmap; + + bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height); + bitmap.allocPixels(); + bitmap.eraseColor(0); + pgc->mCanvas->setBitmapDevice(bitmap); + + GraphicsContext* ctx = new GraphicsContext(pgc); +//printf("-------- create offscreen <canvas> %p\n", ctx); + return ctx; +} + +void GraphicsContext::drawOffscreenContext(GraphicsContext* ctx, const FloatRect* srcRect, const FloatRect& dstRect) +{ + const SkBitmap& bm = GC2Canvas(ctx)->getDevice()->accessBitmap(false); + SkIRect src; + SkRect dst; + SkPaint paint; + + paint.setFilterBitmap(true); + + GC2Canvas(this)->drawBitmapRect(bm, + srcRect ? android_setrect(&src, *srcRect) : NULL, + *android_setrect(&dst, dstRect), + &paint); +} + +FloatRect GraphicsContext::getClipLocalBounds() const +{ + SkRect r; + + if (!GC2Canvas(this)->getClipBounds(&r)) + r.setEmpty(); + + return FloatRect(SkScalarToFloat(r.fLeft), SkScalarToFloat(r.fTop), + SkScalarToFloat(r.width()), SkScalarToFloat(r.height())); +} + +//////////////////////////////////////////////////////////////////////////////////////////////// + +GraphicsContext::GraphicsContext(PlatformGraphicsContext *gc) + : m_common(createGraphicsContextPrivate()) + , m_data(new GraphicsContextPlatformPrivate(this, gc)) +{ + setPaintingDisabled(NULL == gc || NULL == gc->mCanvas); +} + +GraphicsContext::~GraphicsContext() +{ + delete m_data; + this->destroyGraphicsContextPrivate(m_common); +} + +void GraphicsContext::savePlatformState() +{ + // save our private State + m_data->save(); + // save our native canvas + GC2Canvas(this)->save(); +} + +void GraphicsContext::restorePlatformState() +{ + // restore our native canvas + GC2Canvas(this)->restore(); + // restore our private State + m_data->restore(); +} + +// Draws a filled rectangle with a stroked border. +void GraphicsContext::drawRect(const IntRect& rect) +{ + if (paintingDisabled()) + return; + + SkPaint paint; + SkRect r; + + android_setrect(&r, rect); + + if (fillColor().alpha()) { + m_data->setup_paint_fill(&paint); + GC2Canvas(this)->drawRect(r, paint); + } + + if (strokeStyle() != NoStroke && strokeColor().alpha()) { + paint.reset(); + m_data->setup_paint_stroke(&paint, &r); + GC2Canvas(this)->drawRect(r, paint); + } +} + +// This is only used to draw borders. +void GraphicsContext::drawLine(const IntPoint& point1, const IntPoint& point2) +{ + if (paintingDisabled()) + return; + + StrokeStyle style = strokeStyle(); + if (style == NoStroke) + return; + + SkPaint paint; + SkCanvas* canvas = GC2Canvas(this); + const int idx = SkAbs32(point2.x() - point1.x()); + const int idy = SkAbs32(point2.y() - point1.y()); + + // special-case horizontal and vertical lines that are really just dots + if (m_data->setup_paint_stroke(&paint, NULL) && (0 == idx || 0 == idy)) { + const SkScalar diameter = paint.getStrokeWidth(); + const SkScalar radius = SkScalarHalf(diameter); + SkScalar x = SkIntToScalar(SkMin32(point1.x(), point2.x())); + SkScalar y = SkIntToScalar(SkMin32(point1.y(), point2.y())); + SkScalar dx, dy; + int count; + SkRect bounds; + + if (0 == idy) { // horizontal + bounds.set(x, y - radius, x + SkIntToScalar(idx), y + radius); + x += radius; + dx = diameter * 2; + dy = 0; + count = idx; + } else { // vertical + bounds.set(x - radius, y, x + radius, y + SkIntToScalar(idy)); + y += radius; + dx = 0; + dy = diameter * 2; + count = idy; + } + + // the actual count is the number of ONs we hit alternating + // ON(diameter), OFF(diameter), ... + { + SkScalar width = SkScalarDiv(SkIntToScalar(count), diameter); + // now computer the number of cells (ON and OFF) + count = SkScalarRound(width); + // now compute the number of ONs + count = (count + 1) >> 1; + } + + SkAutoMalloc storage(count * sizeof(SkPoint)); + SkPoint* verts = (SkPoint*)storage.get(); + // now build the array of vertices to past to drawPoints + for (int i = 0; i < count; i++) { + verts[i].set(x, y); + x += dx; + y += dy; + } + + paint.setStyle(SkPaint::kFill_Style); + paint.setPathEffect(NULL); + + // clipping to bounds is not required for correctness, but it does + // allow us to reject the entire array of points if we are completely + // offscreen. This is common in a webpage for android, where most of + // the content is clipped out. If drawPoints took an (optional) bounds + // parameter, that might even be better, as we would *just* use it for + // culling, and not both wacking the canvas' save/restore stack. + canvas->save(SkCanvas::kClip_SaveFlag); + canvas->clipRect(bounds); + canvas->drawPoints(SkCanvas::kPoints_PointMode, count, verts, paint); + canvas->restore(); + } else { + SkPoint pts[2]; + android_setpt(&pts[0], point1); + android_setpt(&pts[1], point2); + canvas->drawLine(pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY, paint); + } +} + +static void setrect_for_underline(SkRect* r, GraphicsContext* context, const IntPoint& point, int yOffset, int width) +{ + float lineThickness = context->strokeThickness(); +// if (lineThickness < 1) // do we really need/want this? +// lineThickness = 1; + + yOffset += 1; // we add 1 to have underlines appear below the text + + r->fLeft = SkIntToScalar(point.x()); + r->fTop = SkIntToScalar(point.y() + yOffset); + r->fRight = r->fLeft + SkIntToScalar(width); + r->fBottom = r->fTop + SkFloatToScalar(lineThickness); +} + +void GraphicsContext::drawLineForText(IntPoint const& pt, int width, bool) +{ + if (paintingDisabled()) + return; + + SkRect r; + setrect_for_underline(&r, this, pt, 0, width); + + SkPaint paint; + paint.setAntiAlias(true); + paint.setColor(this->strokeColor().rgb()); + + GC2Canvas(this)->drawRect(r, paint); +} + +void GraphicsContext::drawLineForMisspellingOrBadGrammar(const IntPoint& pt, int width, bool grammar) +{ + if (paintingDisabled()) + return; + + SkRect r; + setrect_for_underline(&r, this, pt, 0, width); + + SkPaint paint; + paint.setAntiAlias(true); + paint.setColor(SK_ColorRED); // is this specified somewhere? + + GC2Canvas(this)->drawRect(r, paint); +} + +// This method is only used to draw the little circles used in lists. +void GraphicsContext::drawEllipse(const IntRect& rect) +{ + if (paintingDisabled()) + return; + + SkPaint paint; + SkRect oval; + + android_setrect(&oval, rect); + + if (fillColor().rgb() & 0xFF000000) { + m_data->setup_paint_fill(&paint); + GC2Canvas(this)->drawOval(oval, paint); + } + if (strokeStyle() != NoStroke) { + paint.reset(); + m_data->setup_paint_stroke(&paint, &oval); + GC2Canvas(this)->drawOval(oval, paint); + } +} + +static inline int fast_mod(int value, int max) +{ + int sign = SkExtractSign(value); + + value = SkApplySign(value, sign); + if (value >= max) { + value %= max; + } + return SkApplySign(value, sign); +} + +void GraphicsContext::strokeArc(const IntRect& r, int startAngle, int angleSpan) +{ + if (paintingDisabled()) + return; + + SkPath path; + SkPaint paint; + SkRect oval; + + android_setrect(&oval, r); + + if (strokeStyle() == NoStroke) { + m_data->setup_paint_fill(&paint); // we want the fill color + paint.setStyle(SkPaint::kStroke_Style); + paint.setStrokeWidth(SkFloatToScalar(this->strokeThickness())); + } + else { + m_data->setup_paint_stroke(&paint, NULL); + } + + // we do this before converting to scalar, so we don't overflow SkFixed + startAngle = fast_mod(startAngle, 360); + angleSpan = fast_mod(angleSpan, 360); + + path.addArc(oval, SkIntToScalar(-startAngle), SkIntToScalar(-angleSpan)); + GC2Canvas(this)->drawPath(path, paint); +} + +void GraphicsContext::drawConvexPolygon(size_t numPoints, const FloatPoint* points, bool shouldAntialias) +{ + if (paintingDisabled()) + return; + + if (numPoints <= 1) + return; + + SkPaint paint; + SkPath path; + + path.incReserve(numPoints); + path.moveTo(SkFloatToScalar(points[0].x()), SkFloatToScalar(points[0].y())); + for (size_t i = 1; i < numPoints; i++) + path.lineTo(SkFloatToScalar(points[i].x()), SkFloatToScalar(points[i].y())); + + if (fillColor().rgb() & 0xFF000000) { + m_data->setup_paint_fill(&paint); + GC2Canvas(this)->drawPath(path, paint); + } + + if (strokeStyle() != NoStroke) { + paint.reset(); + m_data->setup_paint_stroke(&paint, NULL); + GC2Canvas(this)->drawPath(path, paint); + } +} + +#ifdef ANDROID_CANVAS_IMPL + +static void check_set_shader(SkPaint* paint, SkShader* s0, SkShader* s1) +{ + if (s0) { + paint->setShader(s0); + } + else if (s1) { + paint->setShader(s1); + } +} + +void GraphicsContext::fillPath(const Path& webCorePath, PlatformGradient* grad, PlatformPattern* pat) +{ + if (paintingDisabled()) + return; + + SkPaint paint; + + m_data->setup_paint_fill(&paint); + check_set_shader(&paint, grad, pat); + + const SkPath& path = *webCorePath.platformPath(); + +#if 0 + SkDebugf("---- fillPath\n"); + SkPath::Iter iter(path, false); + SkPoint pts[4]; + for (;;) { + SkString str; + const SkPoint* p = &pts[1]; + int n = 0; + const char* name = ""; + switch (iter.next(pts)) { + case SkPath::kMove_Verb: + name = " M"; + p = &pts[0]; + n = 1; + break; + case SkPath::kLine_Verb: + name = " L"; + n = 1; + break; + case SkPath::kQuad_Verb: + name = " Q"; + n = 2; + break; + case SkPath::kCubic_Verb: + name = " C"; + n = 3; + break; + case SkPath::kClose_Verb: + name = " X"; + n = 0; + break; + case SkPath::kDone_Verb: + goto DONE; + } + str.append(name); + for (int i = 0; i < n; i++) { + str.append(" "); + str.appendScalar(p[i].fX); + str.append(" "); + str.appendScalar(p[i].fY); + } + SkDebugf("\"%s\"\n", str.c_str()); + } +DONE: +#endif + + GC2Canvas(this)->drawPath(path, paint); +} + +void GraphicsContext::strokePath(const Path& webCorePath, PlatformGradient* grad, PlatformPattern* pat) +{ + if (paintingDisabled()) + return; + + SkPaint paint; + + m_data->setup_paint_stroke(&paint, NULL); + check_set_shader(&paint, grad, pat); + + GC2Canvas(this)->drawPath(*webCorePath.platformPath(), paint); +} + +void GraphicsContext::fillRect(const FloatRect& rect, PlatformGradient* grad, PlatformPattern* pat) +{ + if (paintingDisabled()) + return; + + SkRect r; + SkPaint paint; + + m_data->setup_paint_fill(&paint); + check_set_shader(&paint, grad, pat); + + GC2Canvas(this)->drawRect(*android_setrect(&r, rect), paint); +} + +void GraphicsContext::fillRoundedRect(const IntRect& rect, const IntSize& topLeft, const IntSize& topRight, + const IntSize& bottomLeft, const IntSize& bottomRight, const Color& color) +{ + if (paintingDisabled()) + return; + + SkPaint paint; + SkPath path; + SkScalar radii[8]; + SkRect r; + + radii[0] = SkIntToScalar(topLeft.width()); + radii[1] = SkIntToScalar(topLeft.height()); + radii[2] = SkIntToScalar(topRight.width()); + radii[3] = SkIntToScalar(topRight.height()); + radii[4] = SkIntToScalar(bottomRight.width()); + radii[5] = SkIntToScalar(bottomRight.height()); + radii[6] = SkIntToScalar(bottomLeft.width()); + radii[7] = SkIntToScalar(bottomLeft.height()); + path.addRoundRect(*android_setrect(&r, rect), radii); + + m_data->setup_paint_fill(&paint); + GC2Canvas(this)->drawPath(path, paint); +} + +void GraphicsContext::strokeRect(const FloatRect& rect, float lineWidth, PlatformGradient* grad, PlatformPattern* pat) +{ + if (paintingDisabled()) + return; + + SkRect r; + SkPaint paint; + + m_data->setup_paint_stroke(&paint, NULL); + paint.setStrokeWidth(SkFloatToScalar(lineWidth)); + check_set_shader(&paint, grad, pat); + + GC2Canvas(this)->drawRect(*android_setrect(&r, rect), paint); +} + +static U8CPU F2B(float x) +{ + return (int)(x * 255); +} + +static SkColor make_color(float a, float r, float g, float b) +{ + return SkColorSetARGB(F2B(a), F2B(r), F2B(g), F2B(b)); +} + +PlatformGradient* GraphicsContext::newPlatformLinearGradient(const FloatPoint& p0, const FloatPoint& p1, + const float stopData[5], int count) +{ + SkPoint pts[2]; + + android_setpt(&pts[0], p0); + android_setpt(&pts[1], p1); + + SkAutoMalloc storage(count * (sizeof(SkColor) + sizeof(SkScalar))); + SkColor* colors = (SkColor*)storage.get(); + SkScalar* pos = (SkScalar*)(colors + count); + + for (int i = 0; i < count; i++) + { + pos[i] = SkFloatToScalar(stopData[0]); + colors[i] = make_color(stopData[4], stopData[1], stopData[2], stopData[3]); + stopData += 5; + } + + return SkGradientShader::CreateLinear(pts, colors, pos, count, + SkShader::kClamp_TileMode); +} + +PlatformGradient* GraphicsContext::newPlatformRadialGradient(const FloatPoint& p0, float r0, + const FloatPoint& p1, float r1, + const float stopData[5], int count) +{ + SkPoint center; + + android_setpt(¢er, p1); + + SkAutoMalloc storage(count * (sizeof(SkColor) + sizeof(SkScalar))); + SkColor* colors = (SkColor*)storage.get(); + SkScalar* pos = (SkScalar*)(colors + count); + + for (int i = 0; i < count; i++) + { + pos[i] = SkFloatToScalar(stopData[0]); + colors[i] = make_color(stopData[4], stopData[1], stopData[2], stopData[3]); + stopData += 5; + } + + return SkGradientShader::CreateRadial(center, SkFloatToScalar(r1), + colors, pos, count, + SkShader::kClamp_TileMode); +} + +void GraphicsContext::freePlatformGradient(PlatformGradient* shader) +{ + shader->safeUnref(); +} + +PlatformPattern* GraphicsContext::newPlatformPattern(Image* image, + Image::TileRule hRule, + Image::TileRule vRule) +{ +//printf("----------- Image %p, [%d %d] %d %d\n", image, image->width(), image->height(), hRule, vRule); + if (NULL == image) + return NULL; + + SkBitmapRef* bm = image->nativeImageForCurrentFrame(); + if (NULL == bm) + return NULL; + + return SkShader::CreateBitmapShader(bm->bitmap(), + android_convert_TileRule(hRule), + android_convert_TileRule(vRule)); +} + +void GraphicsContext::freePlatformPattern(PlatformPattern* shader) +{ + shader->safeUnref(); +} + +#endif + +#if 0 +static int getBlendedColorComponent(int c, int a) +{ + // We use white. + float alpha = (float)(a) / 255; + int whiteBlend = 255 - a; + c -= whiteBlend; + return (int)(c/alpha); +} +#endif + +void GraphicsContext::fillRect(const IntRect& rect, const Color& color) +{ + if (paintingDisabled()) + return; + + if (color.rgb() & 0xFF000000) { + SkPaint paint; + SkRect r; + + android_setrect(&r, rect); + m_data->setup_paint_common(&paint); + paint.setColor(color.rgb()); + GC2Canvas(this)->drawRect(r, paint); + } +} + +void GraphicsContext::fillRect(const FloatRect& rect, const Color& color) +{ + if (paintingDisabled()) + return; + + if (color.rgb() & 0xFF000000) { + SkPaint paint; + SkRect r; + + android_setrect(&r, rect); + m_data->setup_paint_common(&paint); + paint.setColor(color.rgb()); + GC2Canvas(this)->drawRect(r, paint); + } +} + +void GraphicsContext::clip(const IntRect& rect) +{ + if (paintingDisabled()) + return; + + SkRect r; + + GC2Canvas(this)->clipRect(*android_setrect(&r, rect)); +} + +void GraphicsContext::clip(const Path& path) +{ + if (paintingDisabled()) + return; + +// path.platformPath()->dump(false, "clip path"); + + GC2Canvas(this)->clipPath(*path.platformPath()); +} + +void GraphicsContext::addInnerRoundedRectClip(const IntRect& rect, int thickness) +{ + if (paintingDisabled()) + return; + +//printf("-------- addInnerRoundedRectClip: [%d %d %d %d] thickness=%d\n", rect.x(), rect.y(), rect.width(), rect.height(), thickness); + + SkPath path; + SkRect r; + android_setrect(&r, rect); + path.addOval(r, SkPath::kCW_Direction); + // only perform the inset if we won't invert r + if (2*thickness < rect.width() && 2*thickness < rect.height()) + { + r.inset(SkIntToScalar(thickness) ,SkIntToScalar(thickness)); + path.addOval(r, SkPath::kCCW_Direction); + } + GC2Canvas(this)->clipPath(path); +} + +void GraphicsContext::clipOut(const IntRect& r) +{ + if (paintingDisabled()) + return; + + SkRect rect; + + GC2Canvas(this)->clipRect(*android_setrect(&rect, r), SkRegion::kDifference_Op); +} + +void GraphicsContext::clipOutEllipseInRect(const IntRect& r) +{ + if (paintingDisabled()) + return; + + SkRect oval; + SkPath path; + + path.addOval(*android_setrect(&oval, r), SkPath::kCCW_Direction); + GC2Canvas(this)->clipPath(path, SkRegion::kDifference_Op); +} + +void GraphicsContext::clipOut(const Path& p) +{ + if (paintingDisabled()) + return; + + GC2Canvas(this)->clipPath(*p.platformPath(), SkRegion::kDifference_Op); +} + +////////////////////////////////////////////////////////////////////////////////////////////////// + +#if SVG_SUPPORT +KRenderingDeviceContext* GraphicsContext::createRenderingDeviceContext() +{ + return new KRenderingDeviceContextQuartz(platformContext()); +} +#endif + +void GraphicsContext::beginTransparencyLayer(float opacity) +{ + if (paintingDisabled()) + return; + + SkCanvas* canvas = GC2Canvas(this); + + if (opacity < 1) + { + canvas->saveLayerAlpha(NULL, (int)(opacity * 255), SkCanvas::kHasAlphaLayer_SaveFlag); + } + else + canvas->save(); +} + +void GraphicsContext::endTransparencyLayer() +{ + if (paintingDisabled()) + return; + + GC2Canvas(this)->restore(); +} + +void GraphicsContext::setShadow(const IntSize& size, int blur, const Color& color) +{ + if (paintingDisabled()) + return; + + if (blur > 0) + { + SkColor c; + + if (color.isValid()) + c = color.rgb(); + else + c = SkColorSetARGB(0xFF/3, 0, 0, 0); // "std" Apple shadow color + + SkDrawLooper* dl = new SkBlurDrawLooper(SkIntToScalar(blur), + SkIntToScalar(size.width()), + SkIntToScalar(size.height()), + c); + m_data->mState->setDrawLooper(dl)->unref(); + } + else + m_data->mState->setDrawLooper(NULL); +} + +void GraphicsContext::clearShadow() +{ + if (paintingDisabled()) + return; + + m_data->mState->setDrawLooper(NULL); +} + +void GraphicsContext::drawFocusRing(const Color& color) +{ + // Do nothing, since we draw the focus ring independently. +} + +PlatformGraphicsContext* GraphicsContext::platformContext() const +{ + ASSERT(!paintingDisabled()); + return m_data->mPgc; +} + +void GraphicsContext::setMiterLimit(float limit) +{ + m_data->mState->mMiterLimit = limit; +} + +void GraphicsContext::setAlpha(float alpha) +{ + m_data->mState->mAlpha = alpha; +} + +void GraphicsContext::setCompositeOperation(CompositeOperator op) +{ + m_data->mState->mPorterDuffMode = android_convert_compositeOp(op); +} + +void GraphicsContext::clearRect(const FloatRect& rect) +{ + if (paintingDisabled()) + return; + + SkPaint paint; + SkRect r; + + android_setrect(&r, rect); + m_data->setup_paint_fill(&paint); + paint.setPorterDuffXfermode(SkPorterDuff::kClear_Mode); + GC2Canvas(this)->drawRect(r, paint); +} + +void GraphicsContext::strokeRect(const FloatRect& rect, float lineWidth) +{ + if (paintingDisabled()) + return; + + SkPaint paint; + SkRect r; + + android_setrect(&r, rect); + + m_data->setup_paint_stroke(&paint, NULL); + paint.setStrokeWidth(SkFloatToScalar(lineWidth)); + GC2Canvas(this)->drawRect(r, paint); +} + +void GraphicsContext::setLineCap(LineCap cap) +{ + switch (cap) { + case ButtCap: + m_data->mState->mLineCap = SkPaint::kButt_Cap; + break; + case RoundCap: + m_data->mState->mLineCap = SkPaint::kRound_Cap; + break; + case SquareCap: + m_data->mState->mLineCap = SkPaint::kSquare_Cap; + break; + default: + SkDEBUGF(("GraphicsContext::setLineCap: unknown LineCap %d\n", cap)); + break; + } +} + +void GraphicsContext::setLineJoin(LineJoin join) +{ + switch (join) { + case MiterJoin: + m_data->mState->mLineJoin = SkPaint::kMiter_Join; + break; + case RoundJoin: + m_data->mState->mLineJoin = SkPaint::kRound_Join; + break; + case BevelJoin: + m_data->mState->mLineJoin = SkPaint::kBevel_Join; + break; + default: + SkDEBUGF(("GraphicsContext::setLineJoin: unknown LineJoin %d\n", join)); + break; + } +} + +void GraphicsContext::scale(const FloatSize& size) +{ + if (paintingDisabled()) + return; + GC2Canvas(this)->scale(SkFloatToScalar(size.width()), SkFloatToScalar(size.height())); +} + +void GraphicsContext::rotate(float angleInRadians) +{ + if (paintingDisabled()) + return; + GC2Canvas(this)->rotate(SkFloatToScalar(angleInRadians * (180.0f / 3.14159265f))); +} + +void GraphicsContext::translate(float x, float y) +{ + if (paintingDisabled()) + return; + GC2Canvas(this)->translate(SkFloatToScalar(x), SkFloatToScalar(y)); +} + +void GraphicsContext::concatCTM(const AffineTransform& xform) +{ + if (paintingDisabled()) + return; + +//printf("-------------- GraphicsContext::concatCTM\n"); + GC2Canvas(this)->concat((SkMatrix) xform); +} + +FloatRect GraphicsContext::roundToDevicePixels(const FloatRect& rect) +{ + if (paintingDisabled()) + return FloatRect(); + + const SkMatrix& matrix = GC2Canvas(this)->getTotalMatrix(); + SkRect r; + android_setrect(&r, rect); + matrix.mapRect(&r); + FloatRect result(SkScalarToFloat(r.fLeft), SkScalarToFloat(r.fTop), SkScalarToFloat(r.width()), SkScalarToFloat(r.height())); + return result; +} + +////////////////////////////////////////////////////////////////////////////////////////////////// + +void GraphicsContext::setURLForRect(const KURL& link, const IntRect& destRect) +{ + // appears to be PDF specific, so we ignore it +#if 0 + if (paintingDisabled()) + return; + + CFURLRef urlRef = link.createCFURL(); + if (urlRef) { + CGContextRef context = platformContext(); + + // Get the bounding box to handle clipping. + CGRect box = CGContextGetClipBoundingBox(context); + + IntRect intBox((int)box.origin.x, (int)box.origin.y, (int)box.size.width, (int)box.size.height); + IntRect rect = destRect; + rect.intersect(intBox); + + CGPDFContextSetURLForRect(context, urlRef, + CGRectApplyAffineTransform(rect, CGContextGetCTM(context))); + + CFRelease(urlRef); + } +#endif +} + +// we don't need to push these down, since we query the current state and build our paint at draw-time + +void GraphicsContext::setPlatformStrokeColor(const Color&) {} +void GraphicsContext::setPlatformStrokeThickness(float) {} +void GraphicsContext::setPlatformFillColor(const Color&) {} + + +// functions new to Feb-19 tip of tree merge: +AffineTransform GraphicsContext::getCTM() const { + notImplemented(); + return AffineTransform(); +} + +} diff --git a/WebCore/platform/graphics/android/ImageAndroid.cpp b/WebCore/platform/graphics/android/ImageAndroid.cpp new file mode 100644 index 0000000..00145af --- /dev/null +++ b/WebCore/platform/graphics/android/ImageAndroid.cpp @@ -0,0 +1,254 @@ +/* + * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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 "AffineTransform.h" +#include "BitmapImage.h" +#include "Image.h" +#include "FloatRect.h" +#include "GraphicsContext.h" +#include "PlatformGraphicsContext.h" +#include "PlatformString.h" +#include "SharedBuffer.h" + +#include "android_graphics.h" +#include "SkBitmapRef.h" +#include "SkCanvas.h" +#include "SkColorPriv.h" +#include "SkShader.h" +#include "SkString.h" + +#include <utils/AssetManager.h> + +//#define TRACE_SUBSAMPLED_BITMAPS + +android::AssetManager* gGlobalAssetMgr; + +namespace WebCore { + +void FrameData::clear() +{ + if (m_frame) { + m_frame->unref(); + m_frame = 0; + m_duration = 0.; + m_hasAlpha = true; + } +} + +SkBitmapRef* BitmapImage::getBitmap() +{ + return m_bitmapRef; +} + +void BitmapImage::initPlatformData() +{ + m_bitmapRef = NULL; + m_source.clearURL(); +} + +void BitmapImage::invalidatePlatformData() +{ + if (m_bitmapRef) { + m_bitmapRef->unref(); + m_bitmapRef = NULL; + } +} + +void BitmapImage::checkForSolidColor() +{ + m_isSolidColor = false; + if (this->frameCount() > 1) { + if (!m_bitmapRef) { + return; + } + + const SkBitmap& bm = m_bitmapRef->bitmap(); + + if (bm.width() == 1 && bm.height() == 1) { + SkAutoLockPixels alp(bm); + if (bm.getPixels() == NULL) { + return; + } + + SkPMColor color; + switch (bm.getConfig()) { + case SkBitmap::kARGB_8888_Config: + color = *bm.getAddr32(0, 0); + break; + case SkBitmap::kRGB_565_Config: + color = SkPixel16ToPixel32(*bm.getAddr16(0, 0)); + break; + case SkBitmap::kIndex8_Config: { + SkColorTable* ctable = bm.getColorTable(); + if (!ctable) { + return; + } + color = (*ctable)[*bm.getAddr8(0, 0)]; + break; + } + default: // don't check other configs + return; + } + m_isSolidColor = true; + m_solidColor = android_SkPMColorToWebCoreColor(color); + } + } +} + +void BitmapImage::draw(GraphicsContext* ctxt, const FloatRect& dstRect, + const FloatRect& srcRect, CompositeOperator compositeOp) +{ + SkBitmapRef* image = this->nativeImageForCurrentFrame(); + if (!image) { // If it's too early we won't have an image yet. + return; + } + + // in case we get called with an incomplete bitmap + const SkBitmap& bitmap = image->bitmap(); + if (bitmap.getPixels() == NULL && bitmap.pixelRef() == NULL) { + return; + } + + SkIRect srcR; + SkRect dstR; + float invScaleX = (float)bitmap.width() / image->origWidth(); + float invScaleY = (float)bitmap.height() / image->origHeight(); + + android_setrect(&dstR, dstRect); + android_setrect_scaled(&srcR, srcRect, invScaleX, invScaleY); + if (srcR.isEmpty() || dstR.isEmpty()) { + return; + } + + SkCanvas* canvas = ctxt->platformContext()->mCanvas; + SkPaint paint; + + paint.setFilterBitmap(true); + paint.setPorterDuffXfermode(android_convert_compositeOp(compositeOp)); + canvas->drawBitmapRect(bitmap, &srcR, dstR, &paint); + + startAnimation(); + +#ifdef TRACE_SUBSAMPLED_BITMAPS + if (bitmap.width() != image->origWidth() || + bitmap.height() != image->origHeight()) { + SkDebugf("--- BitmapImage::draw [%d %d] orig [%d %d]\n", + bitmap.width(), bitmap.height(), + image->origWidth(), image->origHeight()); + } +#endif +} + +void BitmapImage::setURL(const String& str) +{ + m_source.setURL(str); +} + +/////////////////////////////////////////////////////////////////////////////// + +void Image::drawPattern(GraphicsContext* ctxt, const FloatRect& tileRect, + const AffineTransform& patternTransform, + const FloatPoint& phase, CompositeOperator compositeOp, + const FloatRect& destRect) +{ + SkBitmapRef* image = this->nativeImageForCurrentFrame(); + if (!image) { // If it's too early we won't have an image yet. + return; + } + + // in case we get called with an incomplete bitmap + const SkBitmap& bitmap = image->bitmap(); + if (bitmap.getPixels() == NULL && bitmap.pixelRef() == NULL) { + return; + } + + SkRect dstR; + android_setrect(&dstR, destRect); + if (dstR.isEmpty()) { + return; + } + + SkCanvas* canvas = ctxt->platformContext()->mCanvas; + SkPaint paint; + + SkShader* shader = SkShader::CreateBitmapShader(bitmap, + SkShader::kRepeat_TileMode, + SkShader::kRepeat_TileMode); + paint.setShader(shader)->unref(); + // now paint is the only owner of shader + paint.setPorterDuffXfermode(android_convert_compositeOp(compositeOp)); + paint.setFilterBitmap(true); + + SkMatrix matrix(patternTransform); + + float scaleX = (float)image->origWidth() / bitmap.width(); + float scaleY = (float)image->origHeight() / bitmap.height(); + matrix.preScale(SkFloatToScalar(scaleX), SkFloatToScalar(scaleY)); + + matrix.postTranslate(SkFloatToScalar(phase.x()), + SkFloatToScalar(phase.y())); + shader->setLocalMatrix(matrix); + canvas->drawRect(dstR, paint); + +#ifdef TRACE_SUBSAMPLED_BITMAPS + if (bitmap.width() != image->origWidth() || + bitmap.height() != image->origHeight()) { + SkDebugf("--- Image::drawPattern [%d %d] orig [%d %d] dst [%g %g]\n", + bitmap.width(), bitmap.height(), + image->origWidth(), image->origHeight(), + SkScalarToFloat(dstR.width()), SkScalarToFloat(dstR.height())); + } +#endif +} + +// missingImage, textAreaResizeCorner +Image* Image::loadPlatformResource(const char *name) +{ + if (NULL == gGlobalAssetMgr) { + gGlobalAssetMgr = new android::AssetManager(); + gGlobalAssetMgr->addDefaultAssets(); + } + + SkString path("webkit/"); + path.append(name); + path.append(".png"); + + android::Asset* a = gGlobalAssetMgr->open(path.c_str(), + android::Asset::ACCESS_BUFFER); + if (a == NULL) { + SkDebugf("---------------- failed to open image asset %s\n", name); + return NULL; + } + + Image* image = new BitmapImage; + RefPtr<SharedBuffer> buffer = + new SharedBuffer((const char*)a->getBuffer(false), a->getLength()); + image->setData(buffer, true); + delete a; + return image; +} + +} diff --git a/WebCore/platform/graphics/android/ImageBufferAndroid.cpp b/WebCore/platform/graphics/android/ImageBufferAndroid.cpp new file mode 100644 index 0000000..65fb7cd --- /dev/null +++ b/WebCore/platform/graphics/android/ImageBufferAndroid.cpp @@ -0,0 +1,53 @@ +/* ImageBufferAndroid.cpp +** +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0 +** +** 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. +*/ + +#include "config.h" +#include "ImageBuffer.h" + +#include "GraphicsContext.h" + +using namespace std; + +namespace WebCore { + +auto_ptr<ImageBuffer> ImageBuffer::create(const IntSize& size, bool grayScale) +{ + // Ignore grayScale for now, since SkBitmap doesn't support it... yet + + GraphicsContext* ctx = GraphicsContext::createOffscreenContext(size.width(), size.height()); + + auto_ptr<GraphicsContext> context(ctx); + + return auto_ptr<ImageBuffer>(new ImageBuffer(size, context)); +} + + +ImageBuffer::ImageBuffer(const IntSize& size, auto_ptr<GraphicsContext> context) + : m_data(NULL), m_size(size), m_context(context.release()) +{ +} + +ImageBuffer::~ImageBuffer() +{ +} + +GraphicsContext* ImageBuffer::context() const +{ + return m_context.get(); +} + +} diff --git a/WebCore/platform/graphics/android/ImageSourceAndroid.cpp b/WebCore/platform/graphics/android/ImageSourceAndroid.cpp new file mode 100644 index 0000000..144968d --- /dev/null +++ b/WebCore/platform/graphics/android/ImageSourceAndroid.cpp @@ -0,0 +1,336 @@ +/* +** +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0 +** +** 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. +*/ + +#include "config.h" +#include "ImageDecoder.h" +#include "ImageSource.h" +#include "IntSize.h" +#include "SharedBuffer.h" +#include "PlatformString.h" + +#include "JavaSharedClient.h" + +#include "SkBitmapRef.h" +#include "SkImageRef.h" +#include "SkImageDecoder.h" +#include "SkStream.h" +#include "SkTemplates.h" + +SkPixelRef* SkCreateRLEPixelRef(const SkBitmap& src); + +//#define TRACE_SUBSAMPLE_BITMAPS +//#define TRACE_RLE_BITMAPS + +#include "SkImageRef_GlobalPool.h" +#include "SkImageRef_ashmem.h" + +// made this up, so we don't waste a file-descriptor on small images, plus +// we don't want to lose too much on the round-up to a page size (4K) +#define MIN_ASHMEM_ALLOC_SIZE (32*1024) + +static bool should_use_ashmem(const SkBitmap& bm) { + return bm.getSize() >= MIN_ASHMEM_ALLOC_SIZE; +} + +/* Images larger than this should be subsampled. Using ashmem, the decoded + pixels will be purged as needed, so this value can be pretty large. Making + it too small hurts image quality (e.g. abc.com background). 2Meg works for + the sites I've tested, but if we hit important sites that need more, we + should try increasing it and see if it has negative impact on performance + (i.e. we end up thrashing because we need to keep decoding images that have + been purged. + + Perhaps this value should be some fraction of the available RAM... +*/ +static size_t computeMaxBitmapSizeForCache() { + return 2*1024*1024; +} + +/* 8bit images larger than this should be recompressed in RLE, to reduce + on the imageref cache. + + Downside: performance, since we have to decode/encode + and then rle-decode when we draw. +*/ +static bool shouldReencodeAsRLE(const SkBitmap& bm) { + const SkBitmap::Config c = bm.config(); + return (SkBitmap::kIndex8_Config == c || SkBitmap::kA8_Config == c) + && + bm.width() >= 64 // narrower than this won't compress well in RLE + && + bm.getSize() > (250*1024); +} + +/////////////////////////////////////////////////////////////////////////////// + +class PrivateAndroidImageSourceRec : public SkBitmapRef { +public: + PrivateAndroidImageSourceRec(const SkBitmap& bm, int origWidth, + int origHeight, int sampleSize) + : SkBitmapRef(bm), fSampleSize(sampleSize), fAllDataReceived(false) { + this->setOrigSize(origWidth, origHeight); + } + + int fSampleSize; + bool fAllDataReceived; +}; + +namespace WebCore { + +class SharedBufferStream : public SkMemoryStream { +public: + SharedBufferStream(SharedBuffer* buffer) + : SkMemoryStream(buffer->data(), buffer->size(), false) { + fBuffer = buffer; + buffer->ref(); + } + + virtual ~SharedBufferStream() { + // we can't necessarily call fBuffer->deref() here, as we may be + // in a different thread from webkit, and SharedBuffer is not + // threadsafe. Therefore we defer it until it can be executed in the + // webkit thread. +// SkDebugf("-------- enqueue buffer %p for deref\n", fBuffer); + JavaSharedClient::EnqueueFunctionPtr(CallDeref, fBuffer); + } + +private: + // don't allow this to change our data. should not get called, but we + // override here just to be sure + virtual void setMemory(const void* data, size_t length, bool copyData) { + sk_throw(); + } + + // we share ownership of this with webkit + SharedBuffer* fBuffer; + + // will be invoked in the webkit thread + static void CallDeref(void* buffer) { +// SkDebugf("-------- call deref on buffer %p\n", buffer); + ((SharedBuffer*)buffer)->deref(); + } +}; + + +/////////////////////////////////////////////////////////////////////////////// + +ImageSource::ImageSource() { + m_decoder.m_image = NULL; +} + +ImageSource::~ImageSource() { + delete m_decoder.m_image; +} + +bool ImageSource::initialized() const { + return m_decoder.m_image != NULL; +} + +static int computeSampleSize(const SkBitmap& bitmap) { + const size_t maxSize = computeMaxBitmapSizeForCache(); + size_t size = bitmap.getSize(); + int sampleSize = 1; + + while (size > maxSize) { + sampleSize <<= 1; + size >>= 2; + } + +#ifdef TRACE_SUBSAMPLE_BITMAPS + if (sampleSize > 1) { + SkDebugf("------- bitmap [%d %d] config=%d origSize=%d predictSize=%d sampleSize=%d\n", + bitmap.width(), bitmap.height(), bitmap.config(), + bitmap.getSize(), size, sampleSize); + } +#endif + return sampleSize; +} + +static SkPixelRef* convertToRLE(SkBitmap* bm, const void* data, size_t len) { + if (!shouldReencodeAsRLE(*bm)) { + return NULL; + } + + SkBitmap tmp; + + if (!SkImageDecoder::DecodeMemory(data, len, &tmp) || !tmp.getPixels()) { + return NULL; + } + + SkPixelRef* ref = SkCreateRLEPixelRef(tmp); + if (NULL == ref) { + return NULL; + } + +#ifdef TRACE_RLE_BITMAPS + SkDebugf("---- reencode bitmap as RLE: [%d %d] encodeSize=%d\n", + tmp.width(), tmp.height(), len); +#endif + + bm->setConfig(SkBitmap::kRLE_Index8_Config, tmp.width(), tmp.height()); + return ref; +} + +void ImageSource::clearURL() +{ + m_decoder.m_url.reset(); +} + +void ImageSource::setURL(const String& url) +{ + if (url.startsWith("data:")) { + clearURL(); + } else { + m_decoder.m_url.setUTF16(url.characters(), url.length()); + } +} + +void ImageSource::setData(SharedBuffer* data, bool allDataReceived) +{ + if (NULL == m_decoder.m_image) { + SkBitmap tmp; + + SkMemoryStream stream(data->data(), data->size(), false); + SkImageDecoder* codec = SkImageDecoder::Factory(&stream); + SkAutoTDelete<SkImageDecoder> ad(codec); + + if (!codec || !codec->decode(&stream, &tmp, SkBitmap::kNo_Config, + SkImageDecoder::kDecodeBounds_Mode)) { + return; + } + + int origW = tmp.width(); + int origH = tmp.height(); + int sampleSize = computeSampleSize(tmp); + if (sampleSize > 1) { + codec->setSampleSize(sampleSize); + stream.rewind(); + if (!codec->decode(&stream, &tmp, SkBitmap::kNo_Config, + SkImageDecoder::kDecodeBounds_Mode)) { + return; + } + } + + m_decoder.m_image = new PrivateAndroidImageSourceRec(tmp, origW, origH, + sampleSize); + } + + PrivateAndroidImageSourceRec* decoder = m_decoder.m_image; + if (allDataReceived && !decoder->fAllDataReceived) { + decoder->fAllDataReceived = true; + + SkBitmap* bm = &decoder->bitmap(); + SkPixelRef* ref = convertToRLE(bm, data->data(), data->size()); + + if (NULL == ref) { + SkStream* strm = new SharedBufferStream(data); + // imageref now owns the stream object + if (should_use_ashmem(*bm)) { +// SkDebugf("---- use ashmem for image [%d %d]\n", bm.width(), bm.height()); + ref = new SkImageRef_ashmem(strm, bm->config(), decoder->fSampleSize); + } else { +// SkDebugf("---- use globalpool for image [%d %d]\n", bm.width(), bm.height()); + ref = new SkImageRef_GlobalPool(strm, bm->config(), decoder->fSampleSize); + } + + // SkDebugf("----- allDataReceived [%d %d]\n", bm->width(), bm->height()); + } + + // we promise to never change the pixels (makes picture recording fast) + ref->setImmutable(); + // give it the URL if we have one + ref->setURI(m_decoder.m_url); + // our bitmap is now the only owner of the imageref + bm->setPixelRef(ref)->unref(); + } +} + +bool ImageSource::isSizeAvailable() +{ + return m_decoder.m_image != NULL; +} + +IntSize ImageSource::size() const +{ + if (m_decoder.m_image) { + return IntSize(m_decoder.m_image->origWidth(), m_decoder.m_image->origHeight()); + } + return IntSize(0, 0); +} + +int ImageSource::repetitionCount() +{ + return 1; + // A property with value 0 means loop forever. +} + +size_t ImageSource::frameCount() const +{ + // i.e. 0 frames if we're not decoded, or 1 frame if we are + return m_decoder.m_image != NULL; +} + +SkBitmapRef* ImageSource::createFrameAtIndex(size_t index) +{ + SkASSERT(index == 0); + SkASSERT(m_decoder.m_image != NULL); + m_decoder.m_image->ref(); + return m_decoder.m_image; +} + +float ImageSource::frameDurationAtIndex(size_t index) +{ + SkASSERT(index == 0); + float duration = 0; + + // Many annoying ads specify a 0 duration to make an image flash as quickly as possible. + // We follow Firefox's behavior and use a duration of 100 ms for any frames that specify + // a duration of <= 10 ms. See gfxImageFrame::GetTimeout in Gecko or Radar 4051389 for more. + if (duration <= 0.010f) + duration = 0.100f; + return duration; +} + +bool ImageSource::frameHasAlphaAtIndex(size_t index) +{ + SkASSERT(0 == index); + + if (NULL == m_decoder.m_image) + return true; // if we're not sure, assume the worse-case + const PrivateAndroidImageSourceRec& decoder = *m_decoder.m_image; + // if we're 16bit, we know even without all the data available + if (decoder.bitmap().getConfig() == SkBitmap::kRGB_565_Config) + return false; + + if (!decoder.fAllDataReceived) + return true; // if we're not sure, assume the worse-case + + return !decoder.bitmap().isOpaque(); +} + +bool ImageSource::frameIsCompleteAtIndex(size_t index) +{ + SkASSERT(0 == index); + return m_decoder.m_image && m_decoder.m_image->fAllDataReceived; +} + +void ImageSource::clear() +{ + // do nothing, since the cache is managed elsewhere +} + +} diff --git a/WebCore/platform/graphics/android/PathAndroid.cpp b/WebCore/platform/graphics/android/PathAndroid.cpp new file mode 100644 index 0000000..68f4c0d --- /dev/null +++ b/WebCore/platform/graphics/android/PathAndroid.cpp @@ -0,0 +1,231 @@ +/* +** +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0 +** +** 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. +*/ + +#include "config.h" +#include "Path.h" +#include "FloatRect.h" +#include "AffineTransform.h" + +#include "SkPath.h" +#include "SkRegion.h" + +#include "android_graphics.h" + +namespace WebCore { + +Path::Path() +{ + m_path = new SkPath; +// m_path->setFlags(SkPath::kWinding_FillType); +} + +Path::Path(const Path& other) +{ + m_path = new SkPath(*other.m_path); +} + +Path::~Path() +{ + delete m_path; +} + +Path& Path::operator=(const Path& other) +{ + *m_path = *other.m_path; + return *this; +} + +bool Path::isEmpty() const +{ + return m_path->isEmpty(); +} + +bool Path::contains(const FloatPoint& point, WindRule rule) const +{ + SkRegion rgn, clip; + + int x = (int)floorf(point.x()); + int y = (int)floorf(point.y()); + clip.setRect(x, y, x + 1, y + 1); + + SkPath::FillType ft = m_path->getFillType(); // save + m_path->setFillType(rule == RULE_NONZERO ? SkPath::kWinding_FillType : SkPath::kEvenOdd_FillType); + + bool contains = rgn.setPath(*m_path, clip); + + m_path->setFillType(ft); // restore + return contains; +} + +void Path::translate(const FloatSize& size) +{ + m_path->offset(SkFloatToScalar(size.width()), SkFloatToScalar(size.height())); +} + +FloatRect Path::boundingRect() const +{ + SkRect r; + + m_path->computeBounds(&r, SkPath::kExact_BoundsType); + return FloatRect( SkScalarToFloat(r.fLeft), + SkScalarToFloat(r.fTop), + SkScalarToFloat(r.width()), + SkScalarToFloat(r.height())); +} + +void Path::moveTo(const FloatPoint& point) +{ + m_path->moveTo(SkFloatToScalar(point.x()), SkFloatToScalar(point.y())); +} + +void Path::addLineTo(const FloatPoint& p) +{ + m_path->lineTo(SkFloatToScalar(p.x()), SkFloatToScalar(p.y())); +} + +void Path::addQuadCurveTo(const FloatPoint& cp, const FloatPoint& ep) +{ + m_path->quadTo( SkFloatToScalar(cp.x()), SkFloatToScalar(cp.y()), + SkFloatToScalar(ep.x()), SkFloatToScalar(ep.y())); +} + +void Path::addBezierCurveTo(const FloatPoint& p1, const FloatPoint& p2, const FloatPoint& ep) +{ + m_path->cubicTo(SkFloatToScalar(p1.x()), SkFloatToScalar(p1.y()), + SkFloatToScalar(p2.x()), SkFloatToScalar(p2.y()), + SkFloatToScalar(ep.x()), SkFloatToScalar(ep.y())); +} + +void Path::addArcTo(const FloatPoint& p1, const FloatPoint& p2, float radius) +{ + m_path->arcTo(SkFloatToScalar(p1.x()), SkFloatToScalar(p1.y()), + SkFloatToScalar(p2.x()), SkFloatToScalar(p2.y()), + SkFloatToScalar(radius)); +} + +void Path::closeSubpath() +{ + m_path->close(); +} + +static const float gPI = 3.1415926f; + +void Path::addArc(const FloatPoint& p, float r, float sa, float ea, + bool clockwise) { + SkScalar cx = SkFloatToScalar(p.x()); + SkScalar cy = SkFloatToScalar(p.y()); + SkScalar radius = SkFloatToScalar(r); + + SkRect oval; + oval.set(cx - radius, cy - radius, cx + radius, cy + radius); + + float sweep = ea - sa; + // check for a circle + if (sweep >= 2*gPI || sweep <= -2*gPI) { + m_path->addOval(oval); + } else { + SkScalar startDegrees = SkFloatToScalar(sa * 180 / gPI); + SkScalar sweepDegrees = SkFloatToScalar(sweep * 180 / gPI); + + if (clockwise && sweepDegrees > 0) { + sweepDegrees -= SkIntToScalar(360); + } else if (!clockwise && sweepDegrees < 0) { + sweepDegrees = SkIntToScalar(360) - sweepDegrees; + } + +// SkDebugf("addArc sa=%g ea=%g cw=%d start=%g sweep=%g\n", sa, ea, clockwise, +// SkScalarToFloat(startDegrees), SkScalarToFloat(sweepDegrees)); + + m_path->arcTo(oval, startDegrees, sweepDegrees, false); + } +} + +void Path::addRect(const FloatRect& rect) +{ + SkRect r; + + android_setrect(&r, rect); + m_path->addRect(r); +} + +void Path::addEllipse(const FloatRect& rect) +{ + SkRect r; + + android_setrect(&r, rect); + m_path->addOval(r); +} + +void Path::clear() +{ + m_path->reset(); +} + +static FloatPoint* setfpts(FloatPoint dst[], const SkPoint src[], int count) +{ + for (int i = 0; i < count; i++) + { + dst[i].setX(SkScalarToFloat(src[i].fX)); + dst[i].setY(SkScalarToFloat(src[i].fY)); + } + return dst; +} + +void Path::apply(void* info, PathApplierFunction function) const +{ + SkPath::Iter iter(*m_path, false); + SkPoint pts[4]; + + PathElement elem; + FloatPoint fpts[3]; + + for (;;) + { + switch (iter.next(pts)) { + case SkPath::kMove_Verb: + elem.type = PathElementMoveToPoint; + elem.points = setfpts(fpts, &pts[0], 1); + break; + case SkPath::kLine_Verb: + elem.type = PathElementAddLineToPoint; + elem.points = setfpts(fpts, &pts[1], 1); + break; + case SkPath::kQuad_Verb: + elem.type = PathElementAddQuadCurveToPoint; + elem.points = setfpts(fpts, &pts[1], 2); + break; + case SkPath::kCubic_Verb: + elem.type = PathElementAddCurveToPoint; + elem.points = setfpts(fpts, &pts[1], 3); + break; + case SkPath::kClose_Verb: + elem.type = PathElementCloseSubpath; + elem.points = NULL; + break; + case SkPath::kDone_Verb: + return; + } + function(info, &elem); + } +} + +void Path::transform(const AffineTransform& xform) +{ + m_path->transform(xform); +} + +} diff --git a/WebCore/platform/graphics/android/PlatformGraphics.h b/WebCore/platform/graphics/android/PlatformGraphics.h new file mode 100644 index 0000000..6efdb43 --- /dev/null +++ b/WebCore/platform/graphics/android/PlatformGraphics.h @@ -0,0 +1,25 @@ +/* +** +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0 +** +** 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. +*/ + +#ifndef PlatformGraphics_d +#define PlatformGraphics_d + +typedef class SkShader PlatformGradient; +typedef class SkShader PlatformPattern; + +#endif + diff --git a/WebCore/platform/graphics/android/PlatformGraphicsContext.cpp b/WebCore/platform/graphics/android/PlatformGraphicsContext.cpp new file mode 100644 index 0000000..af443b8 --- /dev/null +++ b/WebCore/platform/graphics/android/PlatformGraphicsContext.cpp @@ -0,0 +1,73 @@ +/* + * + * Copyright 2006, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +#include "config.h" +#include "Node.h" +#include "PlatformGraphicsContext.h" +#include "SkCanvas.h" + +namespace WebCore { + +PlatformGraphicsContext::PlatformGraphicsContext(SkCanvas* canvas) : mCanvas(canvas), m_deleteCanvas(false) +{ + // This is useful only if this Canvas is part of an SkPicture object. + m_buttons = new SkTDArray<Container*>; +} + +PlatformGraphicsContext::PlatformGraphicsContext() : m_deleteCanvas(true) +{ + mCanvas = new SkCanvas; + // Since this is our own private SkCanvas, and has no relation to a picture + // storing button references would be meaningless. + m_buttons = NULL; +} + +PlatformGraphicsContext::~PlatformGraphicsContext() +{ + if (m_deleteCanvas) { +// printf("-------------------- deleting offscreen canvas\n"); + delete mCanvas; + } + if (m_buttons != NULL) { + m_buttons->deleteAll(); + delete m_buttons; + } +} + +SkTDArray<Container*>* PlatformGraphicsContext::getAndClearButtonInfo() +{ + // The caller is now responsible for deleting the array + SkTDArray<Container*>* buttons = m_buttons; + m_buttons = NULL; + return buttons; +} + +void PlatformGraphicsContext::storeButtonInfo(Node* node, const IntRect& r) +{ + if (m_buttons == NULL) + return; + // Initialize all of the nodes to have disabled state, so that we guarantee + // that we paint all of them the first time. + RenderSkinAndroid::State state = RenderSkinAndroid::kDisabled; + Container* container = new Container(node, r, state); + // Place a reference to our subpicture in the Picture. + mCanvas->drawPicture(*(container->picture())); + // Keep track of the information about the button. + *m_buttons->append() = container; +} + +} // WebCore diff --git a/WebCore/platform/graphics/android/PlatformGraphicsContext.h b/WebCore/platform/graphics/android/PlatformGraphicsContext.h new file mode 100644 index 0000000..a2d7ebe --- /dev/null +++ b/WebCore/platform/graphics/android/PlatformGraphicsContext.h @@ -0,0 +1,108 @@ +/* + * + * Copyright 2006, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +#ifndef platform_graphics_context_h +#define platform_graphics_context_h + +#include "IntRect.h" +#include "RenderSkinAndroid.h" +#include "RenderSkinButton.h" +#include "SkCanvas.h" +#include "SkPicture.h" +#include "SkTDArray.h" + +class SkCanvas; +class WebCore::Node; + +class Container { +public: + Container(WebCore::Node* node, const WebCore::IntRect& r, + WebCore::RenderSkinAndroid::State is) + : m_node(node), m_rect(r), m_state(is) + { + m_picture = new SkPicture; + } + + ~Container() + { + m_picture->unref(); + } + + bool matches(WebCore::Node* match) { return m_node == match; } + + // Provide a pointer to our SkPicture. + SkPicture* picture() { return m_picture; } + + // Update the focus state of this button, depending on whether it + // corresponds to the focused node passed in. If its state has changed, + // re-record to the subpicture, so the master picture will reflect the + // change. + void updateFocusState(WebCore::Node* focus) + { + WebCore::RenderSkinAndroid::State state = m_node == focus ? + WebCore::RenderSkinAndroid::kFocused : WebCore::RenderSkinAndroid::kNormal; + if (state == m_state) + return; + m_state = state; + SkCanvas* canvas = m_picture->beginRecording(m_rect.width(), m_rect.height()); + WebCore::RenderSkinButton::Draw(canvas, m_rect, state); + m_picture->endRecording(); + } +private: + // Mark copy and assignment private so noone can use them. + Container& operator=(const Container& src) { return *this; } + Container(const Container& src) { } + // Only used for comparison, since after it is stored it will be transferred + // to the UI thread. + WebCore::Node* m_node; + // The rectangle representing the bounds of the button. + WebCore::IntRect m_rect; + // An SkPicture that, thanks to storeButtonInfo, is pointed to by the master + // picture, so that we can rerecord this button without rerecording the + // world. + SkPicture* m_picture; + // The state of the button - Currently kFocused or kNormal (and kDisabled + // as an initial value), but could be expanded to include other states. + WebCore::RenderSkinAndroid::State m_state; +}; + +namespace WebCore { + +class PlatformGraphicsContext { +public: + PlatformGraphicsContext(); + PlatformGraphicsContext(SkCanvas* canvas); + ~PlatformGraphicsContext(); + + SkCanvas* mCanvas; + + bool deleteUs() const { return m_deleteCanvas; } + // If our graphicscontext has a button list, add a new container for the + // nod/rect, and record a new subpicture for this node/button in the current + // mCanvas + void storeButtonInfo(Node* node, const IntRect& r); + // Detaches button array (if any), returning it to the caller and setting our + // internal ptr to NULL + SkTDArray<Container*>* getAndClearButtonInfo(); +private: + bool m_deleteCanvas; + SkTDArray<Container*>* m_buttons; +}; + +} +#endif + diff --git a/WebCore/platform/graphics/android/SkBitmapRef.h b/WebCore/platform/graphics/android/SkBitmapRef.h new file mode 100644 index 0000000..77afcf3 --- /dev/null +++ b/WebCore/platform/graphics/android/SkBitmapRef.h @@ -0,0 +1,54 @@ +/* include/graphics/SkBitmapRef.h +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0 +** +** 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. +*/ + +#ifndef SkBitmapRef_DEFINED +#define SkBitmapRef_DEFINED + +#include "SkRefCnt.h" +#include "SkBitmap.h" + +class SkBitmapRef : public SkRefCnt { +public: + SkBitmapRef() : fOrigWidth(0), fOrigHeight(0), fAccessed(false) {} + explicit SkBitmapRef(const SkBitmap& src) + : fBitmap(src), + fOrigWidth(src.width()), + fOrigHeight(src.height()), + fAccessed(false) {} + + const SkBitmap& bitmap() const { return fBitmap; } + SkBitmap& bitmap() { return fBitmap; } + + int origWidth() const { return fOrigWidth; } + int origHeight() const { return fOrigHeight; } + + void setOrigSize(int width, int height) { + fOrigWidth = width; + fOrigHeight = height; + } + // return true if this is not the first access + // mark it true so all subsequent calls return true + bool accessed() { bool result = fAccessed; + fAccessed = true; return result; } + +private: + SkBitmap fBitmap; + int fOrigWidth, fOrigHeight; + bool fAccessed; +}; + +#endif diff --git a/WebCore/platform/graphics/android/android_graphics.cpp b/WebCore/platform/graphics/android/android_graphics.cpp new file mode 100644 index 0000000..3964ee1 --- /dev/null +++ b/WebCore/platform/graphics/android/android_graphics.cpp @@ -0,0 +1,223 @@ +/* + * + * Copyright 2007, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +#include "config.h" +#include "android_graphics.h" +#include "IntPoint.h" +#include "IntRect.h" +#include "FloatPoint.h" +#include "FloatRect.h" +#include "SkCanvas.h" +#include "SkColorPriv.h" +#include "SkCornerPathEffect.h" +#include "SkGradientShader.h" +#include "SkPath.h" +#include "SkRegion.h" + +SkPoint* android_setpt(SkPoint* dst, const WebCore::IntPoint& src) +{ + dst->set(SkIntToScalar(src.x()), SkIntToScalar(src.y())); + return dst; +} + +SkPoint* android_setpt(SkPoint* dst, const WebCore::FloatPoint& src) +{ + dst->set(SkFloatToScalar(src.x()), SkFloatToScalar(src.y())); + return dst; +} + +SkRect* android_setrect(SkRect* dst, const WebCore::IntRect& src) +{ + dst->set(SkIntToScalar(src.x()), + SkIntToScalar(src.y()), + SkIntToScalar(src.x() + src.width()), + SkIntToScalar(src.y() + src.height())); + return dst; +} + +SkIRect* android_setrect(SkIRect* dst, const WebCore::IntRect& src) +{ + dst->set(src.x(), src.y(), + src.x() + src.width(), + src.y() + src.height()); + return dst; +} + +SkRect* android_setrect(SkRect* dst, const WebCore::FloatRect& src) +{ + dst->set(SkFloatToScalar(src.x()), + SkFloatToScalar(src.y()), + SkFloatToScalar(src.x() + src.width()), + SkFloatToScalar(src.y() + src.height())); + return dst; +} + +SkIRect* android_setrect(SkIRect* dst, const WebCore::FloatRect& src) +{ + dst->set(SkScalarRound(SkFloatToScalar(src.x())), + SkScalarRound(SkFloatToScalar(src.y())), + SkScalarRound(SkFloatToScalar(src.x() + src.width())), + SkScalarRound(SkFloatToScalar(src.y() + src.height()))); + return dst; +} + +SkIRect* android_setrect_scaled(SkIRect* dst, const WebCore::FloatRect& src, + float sx, float sy) +{ + dst->set(SkScalarRound(SkFloatToScalar(src.x() * sx)), + SkScalarRound(SkFloatToScalar(src.y() * sy)), + SkScalarRound(SkFloatToScalar((src.x() + src.width()) * sx)), + SkScalarRound(SkFloatToScalar((src.y() + src.height()) * sy))); + return dst; +} + +static const struct CompositOpToPorterDuffMode { + uint8_t mCompositOp; + uint8_t mPorterDuffMode; +} gMapCompositOpsToPorterDuffModes[] = { + { WebCore::CompositeClear, SkPorterDuff::kClear_Mode }, + { WebCore::CompositeCopy, SkPorterDuff::kSrcOver_Mode }, // TODO + { WebCore::CompositeSourceOver, SkPorterDuff::kSrcOver_Mode }, + { WebCore::CompositeSourceIn, SkPorterDuff::kSrcIn_Mode }, + { WebCore::CompositeSourceOut, SkPorterDuff::kSrcOut_Mode }, + { WebCore::CompositeSourceAtop, SkPorterDuff::kSrcATop_Mode }, + { WebCore::CompositeDestinationOver, SkPorterDuff::kDstOver_Mode }, + { WebCore::CompositeDestinationIn, SkPorterDuff::kDstIn_Mode }, + { WebCore::CompositeDestinationOut, SkPorterDuff::kDstOut_Mode }, + { WebCore::CompositeDestinationAtop, SkPorterDuff::kDstATop_Mode }, + { WebCore::CompositeXOR, SkPorterDuff::kXor_Mode }, + { WebCore::CompositePlusDarker, SkPorterDuff::kDarken_Mode }, + { WebCore::CompositeHighlight, SkPorterDuff::kSrcOver_Mode }, // TODO + { WebCore::CompositePlusLighter, SkPorterDuff::kLighten_Mode } +}; + +SkPorterDuff::Mode android_convert_compositeOp(WebCore::CompositeOperator op) +{ + const CompositOpToPorterDuffMode* table = gMapCompositOpsToPorterDuffModes; + + for (unsigned i = 0; i < SK_ARRAY_COUNT(gMapCompositOpsToPorterDuffModes); i++) { + if (table[i].mCompositOp == op) { + return (SkPorterDuff::Mode)table[i].mPorterDuffMode; + } + } + + SkDEBUGF(("GraphicsContext::setCompositeOperation uknown CompositOperator %d\n", op)); + return SkPorterDuff::kSrcOver_Mode; // fall-back +} + +SkShader::TileMode android_convert_TileRule(WebCore::Image::TileRule rule) +{ + // stretch == clamp + // repeat == repeat + // RoundTile??? + + return WebCore::Image::RepeatTile == rule ? SkShader::kRepeat_TileMode : SkShader::kClamp_TileMode; +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////// + +static U8CPU InvScaleByte(U8CPU component, uint32_t scale) +{ + SkASSERT(component == (uint8_t)component); + return (component * scale + 0x8000) >> 16; +} + +// move this guy into SkColor.h +static SkColor SkPMColorToColor(SkPMColor pm) +{ + if (0 == pm) + return 0; + + unsigned a = SkGetPackedA32(pm); + uint32_t scale = (255 << 16) / a; + + return SkColorSetARGB(a, + InvScaleByte(SkGetPackedR32(pm), scale), + InvScaleByte(SkGetPackedG32(pm), scale), + InvScaleByte(SkGetPackedB32(pm), scale)); +} + +WebCore::Color android_SkPMColorToWebCoreColor(SkPMColor pm) +{ + SkColor c = SkPMColorToColor(pm); + + return WebCore::Color(SkColorGetR(c), SkColorGetG(c), SkColorGetB(c), SkColorGetA(c)); +} + +const static SkColor focusOuterColors[] = { + SkColorSetARGB(0xff, 0xB3, 0x3F, 0x08), // normal focus ring select + SkColorSetARGB(0xff, 0x46, 0xb0, 0x00), // fake focus ring select, for phone, email, text + SkColorSetARGB(0x00, 0x00, 0x00, 0x00), // no ring, for buttons + SkColorSetARGB(0xff, 0xb0, 0x16, 0x00), // invalid focus ring color + SkColorSetARGB(0xff, 0xAD, 0x5C, 0x0A), // normal focus ring pressed + SkColorSetARGB(0xff, 0x36, 0xc0, 0x00) // fake focus ring pressed +}; + +const static SkColor focusInnerColors[] = { + SkColorSetARGB(0xff, 0xFE, 0x92, 0x30), // normal focus ring select + SkColorSetARGB(0xff, 0x8c, 0xd9, 0x00), // fake focus ring select, for phone, email, text + SkColorSetARGB(0x00, 0x00, 0x00, 0x00), // no ring, for buttons + SkColorSetARGB(0xff, 0xd9, 0x2c, 0x00), // invalid focus ring color + SkColorSetARGB(0xff, 0xFE, 0xBD, 0x3A), // normal focus ring pressed + SkColorSetARGB(0xff, 0x7c, 0xe9, 0x00) // fake focus ring pressed +}; + +const static SkColor focusPressedColors[] = { + SkColorSetARGB(0x80, 0xFF, 0xC6, 0x4B), // normal focus ring pressed + SkColorSetARGB(0x80, 0x7c, 0xe9, 0x00), // fake focus ring pressed + SkColorSetARGB(0x80, 0xFF, 0xC6, 0x4B) // button focus ring pressed +}; + +#define FOCUS_RING_ROUNDEDNESS SkIntToScalar(5) // used to draw corners +#define FOCUS_RING_INNER_DIAMETER SkFixedToScalar(SkIntToFixed(3)>>1) // 3/2 == 1.5 +#define FOCUS_RING_OUTER_OUTSET 2 // used to inflate rects added to region + +void FocusRing::DrawRing(SkCanvas* canvas, + const Vector<WebCore::IntRect>& rects, Flavor flavor) +{ + unsigned rectCount = rects.size(); + SkRegion rgn; + SkPath path; + for (unsigned i = 0; i < rectCount; i++) + { + SkIRect r; + + android_setrect(&r, rects[i]); + r.inset(-FOCUS_RING_OUTER_OUTSET, -FOCUS_RING_OUTER_OUTSET); + rgn.op(r, SkRegion::kUnion_Op); + } + rgn.getBoundaryPath(&path); + + SkPaint paint; + paint.setAntiAlias(true); + paint.setPathEffect(new SkCornerPathEffect(FOCUS_RING_ROUNDEDNESS))->unref(); + if (flavor >= NORMAL_ANIMATING) { // pressed + paint.setColor(focusPressedColors[flavor - NORMAL_ANIMATING]); + canvas->drawPath(path, paint); + } + if (flavor == BUTTON_ANIMATING) + return; + paint.setStyle(SkPaint::kStroke_Style); + paint.setStrokeWidth(FOCUS_RING_OUTER_DIAMETER); + paint.setColor(focusOuterColors[flavor]); + canvas->drawPath(path, paint); + paint.setStrokeWidth(FOCUS_RING_INNER_DIAMETER); + paint.setColor(focusInnerColors[flavor]); + canvas->drawPath(path, paint); +} + + diff --git a/WebCore/platform/graphics/android/android_graphics.h b/WebCore/platform/graphics/android/android_graphics.h new file mode 100644 index 0000000..91c56b7 --- /dev/null +++ b/WebCore/platform/graphics/android/android_graphics.h @@ -0,0 +1,80 @@ +/* + * + * Copyright 2007, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +#ifndef android_graphics_DEFINED +#define android_graphics_DEFINED + +#include "Color.h" +#include "Image.h" +#include "wtf/Vector.h" + +#include "SkColor.h" +#include "SkPorterDuff.h" +#include "SkScalar.h" +#include "SkShader.h" + +class SkCanvas; +struct SkPoint; +struct SKRect; + +namespace WebCore { + class FloatRect; + class IntPoint; + class IntRect; +} + +SkPoint* android_setpt(SkPoint* dst, const WebCore::IntPoint& src); +SkPoint* android_setpt(SkPoint* dst, const WebCore::FloatPoint& src); +SkRect* android_setrect(SkRect* dst, const WebCore::IntRect& src); +SkRect* android_setrect(SkRect* dst, const WebCore::FloatRect& src); +SkIRect* android_setrect(SkIRect* dst, const WebCore::IntRect& src); +SkIRect* android_setrect(SkIRect* dst, const WebCore::FloatRect& src); +SkIRect* android_setrect_scaled(SkIRect* dst, const WebCore::FloatRect& src, + float sx, float sy); + +SkPorterDuff::Mode android_convert_compositeOp(WebCore::CompositeOperator); +SkShader::TileMode android_convert_TileRule(WebCore::Image::TileRule); + +WebCore::Color android_SkPMColorToWebCoreColor(SkPMColor pm); + +// Data and methods for focus rings + +// used to inflate node cache entry +#define FOCUS_RING_HIT_TEST_RADIUS SkIntToScalar(5) + +// used to inval rectangle enclosing pressed state of focus ring +#define FOCUS_RING_OUTER_DIAMETER SkFixedToScalar(SkIntToFixed(13)>>2) // 13/4 == 3.25 + +struct FocusRing { +public: + enum Flavor { + NORMAL_FLAVOR, + FAKE_FLAVOR, + BUTTON_NO_RING, + INVALID_FLAVOR, + NORMAL_ANIMATING, + FAKE_ANIMATING, + BUTTON_ANIMATING, + ANIMATING_COUNT = 2 + }; + + static void DrawRing(SkCanvas* , + const Vector<WebCore::IntRect>& rects, Flavor ); +}; + +#endif + |