diff options
Diffstat (limited to 'WebCore/platform/graphics/android')
22 files changed, 4230 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..c01d078 --- /dev/null +++ b/WebCore/platform/graphics/android/AffineTransformAndroid.cpp @@ -0,0 +1,218 @@ +/* + * Copyright 2007, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 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 "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(const SkMatrix& mat) : m_transform(mat) {} + +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(); +} + + double AffineTransform::a() const { + return SkScalarToDouble(m_transform[0]); + } + void AffineTransform::setA(double a) { + m_transform.set(0, SkDoubleToScalar(a)); + } + + double AffineTransform::b() const { + return SkScalarToDouble(m_transform[1]); + } + void AffineTransform::setB(double b) { + m_transform.set(1, SkDoubleToScalar(b)); + } + + double AffineTransform::c() const { + return SkScalarToDouble(m_transform[3]); + } + void AffineTransform::setC(double c) { + m_transform.set(3, SkDoubleToScalar(c)); + } + + double AffineTransform::d() const { + return SkScalarToDouble(m_transform[4]); + } + void AffineTransform::setD(double d) { + m_transform.set(4, SkDoubleToScalar(d)); + } + + double AffineTransform::e() const { + return SkScalarToDouble(m_transform[2]); + } + void AffineTransform::setE(double e) { + m_transform.set(2, SkDoubleToScalar(e)); + } + + double AffineTransform::f() const { + return SkScalarToDouble(m_transform[5]); + } + void AffineTransform::setF(double f) { + m_transform.set(5, SkDoubleToScalar(f)); + } + +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) +{ + m_transform.setConcat(m2.m_transform, m_transform); + return *this; +} + +AffineTransform AffineTransform::operator* (const AffineTransform &m2) +{ + AffineTransform cat; + + cat.m_transform.setConcat(m2.m_transform, 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..54a1a08 --- /dev/null +++ b/WebCore/platform/graphics/android/FontAndroid.cpp @@ -0,0 +1,208 @@ +/* + * Copyright 2009, The Android Open Source Project + * 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 "SkLayerDrawLooper.h" +#include "SkPaint.h" +#include "SkTemplates.h" +#include "SkTypeface.h" +#include "SkUtils.h" + +namespace WebCore { + +static void updateForFont(SkPaint* paint, const SimpleFontData* font) { + font->platformData().setupPaint(paint); + paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding); +} + +static SkPaint* setupFill(SkPaint* paint, GraphicsContext* gc, + const SimpleFontData* font) { + gc->setupFillPaint(paint); + updateForFont(paint, font); + return paint; +} + +static SkPaint* setupStroke(SkPaint* paint, GraphicsContext* gc, + const SimpleFontData* font) { + gc->setupStrokePaint(paint); + updateForFont(paint, font); + return paint; +} + +static bool setupForText(SkPaint* paint, GraphicsContext* gc, + const SimpleFontData* font) { + int mode = gc->textDrawingMode(); + + if ((mode & (cTextFill | cTextStroke)) == (cTextFill | cTextStroke)) { + SkLayerDrawLooper* looper = new SkLayerDrawLooper; + paint->setLooper(looper)->unref(); + + // we clear the looper, in case we have a shadow + + SkPaint* fillP = NULL; + SkPaint* strokeP = NULL; + if (gc->willStroke()) { + strokeP = setupStroke(looper->addLayer(), gc, font); + strokeP->setLooper(NULL); + } + if (gc->willFill()) { + fillP = setupFill(looper->addLayer(), gc, font); + fillP->setLooper(NULL); + } + + SkPaint shadowPaint; + SkPoint offset; + if (gc->setupShadowPaint(&shadowPaint, &offset)) { + SkPaint* p = looper->addLayer(offset.fX, offset.fY); + *p = shadowPaint; + if (strokeP && !fillP) { + // stroke the shadow if we have stroke but no fill + p->setStyle(SkPaint::kStroke_Style); + p->setStrokeWidth(strokeP->getStrokeWidth()); + } + updateForFont(p, font); + } + } else if (mode & cTextFill) { + (void)setupFill(paint, gc, font); + } else if (mode & cTextStroke) { + (void)setupStroke(paint, gc, font); + } else { + return false; + } + return true; +} + +void Font::drawGlyphs(GraphicsContext* gc, const SimpleFontData* font, + const GlyphBuffer& glyphBuffer, int from, int numGlyphs, + const FloatPoint& point) const { + SkASSERT(sizeof(GlyphBufferGlyph) == sizeof(uint16_t)); // compile-time assert + + SkPaint paint; + if (!setupForText(&paint, gc, font)) { + return; + } + + SkScalar x = SkFloatToScalar(point.x()); + SkScalar y = SkFloatToScalar(point.y()); + const GlyphBufferGlyph* glyphs = glyphBuffer.glyphs(from); + const GlyphBufferAdvance* adv = glyphBuffer.advances(from); + SkAutoSTMalloc<32, SkPoint> storage(numGlyphs); + SkPoint* pos = storage.get(); + + /* We need an array of [x,y,x,y,x,y,...], but webkit is giving us + point.xy + [width, height, width, height, ...], so we have to convert + */ + for (int i = 0; i < numGlyphs; i++) { + pos[i].set(x, y); + x += SkFloatToScalar(adv[i].width()); + y += SkFloatToScalar(adv[i].height()); + } + + SkCanvas* canvas = gc->platformContext()->mCanvas; + canvas->drawPosText(glyphs, numGlyphs * sizeof(*glyphs), pos, 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; + + if (!setupForText(&paint, gc, primaryFont())) { + return; + } + + // go to chars, instead of glyphs, which was set by setupForText() + paint.setTextEncoding(SkPaint::kUTF16_TextEncoding); + + 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..c257348 --- /dev/null +++ b/WebCore/platform/graphics/android/FontCacheAndroid.cpp @@ -0,0 +1,134 @@ +/* + * Copyright 2009, The Android Open Source Project + * 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 "NotImplemented.h" +#include "SimpleFontData.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) +{ + // since all of our fonts logically map to the fallback, we can always claim + // that each font supports all characters. + return font.primaryFont(); +} + +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; +} + +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() >= FontWeightBold) + 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; +} + + // new as of SVN change 36269, Sept 8, 2008 +void FontCache::getTraitsInFamily(const AtomicString& familyName, Vector<unsigned>& traitsMasks) +{ + // Don't understand this yet, but it seems safe to leave unimplemented +} + +} + diff --git a/WebCore/platform/graphics/android/FontCustomPlatformData.cpp b/WebCore/platform/graphics/android/FontCustomPlatformData.cpp new file mode 100644 index 0000000..eb1933c --- /dev/null +++ b/WebCore/platform/graphics/android/FontCustomPlatformData.cpp @@ -0,0 +1,78 @@ +/* + * Copyright 2008, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 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 "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, + FontRenderingMode mode) +{ + // 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..e45d509 --- /dev/null +++ b/WebCore/platform/graphics/android/FontCustomPlatformData.h @@ -0,0 +1,57 @@ +/* + * Copyright 2008, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 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. + */ + +#ifndef FontCustomPlatformData_h_ +#define FontCustomPlatformData_h_ + +#include <wtf/Noncopyable.h> +#include "FontRenderingMode.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, FontRenderingMode ); + + 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..3dd9789 --- /dev/null +++ b/WebCore/platform/graphics/android/FontDataAndroid.cpp @@ -0,0 +1,122 @@ +/* + * Copyright 2009, The Android Open Source Project + * 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..2bb8834 --- /dev/null +++ b/WebCore/platform/graphics/android/FontPlatformData.h @@ -0,0 +1,72 @@ +/* + * 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 2009, The Android Open Source Project + * 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 + +#include "StringImpl.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(WTF::HashTableDeletedValueType) + : mTypeface(hashTableDeletedFontValue()) { } + bool isHashTableDeletedValue() const { + return mTypeface == hashTableDeletedFontValue(); + } + + 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; + + static SkTypeface* hashTableDeletedFontValue() { + return reinterpret_cast<SkTypeface*>(-1); + } +}; + +} /* namespace */ + +#endif diff --git a/WebCore/platform/graphics/android/FontPlatformDataAndroid.cpp b/WebCore/platform/graphics/android/FontPlatformDataAndroid.cpp new file mode 100644 index 0000000..e82c1f6 --- /dev/null +++ b/WebCore/platform/graphics/android/FontPlatformDataAndroid.cpp @@ -0,0 +1,176 @@ +/* + * 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 2009, The Android Open Source Project + * 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) +{ + if (hashTableDeletedFontValue() != src.mTypeface) { + 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) +{ + if (hashTableDeletedFontValue() != mTypeface) { + mTypeface->safeRef(); + } + + inc_count(); + trace(3); +} + +FontPlatformData::FontPlatformData(const FontPlatformData& src, float textSize) + : mTypeface(src.mTypeface), mTextSize(textSize), mFakeBold(src.mFakeBold), mFakeItalic(src.mFakeItalic) +{ + if (hashTableDeletedFontValue() != mTypeface) { + mTypeface->safeRef(); + } + + inc_count(); + trace(4); +} + +FontPlatformData::~FontPlatformData() +{ + dec_count(); +#ifdef TRACE_FONTPLATFORMDATA_LIFE + SkDebugf("----------- ~FontPlatformData\n"); +#endif + + if (hashTableDeletedFontValue() != mTypeface) { + mTypeface->safeUnref(); + } +} + +FontPlatformData& FontPlatformData::operator=(const FontPlatformData& src) +{ + if (hashTableDeletedFontValue() != src.mTypeface) { + src.mTypeface->safeRef(); + } + if (hashTableDeletedFontValue() != mTypeface) { + mTypeface->safeUnref(); + } + + 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 mTypeface == a.mTypeface && + mTextSize == a.mTextSize && + mFakeBold == a.mFakeBold && + mFakeItalic == a.mFakeItalic; +} + +unsigned FontPlatformData::hash() const +{ + unsigned h; + + if (hashTableDeletedFontValue() == mTypeface) { + h = reinterpret_cast<unsigned>(mTypeface); + } else { + h = SkTypeface::UniqueID(mTypeface); + } + + uint32_t sizeAsInt = *reinterpret_cast<const uint32_t*>(&mTextSize); + + h ^= 0x01010101 * (((int)mFakeBold << 1) | (int)mFakeItalic); + h ^= sizeAsInt; + return h; +} + +} + diff --git a/WebCore/platform/graphics/android/GlyphMapAndroid.cpp b/WebCore/platform/graphics/android/GlyphMapAndroid.cpp new file mode 100644 index 0000000..1df06b1 --- /dev/null +++ b/WebCore/platform/graphics/android/GlyphMapAndroid.cpp @@ -0,0 +1,70 @@ +/* + * Copyright 2009, The Android Open Source Project + * 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/GradientAndroid.cpp b/WebCore/platform/graphics/android/GradientAndroid.cpp new file mode 100644 index 0000000..71b7f73 --- /dev/null +++ b/WebCore/platform/graphics/android/GradientAndroid.cpp @@ -0,0 +1,116 @@ +/* + * Copyright 2006, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 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 "Gradient.h" + +#include "android_graphics.h" +#include "CSSParser.h" +#include "GraphicsContext.h" +#include "NotImplemented.h" +#include "SkCanvas.h" +#include "SkColorShader.h" +#include "SkGradientShader.h" +#include "SkPaint.h" + +class PlatformGradientRec { +public: + PlatformGradientRec() : m_shader(NULL) {} + ~PlatformGradientRec() { m_shader->safeUnref(); } + + SkShader* m_shader; + SkShader::TileMode m_tileMode; +}; + +namespace WebCore { + +void Gradient::platformDestroy() +{ + delete m_gradient; + m_gradient = 0; +} + +static U8CPU F2B(float x) +{ + return (int)(x * 255); +} + +SkShader* Gradient::getShader(SkShader::TileMode mode) { + if (NULL == m_gradient) { + m_gradient = new PlatformGradientRec; + } else if (m_gradient->m_tileMode == mode) { + return m_gradient->m_shader; + } + + SkPoint pts[2]; + + android_setpt(&pts[0], m_p0); + android_setpt(&pts[1], m_p1); + size_t count = m_stops.size(); + SkAutoMalloc storage(count * (sizeof(SkColor) + sizeof(SkScalar))); + SkColor* colors = (SkColor*)storage.get(); + SkScalar* pos = (SkScalar*)(colors + count); + + Vector<ColorStop>::iterator iter = m_stops.begin(); + int i = -1; + while (i++, iter != m_stops.end()) { + pos[i] = SkFloatToScalar(iter->stop); + colors[i] = SkColorSetARGB(F2B(iter->alpha), F2B(iter->red), + F2B(iter->green), F2B(iter->blue)); + ++iter; + } + + SkShader* s; + if (0 == count) { + // it seems the spec says a zero-size gradient draws transparent + s = new SkColorShader(0); + } else if (m_radial) { + s = SkGradientShader::CreateRadial(pts[0], SkFloatToScalar(m_r0), + colors, pos, count, mode); + } else { + s = SkGradientShader::CreateLinear(pts, colors, pos, count, mode); + } + + m_gradient->m_shader->safeUnref(); + m_gradient->m_shader = s; + m_gradient->m_tileMode = mode; + return s; +} + +void Gradient::fill(GraphicsContext* context, const FloatRect& rect) +{ + SkRect r; + SkPaint paint; + // we don't care about the mode, so try to use the existing one + SkShader::TileMode mode = m_gradient ? m_gradient->m_tileMode : + SkShader::kClamp_TileMode; + + paint.setAntiAlias(true); + paint.setShader(this->getShader(mode)); + android_gc2canvas(context)->drawRect(*android_setrect(&r, rect), paint); +} + + +} //namespace diff --git a/WebCore/platform/graphics/android/GraphicsContextAndroid.cpp b/WebCore/platform/graphics/android/GraphicsContextAndroid.cpp new file mode 100644 index 0000000..c2e0f02 --- /dev/null +++ b/WebCore/platform/graphics/android/GraphicsContextAndroid.cpp @@ -0,0 +1,1118 @@ +/* + * Copyright 2006, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 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 "Gradient.h" +#include "GraphicsContext.h" +#include "GraphicsContextPrivate.h" +#include "NotImplemented.h" +#include "Path.h" +#include "Pattern.h" + +#include "SkBlurDrawLooper.h" +#include "SkBlurMaskFilter.h" +#include "SkCanvas.h" +#include "SkColorPriv.h" +#include "SkDashPathEffect.h" +#include "SkDevice.h" +#include "SkPaint.h" +#include "SkPorterDuff.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); +} + +template <typename T> T* deepCopyPtr(const T* src) { + return src ? new T(*src) : NULL; +} + +/* 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() +*/ + +struct ShadowRec { + SkScalar mBlur; // >0 means valid shadow + SkScalar mDx; + SkScalar mDy; + SkColor mColor; +}; + +class GraphicsContextPlatformPrivate { +public: + GraphicsContext* mCG; // back-ptr to our parent + PlatformGraphicsContext* mPgc; + + struct State { + SkPath* mPath; + float mMiterLimit; + float mAlpha; + float mStrokeThickness; + SkPaint::Cap mLineCap; + SkPaint::Join mLineJoin; + SkPorterDuff::Mode mPorterDuffMode; + int mDashRatio; //ratio of the length of a dash to its width + ShadowRec mShadow; + SkColor mFillColor; + SkColor mStrokeColor; + bool mUseAA; + + State() { + mPath = NULL; // lazily allocated + mMiterLimit = 4; + mAlpha = 1; + mStrokeThickness = 0.0f; // Same as default in GraphicsContextPrivate.h + mLineCap = SkPaint::kDefault_Cap; + mLineJoin = SkPaint::kDefault_Join; + mPorterDuffMode = SkPorterDuff::kSrcOver_Mode; + mDashRatio = 3; + mUseAA = true; + mShadow.mBlur = 0; + mFillColor = SK_ColorBLACK; + mStrokeColor = SK_ColorBLACK; + } + + State(const State& other) { + memcpy(this, &other, sizeof(State)); + mPath = deepCopyPtr<SkPath>(other.mPath); + } + + ~State() { + delete mPath; + } + + void setShadow(int radius, int dx, int dy, SkColor c) { + // cut the radius in half, to visually match the effect seen in + // safari browser + mShadow.mBlur = SkScalarHalf(SkIntToScalar(radius)); + mShadow.mDx = SkIntToScalar(dx); + mShadow.mDy = SkIntToScalar(dy); + mShadow.mColor = c; + } + + bool setupShadowPaint(SkPaint* paint, SkPoint* offset) { + if (mShadow.mBlur > 0) { + paint->setAntiAlias(true); + paint->setDither(true); + paint->setPorterDuffXfermode(mPorterDuffMode); + paint->setColor(mShadow.mColor); + paint->setMaskFilter(SkBlurMaskFilter::Create(mShadow.mBlur, + SkBlurMaskFilter::kNormal_BlurStyle))->unref(); + offset->set(mShadow.mDx, mShadow.mDy); + return true; + } + return false; + } + + 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 setFillColor(const Color& c) { + mState->mFillColor = c.rgb(); + } + + void setStrokeColor(const Color& c) { + mState->mStrokeColor = c.rgb(); + } + + void setStrokeThickness(float f) { + mState->mStrokeThickness = f; + } + + void beginPath() { + if (mState->mPath) { + mState->mPath->reset(); + } + } + + void addPath(const SkPath& other) { + if (!mState->mPath) { + mState->mPath = new SkPath(other); + } else { + mState->mPath->addPath(other); + } + } + + // may return null + SkPath* getPath() const { return mState->mPath; } + + void setup_paint_common(SkPaint* paint) const { + paint->setAntiAlias(mState->mUseAA); + paint->setDither(true); + paint->setPorterDuffXfermode(mState->mPorterDuffMode); + if (mState->mShadow.mBlur > 0) { + SkDrawLooper* looper = new SkBlurDrawLooper(mState->mShadow.mBlur, + mState->mShadow.mDx, + mState->mShadow.mDy, + mState->mShadow.mColor); + paint->setLooper(looper)->unref(); + } + + /* need to sniff textDrawingMode(), which returns the bit_OR of... + const int cTextInvisible = 0; + const int cTextFill = 1; + const int cTextStroke = 2; + const int cTextClip = 4; + */ + } + + void setup_paint_fill(SkPaint* paint) const { + this->setup_paint_common(paint); + paint->setColor(mState->mFillColor); + } + + /* 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); + paint->setColor(mState->mStrokeColor); + + float width = mState->mStrokeThickness; + + // this allows dashing and dotting to work properly for hairline strokes + // FIXME: Should we only do this for dashed and dotted 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&); +}; + +static SkShader::TileMode SpreadMethod2TileMode(GradientSpreadMethod sm) { + SkShader::TileMode mode = SkShader::kClamp_TileMode; + + switch (sm) { + case SpreadMethodPad: + mode = SkShader::kClamp_TileMode; + break; + case SpreadMethodReflect: + mode = SkShader::kMirror_TileMode; + break; + case SpreadMethodRepeat: + mode = SkShader::kRepeat_TileMode; + break; + } + return mode; +} + +static void extactShader(SkPaint* paint, ColorSpace cs, Pattern* pat, + Gradient* grad, GradientSpreadMethod sm) { + switch (cs) { + case PatternColorSpace: + // createPlatformPattern() returns a new inst + paint->setShader(pat->createPlatformPattern( + AffineTransform()))->safeUnref(); + break; + case GradientColorSpace: { + // grad->getShader() returns a cached obj + paint->setShader(grad->getShader(SpreadMethod2TileMode(sm))); + break; + } + default: + break; + } +} + +//////////////////////////////////////////////////////////////////////////////////////////////// + +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; +} + +//////////////////////////////////////////////////////////////////////////////////////////////// + +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(); +} + +bool GraphicsContext::willFill() const { + return m_data->mState->mFillColor != 0; +} + +bool GraphicsContext::willStroke() const { + return m_data->mState->mStrokeColor != 0; +} + +// 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 (GC2Canvas(this)->quickReject(path, shouldAntialias ? + SkCanvas::kAA_EdgeType : SkCanvas::kBW_EdgeType)) { + return; + } + + if (fillColor().rgb() & 0xFF000000) { + m_data->setup_paint_fill(&paint); + paint.setAntiAlias(shouldAntialias); + GC2Canvas(this)->drawPath(path, paint); + } + + if (strokeStyle() != NoStroke) { + paint.reset(); + m_data->setup_paint_stroke(&paint, NULL); + paint.setAntiAlias(shouldAntialias); + GC2Canvas(this)->drawPath(path, 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::fillRect(const FloatRect& rect) +{ + SkPaint paint; + SkRect r; + + android_setrect(&r, rect); + m_data->setup_paint_fill(&paint); + + extactShader(&paint, m_common->state.fillColorSpace, + m_common->state.fillPattern.get(), + m_common->state.fillGradient.get(), spreadMethod()); + + 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()); // punch in the specified color + paint.setShader(NULL); // in case we had one set + GC2Canvas(this)->drawRect(r, paint); + } +} + +void GraphicsContext::clip(const FloatRect& 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); +} + +void GraphicsContext::clipToImageBuffer(const FloatRect&, const ImageBuffer*) { + SkDebugf("xxxxxxxxxxxxxxxxxx clipToImageBuffer not implemented\n"); +} + +////////////////////////////////////////////////////////////////////////////////////////////////// + +#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::setupFillPaint(SkPaint* paint) { + m_data->setup_paint_fill(paint); + } + + void GraphicsContext::setupStrokePaint(SkPaint* paint) { + m_data->setup_paint_stroke(paint, NULL); + } + + bool GraphicsContext::setupShadowPaint(SkPaint* paint, SkPoint* offset) { + return m_data->mState->setupShadowPaint(paint, offset); + } + + // referenced from CanvasStyle.cpp + void GraphicsContext::setCMYKAFillColor(float c, float m, float y, float k, float a) { + float r = 1 - (c + k); + float g = 1 - (m + k); + float b = 1 - (y + k); + return this->setFillColor(Color(r, g, b, a)); + } + + // referenced from CanvasStyle.cpp + void GraphicsContext::setCMYKAStrokeColor(float c, float m, float y, float k, float a) { + float r = 1 - (c + k); + float g = 1 - (m + k); + float b = 1 - (y + k); + return this->setStrokeColor(Color(r, g, b, a)); + } + + void GraphicsContext::setPlatformStrokeColor(const Color& c) { + m_data->setStrokeColor(c); + } + + void GraphicsContext::setPlatformStrokeThickness(float f) { + m_data->setStrokeThickness(f); + } + + void GraphicsContext::setPlatformFillColor(const Color& c) { + m_data->setFillColor(c); + } + +void GraphicsContext::setPlatformShadow(const IntSize& size, int blur, const Color& color) +{ + if (paintingDisabled()) + return; + + if (blur <= 0) { + this->clearPlatformShadow(); + } + + SkColor c; + if (color.isValid()) { + c = color.rgb(); + } else { + c = SkColorSetARGB(0xFF/3, 0, 0, 0); // "std" Apple shadow color + } + m_data->mState->setShadow(blur, size.width(), size.height(), c); +} + +void GraphicsContext::clearPlatformShadow() +{ + if (paintingDisabled()) + return; + + m_data->mState->setShadow(0, 0, 0, 0); +} + +/////////////////////////////////////////////////////////////////////////////// + +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 +} + +void GraphicsContext::setUseAntialiasing(bool useAA) { + if (paintingDisabled()) + return; + m_data->mState->mUseAA = useAA; +} + +AffineTransform GraphicsContext::getCTM() const { + return AffineTransform(GC2Canvas(this)->getTotalMatrix()); +} + +/////////////////////////////////////////////////////////////////////////////// + +void GraphicsContext::beginPath() { + m_data->beginPath(); +} + +void GraphicsContext::addPath(const Path& p) { + m_data->addPath(*p.platformPath()); +} + +void GraphicsContext::drawPath() { + this->fillPath(); + this->strokePath(); +} + +void GraphicsContext::fillPath() { + SkPath* path = m_data->getPath(); + if (paintingDisabled() || !path) + return; + + switch (this->fillRule()) { + case RULE_NONZERO: + path->setFillType(SkPath::kWinding_FillType); + break; + case RULE_EVENODD: + path->setFillType(SkPath::kEvenOdd_FillType); + break; + } + + SkPaint paint; + m_data->setup_paint_fill(&paint); + + extactShader(&paint, m_common->state.fillColorSpace, + m_common->state.fillPattern.get(), + m_common->state.fillGradient.get(), spreadMethod()); + + GC2Canvas(this)->drawPath(*path, paint); +} + +void GraphicsContext::strokePath() { + const SkPath* path = m_data->getPath(); + if (paintingDisabled() || !path || strokeStyle() == NoStroke) + return; + + SkPaint paint; + m_data->setup_paint_stroke(&paint, NULL); + + extactShader(&paint, m_common->state.strokeColorSpace, + m_common->state.strokePattern.get(), + m_common->state.strokeGradient.get(), spreadMethod()); + + GC2Canvas(this)->drawPath(*path, paint); +} + +void GraphicsContext::setImageInterpolationQuality(InterpolationQuality mode) +{ + /* + enum InterpolationQuality { + InterpolationDefault, + InterpolationNone, + InterpolationLow, + InterpolationMedium, + InterpolationHigh + }; + + TODO: record this, so we can know when to use bitmap-filtering when we draw + ... not sure how meaningful this will be given our playback model. + + Certainly safe to do nothing for the present. + */ +} + +} // namespace WebCore + +/////////////////////////////////////////////////////////////////////////////// + +SkCanvas* android_gc2canvas(WebCore::GraphicsContext* gc) { + return gc->platformContext()->mCanvas; +} + diff --git a/WebCore/platform/graphics/android/ImageAndroid.cpp b/WebCore/platform/graphics/android/ImageAndroid.cpp new file mode 100644 index 0000000..04235d5 --- /dev/null +++ b/WebCore/platform/graphics/android/ImageAndroid.cpp @@ -0,0 +1,297 @@ +/* + * Copyright 2009, The Android Open Source Project + * 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 "SkImageDecoder.h" +#include "SkShader.h" +#include "SkString.h" +#include "SkTemplates.h" + +#include <utils/AssetManager.h> + +//#define TRACE_SUBSAMPLED_BITMAPS +//#define TRACE_SKIPPED_BITMAPS + +android::AssetManager* globalAssetManager() { + static android::AssetManager* gGlobalAssetMgr; + if (!gGlobalAssetMgr) { + gGlobalAssetMgr = new android::AssetManager(); + gGlobalAssetMgr->addDefaultAssets(); + } + return gGlobalAssetMgr; +} + +namespace WebCore { + +void FrameData::clear() +{ + if (m_frame) { + m_frame->unref(); + m_frame = 0; + m_duration = 0.; + m_hasAlpha = true; + } +} + +BitmapImage::BitmapImage(SkBitmapRef* ref, ImageObserver* observer) + : Image(observer) + , m_currentFrame(0) + , m_frames(0) + , m_frameTimer(0) + , m_repetitionCount(0) + , m_repetitionsComplete(0) + , m_isSolidColor(false) + , m_animationFinished(true) + , m_allDataReceived(true) + , m_haveSize(true) + , m_sizeAvailable(true) + , m_decodedSize(0) + , m_haveFrameCount(true) + , m_frameCount(1) +{ + initPlatformData(); + + m_size = IntSize(ref->bitmap().width(), ref->bitmap().height()); + + m_frames.grow(1); + m_frames[0].m_frame = ref; + m_frames[0].m_hasAlpha = !ref->bitmap().isOpaque(); + checkForSolidColor(); + ref->ref(); +} + + +void BitmapImage::initPlatformData() +{ + m_source.clearURL(); +} + +void BitmapImage::invalidatePlatformData() +{ +} + +void BitmapImage::checkForSolidColor() +{ + m_isSolidColor = false; + if (frameCount() == 1) { + SkBitmapRef* ref = frameAtIndex(0); + if (!ref) { + return; // keep solid == false + } + + const SkBitmap& bm = ref->bitmap(); + if (bm.width() != 1 || bm.height() != 1) { + return; // keep solid == false + } + + SkAutoLockPixels alp(bm); + if (!bm.readyToDraw()) { + return; // keep solid == false + } + + 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: + return; // keep solid == false + } + 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) { +#ifdef TRACE_SKIPPED_BITMAPS + SkDebugf("----- skip bitmapimage: [%d %d] pixels %p pixelref %p\n", + bitmap.width(), bitmap.height(), + bitmap.getPixels(), bitmap.pixelRef()); +#endif + 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()) { +#ifdef TRACE_SKIPPED_BITMAPS + SkDebugf("----- skip bitmapimage: [%d %d] src-empty %d dst-empty %d\n", + bitmap.width(), bitmap.height(), + srcR.isEmpty(), dstR.isEmpty()); +#endif + 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 +PassRefPtr<Image> Image::loadPlatformResource(const char *name) +{ + android::AssetManager* am = globalAssetManager(); + + SkString path("webkit/"); + path.append(name); + path.append(".png"); + + android::Asset* a = am->open(path.c_str(), + android::Asset::ACCESS_BUFFER); + if (a == NULL) { + SkDebugf("---------------- failed to open image asset %s\n", name); + return NULL; + } + + SkAutoTDelete<android::Asset> ad(a); + + SkBitmap bm; + if (SkImageDecoder::DecodeMemory(a->getBuffer(false), a->getLength(), &bm)) { + SkBitmapRef* ref = new SkBitmapRef(bm); + // create will call ref(), so we need aur() to release ours upon return + SkAutoUnref aur(ref); + return BitmapImage::create(ref, 0); + } + return Image::nullImage(); +} + +} // namespace + diff --git a/WebCore/platform/graphics/android/ImageBufferAndroid.cpp b/WebCore/platform/graphics/android/ImageBufferAndroid.cpp new file mode 100644 index 0000000..de88b33 --- /dev/null +++ b/WebCore/platform/graphics/android/ImageBufferAndroid.cpp @@ -0,0 +1,216 @@ +/* + * Copyright 2007, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 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 "BitmapImage.h" +#include "ImageBuffer.h" +#include "ImageData.h" +#include "NotImplemented.h" + +#include "android_graphics.h" +#include "GraphicsContext.h" +#include "PlatformGraphicsContext.h" +#include "SkBitmapRef.h" +#include "SkCanvas.h" +#include "SkColorPriv.h" +#include "SkDevice.h" +#include "SkUnPreMultiply.h" + +using namespace std; + +namespace WebCore { + +ImageBufferData::ImageBufferData(const IntSize&) +{ +} + +ImageBuffer::ImageBuffer(const IntSize& size, bool grayScale, bool& success) + : m_data(size) + , m_size(size) +{ + m_context.set(GraphicsContext::createOffscreenContext(size.width(), size.height())); + success = true; +} + +ImageBuffer::~ImageBuffer() +{ +} + +GraphicsContext* ImageBuffer::context() const +{ + return m_context.get(); +} + +/* This guy needs to make a deep copy of the bitmap, so that the returned + image doesn't reflect any subsequent changes to the canvas' backend. + e.g. this is called when <canvas> wants to make a Pattern, which needs + to snapshot the current pixels when it is created. + */ +Image* ImageBuffer::image() const +{ + if (!m_image) { + ASSERT(context()); + SkCanvas* canvas = context()->platformContext()->mCanvas; + SkDevice* device = canvas->getDevice(); + const SkBitmap& orig = device->accessBitmap(false); + + SkBitmap copy; + orig.copyTo(©, orig.config()); + + SkBitmapRef* ref = new SkBitmapRef(copy); + m_image = BitmapImage::create(ref, 0); + ref->unref(); + } + return m_image.get(); +} + +PassRefPtr<ImageData> ImageBuffer::getImageData(const IntRect& rect) const +{ + GraphicsContext* gc = this->context(); + if (!gc) { + return 0; + } + + const SkBitmap& src = android_gc2canvas(gc)->getDevice()->accessBitmap(false); + SkAutoLockPixels alp(src); + if (!src.getPixels()) { + return 0; + } + + PassRefPtr<ImageData> result = ImageData::create(rect.width(), rect.height()); + unsigned char* data = result->data()->data().data(); + + if (rect.x() < 0 || rect.y() < 0 || (rect.x() + rect.width()) > m_size.width() || (rect.y() + rect.height()) > m_size.height()) + memset(data, 0, result->data()->length()); + + int originx = rect.x(); + int destx = 0; + if (originx < 0) { + destx = -originx; + originx = 0; + } + int endx = rect.x() + rect.width(); + if (endx > m_size.width()) + endx = m_size.width(); + int numColumns = endx - originx; + + int originy = rect.y(); + int desty = 0; + if (originy < 0) { + desty = -originy; + originy = 0; + } + int endy = rect.y() + rect.height(); + if (endy > m_size.height()) + endy = m_size.height(); + int numRows = endy - originy; + + unsigned srcPixelsPerRow = src.rowBytesAsPixels(); + unsigned destBytesPerRow = 4 * rect.width(); + + const SkPMColor* srcRows = src.getAddr32(originx, originy); + unsigned char* destRows = data + desty * destBytesPerRow + destx * 4; + for (int y = 0; y < numRows; ++y) { + for (int x = 0; x < numColumns; x++) { + // ugh, it appears they want unpremultiplied pixels + SkColor c = SkUnPreMultiply::PMColorToColor(srcRows[x]); + int basex = x * 4; + destRows[basex + 0] = SkColorGetR(c); + destRows[basex + 1] = SkColorGetG(c); + destRows[basex + 2] = SkColorGetB(c); + destRows[basex + 3] = SkColorGetA(c); + } + srcRows += srcPixelsPerRow; + destRows += destBytesPerRow; + } + return result; +} + +void ImageBuffer::putImageData(ImageData* source, const IntRect& sourceRect, const IntPoint& destPoint) +{ + GraphicsContext* gc = this->context(); + if (!gc) { + return; + } + + const SkBitmap& dst = android_gc2canvas(gc)->getDevice()->accessBitmap(true); + SkAutoLockPixels alp(dst); + if (!dst.getPixels()) { + return; + } + + ASSERT(sourceRect.width() > 0); + ASSERT(sourceRect.height() > 0); + + int originx = sourceRect.x(); + int destx = destPoint.x() + sourceRect.x(); + ASSERT(destx >= 0); + ASSERT(destx < m_size.width()); + ASSERT(originx >= 0); + ASSERT(originx <= sourceRect.right()); + + int endx = destPoint.x() + sourceRect.right(); + ASSERT(endx <= m_size.width()); + + int numColumns = endx - destx; + + int originy = sourceRect.y(); + int desty = destPoint.y() + sourceRect.y(); + ASSERT(desty >= 0); + ASSERT(desty < m_size.height()); + ASSERT(originy >= 0); + ASSERT(originy <= sourceRect.bottom()); + + int endy = destPoint.y() + sourceRect.bottom(); + ASSERT(endy <= m_size.height()); + int numRows = endy - desty; + + unsigned srcBytesPerRow = 4 * source->width(); + unsigned dstPixelsPerRow = dst.rowBytesAsPixels(); + + unsigned char* srcRows = source->data()->data().data() + originy * srcBytesPerRow + originx * 4; + SkPMColor* dstRows = dst.getAddr32(destx, desty); + for (int y = 0; y < numRows; ++y) { + for (int x = 0; x < numColumns; x++) { + int basex = x * 4; + dstRows[x] = SkPackARGB32(srcRows[basex + 3], + srcRows[basex + 0], + srcRows[basex + 1], + srcRows[basex + 2]); + } + dstRows += dstPixelsPerRow; + srcRows += srcBytesPerRow; + } +} + + +String ImageBuffer::toDataURL(const String&) const +{ + // leaving this unimplemented, until I understand what its for (and what it + // really is). + return "data:,"; // I think this means we couldn't make the data url +} + +} diff --git a/WebCore/platform/graphics/android/ImageBufferData.h b/WebCore/platform/graphics/android/ImageBufferData.h new file mode 100644 index 0000000..7d8cd3b --- /dev/null +++ b/WebCore/platform/graphics/android/ImageBufferData.h @@ -0,0 +1,40 @@ +/* + * Copyright 2008, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 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. + */ + +#ifndef ImageBufferData_h +#define ImageBufferData_h + +namespace WebCore { + +class IntSize; + +class ImageBufferData { +public: + ImageBufferData(const IntSize&); +}; + +} // namespace WebCore + +#endif // ImageBufferData_h diff --git a/WebCore/platform/graphics/android/ImageSourceAndroid.cpp b/WebCore/platform/graphics/android/ImageSourceAndroid.cpp new file mode 100644 index 0000000..a6bf6c6 --- /dev/null +++ b/WebCore/platform/graphics/android/ImageSourceAndroid.cpp @@ -0,0 +1,361 @@ +/* + * Copyright 2007, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 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 "ImageDecoder.h" +#include "ImageSource.h" +#include "IntSize.h" +#include "NotImplemented.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) + +// don't use RLE for images smaller than this, since they incur a drawing cost +// (and don't work as patterns yet) we only want to use RLE when we must +#define MIN_RLE_ALLOC_SIZE (512*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() > MIN_RLE_ALLOC_SIZE; +} + +/////////////////////////////////////////////////////////////////////////////// + +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; +}; + +using namespace android; + +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); + +// SkDebugf("----- started: [%d %d] %s\n", origW, origH, m_decoder.m_url.c_str()); + } + + 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(); + +// SkDebugf("---- finished: [%d %d] %s\n", bm->width(), bm->height(), ref->getURI()); + } +} + +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 +} + +IntSize ImageSource::frameSizeAtIndex(size_t index) const +{ + // for now, all (1) of our frames are the same size + return this->size(); +} + +} diff --git a/WebCore/platform/graphics/android/PathAndroid.cpp b/WebCore/platform/graphics/android/PathAndroid.cpp new file mode 100644 index 0000000..819173b --- /dev/null +++ b/WebCore/platform/graphics/android/PathAndroid.cpp @@ -0,0 +1,268 @@ +/* + * Copyright 2007, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 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 "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.14159265f; +static const float g2PI = 6.28318531f; +static const float g180OverPI = 57.29577951308f; + +static float fast_mod(float angle, float max) { + if (angle >= max || angle <= -max) { + angle = fmodf(angle, max); + } + return angle; +} + +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; + bool prependOval = false; + + /* Note if clockwise and the sign of the sweep disagree. This particular + logic was deduced from http://canvex.lazyilluminati.com/misc/arc.html + */ + if (clockwise && (sweep > 0 || sweep < -g2PI)) { + sweep = fmodf(sweep, g2PI) - g2PI; + } else if (!clockwise && (sweep < 0 || sweep > g2PI)) { + sweep = fmodf(sweep, g2PI) + g2PI; + } + + // If the abs(sweep) >= 2PI, then we need to add a circle before we call + // arcTo, since it treats the sweep mod 2PI. We don't have a prepend call, + // so we just remember this, and at the end create a new path with an oval + // and our current path, and then swap then. + // + if (sweep >= g2PI || sweep <= -g2PI) { + prependOval = true; +// SkDebugf("addArc sa=%g ea=%g cw=%d sweep %g treat as circle\n", sa, ea, clockwise, sweep); + + // now reduce sweep to just the amount we need, so that the current + // point is left where the caller expects it. + sweep = fmodf(sweep, g2PI); + } + + sa = fast_mod(sa, g2PI); + SkScalar startDegrees = SkFloatToScalar(sa * g180OverPI); + SkScalar sweepDegrees = SkFloatToScalar(sweep * g180OverPI); + +// SkDebugf("addArc sa=%g ea=%g cw=%d sweep=%g ssweep=%g\n", sa, ea, clockwise, sweep, SkScalarToFloat(sweepDegrees)); + m_path->arcTo(oval, startDegrees, sweepDegrees, false); + + if (prependOval) { + SkPath tmp; + tmp.addOval(oval); + tmp.addPath(*m_path); + m_path->swap(tmp); + } +} + +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/PatternAndroid.cpp b/WebCore/platform/graphics/android/PatternAndroid.cpp new file mode 100644 index 0000000..2840faa --- /dev/null +++ b/WebCore/platform/graphics/android/PatternAndroid.cpp @@ -0,0 +1,54 @@ +/* + * Copyright 2006, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 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 "Pattern.h" + +#include "android_graphics.h" +#include "GraphicsContext.h" +#include "SkBitmapRef.h" +#include "SkCanvas.h" +#include "SkColorShader.h" +#include "SkShader.h" +#include "SkPaint.h" + +namespace WebCore { + +static SkShader::TileMode toTileMode(bool doRepeat) { + return doRepeat ? SkShader::kRepeat_TileMode : SkShader::kClamp_TileMode; +} + +SkShader* Pattern::createPlatformPattern(const AffineTransform& transform) const +{ + SkBitmapRef* ref = tileImage()->nativeImageForCurrentFrame(); + SkShader* s = SkShader::CreateBitmapShader(ref->bitmap(), + toTileMode(m_repeatX), + toTileMode(m_repeatY)); + + // TODO: do I treat transform as a local matrix??? + return s; +} + +} //namespace diff --git a/WebCore/platform/graphics/android/PlatformGraphicsContext.cpp b/WebCore/platform/graphics/android/PlatformGraphicsContext.cpp new file mode 100644 index 0000000..b13f45f --- /dev/null +++ b/WebCore/platform/graphics/android/PlatformGraphicsContext.cpp @@ -0,0 +1,77 @@ +/* + * Copyright 2006, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 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 "Node.h" +#include "PlatformGraphicsContext.h" +#include "SkCanvas.h" + +namespace WebCore { + +PlatformGraphicsContext::PlatformGraphicsContext(SkCanvas* canvas, WTF::Vector<Container>* buttons) + : mCanvas(canvas), m_deleteCanvas(false), m_buttons(buttons) +{ +} + +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; + } +} + +void PlatformGraphicsContext::storeButtonInfo(Node* node, const IntRect& r) +{ + if (m_buttons == NULL) + return; + // Check to see if we already have a Container for this node. If so, update + // it with the new rectangle and make the new recording canvas reference + // its picture. + Container* end = m_buttons->end(); + for (Container* ptr = m_buttons->begin(); ptr != end; ptr++) { + if (ptr->matches(node)) { + mCanvas->drawPicture(*(ptr->picture())); + ptr->setRect(r); + return; + } + } + // We did not have a Container representing this node, so create a new one. + Container container(node, r); + // 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..dce8ef3 --- /dev/null +++ b/WebCore/platform/graphics/android/PlatformGraphicsContext.h @@ -0,0 +1,169 @@ +/* + * Copyright 2006, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 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. + */ + +#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) + : m_node(node), m_rect(r), m_state(WebCore::RenderSkinAndroid::kDisabled) + { + m_picture = new SkPicture; + } + + ~Container() + { + m_picture->unref(); + } + + Container& operator=(const Container& src) + { + if (this != &src) { + m_node = src.m_node; + if (m_picture) + m_picture->unref(); + m_picture = src.m_picture; + m_picture->ref(); + m_rect = src.m_rect; + m_state = WebCore::RenderSkinAndroid::kDisabled; + } + return *this; + } + + Container(const Container& src) + { + m_node = src.m_node; + m_picture = src.m_picture; + m_picture->ref(); + m_rect = src.m_rect; + m_state = WebCore::RenderSkinAndroid::kDisabled; + } + + // m_picture has a ref count of 1 to begin with. It will increase each time + // m_picture is referenced by another picture. When the other pictures are + // deleted, the ref count gets decremented. If the ref count is one, then + // no other pictures reference this one, so the button is no longer being + // used, and therefore can be removed. + bool canBeRemoved() + { + return m_picture->getRefCnt() == 1; + } + + bool matches(const WebCore::Node* match) { return m_node == match; } + + const WebCore::Node* node() const { return m_node; } + + // Provide a pointer to our SkPicture. + SkPicture* picture() { return m_picture; } + + WebCore::IntRect rect() { return m_rect; } + + // Update the rectangle with a new rectangle, as the positioning of this + // button may have changed due to a new layout. If it is a new rectangle, + // set its state to disabled, so that it will be redrawn when we cycle + // through the list of buttons. + void setRect(WebCore::IntRect r) + { + if (m_rect != r) { + m_rect = r; + m_state = WebCore::RenderSkinAndroid::kDisabled; + } + } + + // 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::RenderSkinAndroid::State state) + { + if (state == m_state) + return; + // If this button is being told to draw focused, but it is already in a + // pressed state, leave it in the pressed state, to show that it is + // being followed. + if (m_state == WebCore::RenderSkinAndroid::kPressed && + state == WebCore::RenderSkinAndroid::kFocused) + 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: + // 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 GraphicsContext; + +class PlatformGraphicsContext { +public: + PlatformGraphicsContext(); + // Pass in a recording canvas, and an array of button information to be + // updated. + PlatformGraphicsContext(SkCanvas* canvas, WTF::Vector<Container>* buttons); + ~PlatformGraphicsContext(); + + void setupFillPaint(GraphicsContext*, SkPaint*); + void setupStrokePaint(GraphicsContext*, SkPaint*); + + 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); +private: + bool m_deleteCanvas; + WTF::Vector<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..094102b --- /dev/null +++ b/WebCore/platform/graphics/android/SkBitmapRef.h @@ -0,0 +1,62 @@ +/* + * Copyright 2006, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 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. + */ + +#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..2bc0c8f --- /dev/null +++ b/WebCore/platform/graphics/android/android_graphics.cpp @@ -0,0 +1,228 @@ +/* + * Copyright 2007, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 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 "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); + + // need the cast to find the right constructor + return WebCore::Color((int)SkColorGetR(c), (int)SkColorGetG(c), + (int)SkColorGetB(c), (int)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(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(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 +}; + +#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); + } + 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..21492cf --- /dev/null +++ b/WebCore/platform/graphics/android/android_graphics.h @@ -0,0 +1,89 @@ +/* + * Copyright 2007, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 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. + */ + +#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; + class GraphicsContext; +} + +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); + +SkCanvas* android_gc2canvas(WebCore::GraphicsContext* gc); + +// Data and methods for focus rings + +// used to inflate node cache entry +#define FOCUS_RING_HIT_TEST_RADIUS 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, + INVALID_FLAVOR, + NORMAL_ANIMATING, + FAKE_ANIMATING, + ANIMATING_COUNT = 2 + }; + + static void DrawRing(SkCanvas* , + const Vector<WebCore::IntRect>& rects, Flavor ); +}; + +#endif + |